From a3e6d99b82c02d5407787f67aa63c018c9fec6c7 Mon Sep 17 00:00:00 2001 From: prodrigues Date: Fri, 20 Sep 2024 14:44:48 +0100 Subject: [PATCH] added singlestore-proxy it tests --- .../tests/replicas/singlestore.test.ts | 54 +++---- .../tests/singlestore/singlestore-common.ts | 1 - .../singlestore/singlestore-proxy.test.ts | 140 ++++++++++++++++++ 3 files changed, 167 insertions(+), 28 deletions(-) create mode 100644 integration-tests/tests/singlestore/singlestore-proxy.test.ts diff --git a/integration-tests/tests/replicas/singlestore.test.ts b/integration-tests/tests/replicas/singlestore.test.ts index 8bf3bd396..76d84c972 100644 --- a/integration-tests/tests/replicas/singlestore.test.ts +++ b/integration-tests/tests/replicas/singlestore.test.ts @@ -1,6 +1,6 @@ import { sql } from 'drizzle-orm'; -import { boolean, singlestoreTable, serial, text, withReplicas } from 'drizzle-orm/singlestore-core'; import { drizzle } from 'drizzle-orm/singlestore'; +import { boolean, serial, singlestoreTable, text, withReplicas } from 'drizzle-orm/singlestore-core'; import { describe, expect, it, vi } from 'vitest'; const usersTable = singlestoreTable('users', { @@ -558,9 +558,9 @@ describe('[transaction] replicas singlestore', () => { describe('[findFirst] read replicas singlestore', () => { it('primary findFirst', () => { - const primaryDb = drizzle({} as any, { schema: { usersTable }}); - const read1 = drizzle({} as any, { schema: { usersTable }}); - const read2 = drizzle({} as any, { schema: { usersTable }}); + const primaryDb = drizzle({} as any, { schema: { usersTable } }); + const read1 = drizzle({} as any, { schema: { usersTable } }); + const read2 = drizzle({} as any, { schema: { usersTable } }); const db = withReplicas(primaryDb, [read1, read2]); @@ -578,9 +578,9 @@ describe('[findFirst] read replicas singlestore', () => { }); it('random replica findFirst', () => { - const primaryDb = drizzle({} as any, { schema: { usersTable }}); - const read1 = drizzle({} as any, { schema: { usersTable }}); - const read2 = drizzle({} as any, { schema: { usersTable }}); + const primaryDb = drizzle({} as any, { schema: { usersTable } }); + const read1 = drizzle({} as any, { schema: { usersTable } }); + const read2 = drizzle({} as any, { schema: { usersTable } }); const randomMockReplica = vi.fn().mockReturnValueOnce(read1).mockReturnValueOnce(read2); @@ -607,8 +607,8 @@ describe('[findFirst] read replicas singlestore', () => { }); it('single read replica findFirst', () => { - const primaryDb = drizzle({} as any, { schema: { usersTable }}); - const read1 = drizzle({} as any, { schema: { usersTable }}); + const primaryDb = drizzle({} as any, { schema: { usersTable } }); + const read1 = drizzle({} as any, { schema: { usersTable } }); const db = withReplicas(primaryDb, [read1]); @@ -625,8 +625,8 @@ describe('[findFirst] read replicas singlestore', () => { }); it('single read replica findFirst + primary findFirst', () => { - const primaryDb = drizzle({} as any, { schema: { usersTable }}); - const read1 = drizzle({} as any, { schema: { usersTable }}); + const primaryDb = drizzle({} as any, { schema: { usersTable } }); + const read1 = drizzle({} as any, { schema: { usersTable } }); const db = withReplicas(primaryDb, [read1]); @@ -644,9 +644,9 @@ describe('[findFirst] read replicas singlestore', () => { }); it('always first read findFirst', () => { - const primaryDb = drizzle({} as any, { schema: { usersTable }}); - const read1 = drizzle({} as any, { schema: { usersTable }}); - const read2 = drizzle({} as any, { schema: { usersTable }}); + const primaryDb = drizzle({} as any, { schema: { usersTable } }); + const read1 = drizzle({} as any, { schema: { usersTable } }); + const read2 = drizzle({} as any, { schema: { usersTable } }); const db = withReplicas(primaryDb, [read1, read2], (replicas) => { return replicas[0]!; @@ -670,9 +670,9 @@ describe('[findFirst] read replicas singlestore', () => { describe('[findMany] read replicas singlestore', () => { it('primary findMany', () => { - const primaryDb = drizzle({} as any, { schema: { usersTable }}); - const read1 = drizzle({} as any, { schema: { usersTable }}); - const read2 = drizzle({} as any, { schema: { usersTable }}); + const primaryDb = drizzle({} as any, { schema: { usersTable } }); + const read1 = drizzle({} as any, { schema: { usersTable } }); + const read2 = drizzle({} as any, { schema: { usersTable } }); const db = withReplicas(primaryDb, [read1, read2]); @@ -691,9 +691,9 @@ describe('[findMany] read replicas singlestore', () => { }); it('random replica findMany', () => { - const primaryDb = drizzle({} as any, { schema: { usersTable }}); - const read1 = drizzle({} as any, { schema: { usersTable }}); - const read2 = drizzle({} as any, { schema: { usersTable }}); + const primaryDb = drizzle({} as any, { schema: { usersTable } }); + const read1 = drizzle({} as any, { schema: { usersTable } }); + const read2 = drizzle({} as any, { schema: { usersTable } }); const randomMockReplica = vi.fn().mockReturnValueOnce(read1).mockReturnValueOnce(read2); @@ -724,8 +724,8 @@ describe('[findMany] read replicas singlestore', () => { }); it('single read replica findMany', () => { - const primaryDb = drizzle({} as any, { schema: { usersTable }}); - const read1 = drizzle({} as any, { schema: { usersTable }}); + const primaryDb = drizzle({} as any, { schema: { usersTable } }); + const read1 = drizzle({} as any, { schema: { usersTable } }); const db = withReplicas(primaryDb, [read1]); @@ -748,8 +748,8 @@ describe('[findMany] read replicas singlestore', () => { }); it('single read replica findMany + primary findMany', () => { - const primaryDb = drizzle({} as any, { schema: { usersTable }}); - const read1 = drizzle({} as any, { schema: { usersTable }}); + const primaryDb = drizzle({} as any, { schema: { usersTable } }); + const read1 = drizzle({} as any, { schema: { usersTable } }); const db = withReplicas(primaryDb, [read1]); @@ -774,9 +774,9 @@ describe('[findMany] read replicas singlestore', () => { }); it('always first read findMany', () => { - const primaryDb = drizzle({} as any, { schema: { usersTable }}); - const read1 = drizzle({} as any, { schema: { usersTable }}); - const read2 = drizzle({} as any, { schema: { usersTable }}); + const primaryDb = drizzle({} as any, { schema: { usersTable } }); + const read1 = drizzle({} as any, { schema: { usersTable } }); + const read2 = drizzle({} as any, { schema: { usersTable } }); const db = withReplicas(primaryDb, [read1, read2], (replicas) => { return replicas[0]!; diff --git a/integration-tests/tests/singlestore/singlestore-common.ts b/integration-tests/tests/singlestore/singlestore-common.ts index 851484f41..1e9079753 100644 --- a/integration-tests/tests/singlestore/singlestore-common.ts +++ b/integration-tests/tests/singlestore/singlestore-common.ts @@ -293,7 +293,6 @@ export function tests(driver?: string) { testRunNumber += 1; console.log(`Test number: ${testRunNumber}`); - }); async function setupReturningFunctionsTest(db: SingleStoreDatabase) { diff --git a/integration-tests/tests/singlestore/singlestore-proxy.test.ts b/integration-tests/tests/singlestore/singlestore-proxy.test.ts new file mode 100644 index 000000000..51dc48a4a --- /dev/null +++ b/integration-tests/tests/singlestore/singlestore-proxy.test.ts @@ -0,0 +1,140 @@ +import retry from 'async-retry'; +import type { SingleStoreRemoteDatabase } from 'drizzle-orm/singlestore-proxy'; +import { drizzle as proxyDrizzle } from 'drizzle-orm/singlestore-proxy'; +import * as mysql2 from 'mysql2/promise'; +import { afterAll, beforeAll, beforeEach } from 'vitest'; +import { skipTests } from '~/common'; +import { createDockerDB, tests } from './singlestore-common'; + +const ENABLE_LOGGING = false; + +// eslint-disable-next-line drizzle-internal/require-entity-kind +class ServerSimulator { + constructor(private db: mysql2.Connection) {} + + async query(sql: string, params: any[], method: 'all' | 'execute') { + if (method === 'all') { + try { + const result = await this.db.query({ + sql, + values: params, + rowsAsArray: true, + typeCast: function(field: any, next: any) { + if (field.type === 'TIMESTAMP' || field.type === 'DATETIME' || field.type === 'DATE') { + return field.string(); + } + return next(); + }, + }); + + return { data: result[0] as any }; + } catch (e: any) { + return { error: e }; + } + } else if (method === 'execute') { + try { + const result = await this.db.query({ + sql, + values: params, + typeCast: function(field: any, next: any) { + if (field.type === 'TIMESTAMP' || field.type === 'DATETIME' || field.type === 'DATE') { + return field.string(); + } + return next(); + }, + }); + + return { data: result as any }; + } catch (e: any) { + return { error: e }; + } + } else { + return { error: 'Unknown method value' }; + } + } + + async migrations(queries: string[]) { + await this.db.query('START TRANSACTION'); + try { + for (const query of queries) { + await this.db.query(query); + } + await this.db.query('COMMIT'); + } catch (e) { + await this.db.query('ROLLBACK'); + throw e; + } + + return {}; + } +} + +let db: SingleStoreRemoteDatabase; +let client: mysql2.Connection; +let serverSimulator: ServerSimulator; + +beforeAll(async () => { + let connectionString; + if (process.env['SINGLESTORE_CONNECTION_STRING']) { + connectionString = process.env['SINGLESTORE_CONNECTION_STRING']; + } else { + const { connectionString: conStr } = await createDockerDB(); + connectionString = conStr; + } + client = await retry(async () => { + client = await mysql2.createConnection(connectionString); + await client.connect(); + return client; + }, { + retries: 20, + factor: 1, + minTimeout: 250, + maxTimeout: 250, + randomize: false, + onRetry() { + client?.end(); + }, + }); + + await client.query(`CREATE DATABASE IF NOT EXISTS drizzle;`); + await client.changeUser({ database: 'drizzle' }); + + serverSimulator = new ServerSimulator(client); + db = proxyDrizzle(async (sql, params, method) => { + try { + const response = await serverSimulator.query(sql, params, method); + + if (response.error !== undefined) { + throw response.error; + } + + return { rows: response.data }; + } catch (e: any) { + console.error('Error from singlestore proxy server:', e.message); + throw e; + } + }, { logger: ENABLE_LOGGING }); +}); + +afterAll(async () => { + await client?.end(); +}); + +beforeEach((ctx) => { + ctx.singlestore = { + db, + }; +}); + +skipTests([ + 'select iterator w/ prepared statement', + 'select iterator', + 'nested transaction rollback', + 'nested transaction', + 'transaction rollback', + 'transaction', + 'transaction with options (set isolationLevel)', + 'migrator', +]); + +tests();