Files
drizzle-team--drizzle-orm/drizzle-kit/tests/introspect/singlestore.test.ts
T
2026-07-13 12:10:05 +08:00

276 lines
6.8 KiB
TypeScript

import Docker from 'dockerode';
import 'dotenv/config';
import { SQL, sql } from 'drizzle-orm';
import {
bigint,
char,
decimal,
double,
float,
int,
mediumint,
singlestoreTable,
smallint,
text,
tinyint,
varchar,
} from 'drizzle-orm/singlestore-core';
import * as fs from 'fs';
import getPort from 'get-port';
import { Connection, createConnection } from 'mysql2/promise';
import { introspectSingleStoreToFile } from 'tests/schemaDiffer';
import { v4 as uuid } from 'uuid';
import { afterAll, beforeAll, beforeEach, expect, test } from 'vitest';
let client: Connection;
let singlestoreContainer: Docker.Container;
async function createDockerDB(): Promise<string> {
const docker = new Docker();
const port = await getPort({ port: 3306 });
const image = 'ghcr.io/singlestore-labs/singlestoredb-dev:latest';
const pullStream = await docker.pull(image);
await new Promise((resolve, reject) =>
docker.modem.followProgress(pullStream, (err) => err ? reject(err) : resolve(err))
);
singlestoreContainer = await docker.createContainer({
Image: image,
Env: ['ROOT_PASSWORD=singlestore'],
name: `drizzle-integration-tests-${uuid()}`,
HostConfig: {
AutoRemove: true,
PortBindings: {
'3306/tcp': [{ HostPort: `${port}` }],
},
},
});
await singlestoreContainer.start();
await new Promise((resolve) => setTimeout(resolve, 4000));
return `singlestore://root:singlestore@localhost:${port}/`;
}
beforeAll(async () => {
const connectionString = process.env.SINGLESTORE_CONNECTION_STRING ?? await createDockerDB();
const sleep = 1000;
let timeLeft = 20000;
let connected = false;
let lastError: unknown | undefined;
do {
try {
client = await createConnection(connectionString);
await client.connect();
connected = true;
break;
} catch (e) {
lastError = e;
await new Promise((resolve) => setTimeout(resolve, sleep));
timeLeft -= sleep;
}
} while (timeLeft > 0);
if (!connected) {
console.error('Cannot connect to SingleStore');
await client?.end().catch(console.error);
await singlestoreContainer?.stop().catch(console.error);
throw lastError;
}
});
afterAll(async () => {
await client?.end().catch(console.error);
await singlestoreContainer?.stop().catch(console.error);
});
beforeEach(async () => {
await client.query(`drop database if exists \`drizzle\`;`);
await client.query(`create database \`drizzle\`;`);
await client.query(`use \`drizzle\`;`);
});
if (!fs.existsSync('tests/introspect/singlestore')) {
fs.mkdirSync('tests/introspect/singlestore');
}
// TODO: Unskip this test when generated column is implemented
/* test.skip('generated always column: link to another column', async () => {
const schema = {
users: singlestoreTable('users', {
id: int('id'),
email: text('email'),
generatedEmail: text('generatedEmail').generatedAlwaysAs(
(): SQL => sql`\`email\``,
),
}),
};
const { statements, sqlStatements } = await introspectSingleStoreToFile(
client,
schema,
'generated-link-column',
'drizzle',
);
expect(statements.length).toBe(0);
expect(sqlStatements.length).toBe(0);
}); */
// TODO: Unskip this test when generated column is implemented
/* test.skip('generated always column virtual: link to another column', async () => {
const schema = {
users: singlestoreTable('users', {
id: int('id'),
email: text('email'),
generatedEmail: text('generatedEmail').generatedAlwaysAs(
(): SQL => sql`\`email\``,
{ mode: 'virtual' },
),
}),
};
const { statements, sqlStatements } = await introspectSingleStoreToFile(
client,
schema,
'generated-link-column-virtual',
'drizzle',
);
expect(statements.length).toBe(0);
expect(sqlStatements.length).toBe(0);
}); */
test('Default value of character type column: char', async () => {
const schema = {
users: singlestoreTable('users', {
id: int('id'),
sortKey: char('sortKey', { length: 255 }).default('0'),
}),
};
const { statements, sqlStatements } = await introspectSingleStoreToFile(
client,
schema,
'default-value-char-column',
'drizzle',
);
expect(statements.length).toBe(0);
expect(sqlStatements.length).toBe(0);
});
test('Default value of character type column: varchar', async () => {
const schema = {
users: singlestoreTable('users', {
id: int('id'),
sortKey: varchar('sortKey', { length: 255 }).default('0'),
}),
};
const { statements, sqlStatements } = await introspectSingleStoreToFile(
client,
schema,
'default-value-varchar-column',
'drizzle',
);
expect(statements.length).toBe(0);
expect(sqlStatements.length).toBe(0);
});
// TODO: Unskip this test when views are implemented
/* test('view #1', async () => {
const users = singlestoreTable('users', { id: int('id') });
const testView = singlestoreView('some_view', { id: int('id') }).as(
sql`select \`drizzle\`.\`users\`.\`id\` AS \`id\` from \`drizzle\`.\`users\``,
);
const schema = {
users: users,
testView,
};
const { statements, sqlStatements } = await introspectSingleStoreToFile(
client,
schema,
'view-1',
'drizzle',
);
expect(statements.length).toBe(0);
expect(sqlStatements.length).toBe(0);
}); */
// TODO: Unskip this test when views are implemented
/* test('view #2', async () => {
const users = singlestoreTable('some_users', { id: int('id') });
const testView = singlestoreView('some_view', { id: int('id') }).algorithm('temptable').sqlSecurity('definer').as(
sql`SELECT * FROM ${users}`,
);
const schema = {
users: users,
testView,
};
const { statements, sqlStatements } = await introspectSingleStoreToFile(
client,
schema,
'view-2',
'drizzle',
);
expect(statements.length).toBe(0);
expect(sqlStatements.length).toBe(0);
}); */
test('handle float type', async () => {
const schema = {
table: singlestoreTable('table', {
col1: float(),
col2: float({ precision: 2 }),
col3: float({ precision: 2, scale: 1 }),
}),
};
const { statements, sqlStatements } = await introspectSingleStoreToFile(
client,
schema,
'handle-float-type',
'drizzle',
);
expect(statements.length).toBe(0);
expect(sqlStatements.length).toBe(0);
});
test('handle unsigned numerical types', async () => {
const schema = {
table: singlestoreTable('table', {
col1: int({ unsigned: true }),
col2: tinyint({ unsigned: true }),
col3: smallint({ unsigned: true }),
col4: mediumint({ unsigned: true }),
col5: bigint({ mode: 'number', unsigned: true }),
col6: float({ unsigned: true }),
col7: float({ precision: 2, scale: 1, unsigned: true }),
col8: double({ unsigned: true }),
col9: double({ precision: 2, scale: 1, unsigned: true }),
col10: decimal({ unsigned: true }),
col11: decimal({ precision: 2, scale: 1, unsigned: true }),
}),
};
const { statements, sqlStatements } = await introspectSingleStoreToFile(
client,
schema,
'handle-unsigned-numerical-types',
'drizzle',
);
expect(statements.length).toBe(0);
expect(sqlStatements.length).toBe(0);
});