From 6eb34c7461c4d1b2ba418593e9305e4a615590e6 Mon Sep 17 00:00:00 2001 From: Karen Chen Date: Wed, 18 Dec 2024 13:18:38 -0800 Subject: [PATCH] fix: integration tests --- .github/workflows/integration_tests.yml | 1 + common/lib/client_wrapper.ts | 2 - common/lib/mysql_client_wrapper.ts | 1 - common/lib/plugin_service.ts | 2 - .../lib/plugins/failover/failover_plugin.ts | 2 +- common/lib/session_state.ts | 6 +- common/lib/session_state_service_impl.ts | 50 +--- common/lib/utils/sql_method_utils.ts | 8 +- mysql/lib/client.ts | 12 +- mysql/lib/dialect/mysql_database_dialect.ts | 6 +- pg/lib/client.ts | 11 +- .../container/tests/aurora_failover.test.ts | 242 +++++++++--------- .../container/tests/autoscaling.test.ts | 4 +- .../tests/basic_connectivity.test.ts | 10 +- .../tests/iam_authentication.test.ts | 2 +- .../container/tests/performance.test.ts | 2 +- .../tests/read_write_splitting.test.ts | 4 +- .../container/tests/session_state.test.ts | 23 +- .../container/tests/utils/perf_util.ts | 2 +- .../tests/utils/test_database_info.ts | 8 +- .../container/tests/utils/test_environment.ts | 4 +- tests/plugin_manager_benchmarks.ts | 4 +- tests/unit/session_state_service_impl.test.ts | 65 ++--- 23 files changed, 208 insertions(+), 263 deletions(-) diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml index 52594b28..c18d481e 100644 --- a/.github/workflows/integration_tests.yml +++ b/.github/workflows/integration_tests.yml @@ -5,6 +5,7 @@ on: push: branches: - main + - refactor/update-read-only paths-ignore: - "**/*.md" - "**/*.jpg" diff --git a/common/lib/client_wrapper.ts b/common/lib/client_wrapper.ts index 1ad24d75..68ac21df 100644 --- a/common/lib/client_wrapper.ts +++ b/common/lib/client_wrapper.ts @@ -15,14 +15,12 @@ */ import { HostInfo } from "./host_info"; -import { SessionState } from "./session_state"; export interface ClientWrapper { readonly client: any; readonly hostInfo: HostInfo; readonly properties: Map; readonly id: string; - readonly sessionState: SessionState; query(sql: any): Promise; diff --git a/common/lib/mysql_client_wrapper.ts b/common/lib/mysql_client_wrapper.ts index 4caa4fbe..b85ce891 100644 --- a/common/lib/mysql_client_wrapper.ts +++ b/common/lib/mysql_client_wrapper.ts @@ -31,7 +31,6 @@ export class MySQLClientWrapper implements ClientWrapper { readonly hostInfo: HostInfo; readonly properties: Map; readonly id: string; - readonly sessionState: SessionState = new SessionState(); /** * Creates a wrapper for the target community driver client. diff --git a/common/lib/plugin_service.ts b/common/lib/plugin_service.ts index 528a26bf..f4ea810a 100644 --- a/common/lib/plugin_service.ts +++ b/common/lib/plugin_service.ts @@ -386,8 +386,6 @@ export class PluginService implements ErrorHandler, HostListProviderService { async abortCurrentClient(): Promise { if (this._currentClient.targetClient) { await this._currentClient.targetClient.abort(); - // this.setInTransaction(false); - // this.getSessionStateService().reset(); } } diff --git a/common/lib/plugins/failover/failover_plugin.ts b/common/lib/plugins/failover/failover_plugin.ts index b6914c66..ac4209a9 100644 --- a/common/lib/plugins/failover/failover_plugin.ts +++ b/common/lib/plugins/failover/failover_plugin.ts @@ -388,9 +388,9 @@ export class FailoverPlugin extends AbstractConnectionPlugin { throw new FailoverFailedError(Messages.get("Failover.unableToConnectToReader")); } - this.pluginService.getCurrentHostInfo()?.removeAlias(Array.from(oldAliases)); await this.pluginService.abortCurrentClient(); await this.pluginService.setCurrentClient(result.client, result.newHost); + this.pluginService.getCurrentHostInfo()?.removeAlias(Array.from(oldAliases)); await this.updateTopology(true); this.failoverReaderSuccessCounter.inc(); } catch (error: any) { diff --git a/common/lib/session_state.ts b/common/lib/session_state.ts index db349826..1fb258d9 100644 --- a/common/lib/session_state.ts +++ b/common/lib/session_state.ts @@ -22,8 +22,10 @@ export abstract class SessionStateField { pristineValue?: Type; constructor(copy?: SessionStateField) { - this.value = copy.value; - this.pristineValue = copy.pristineValue; + if (copy) { + this.value = copy.value; + this.pristineValue = copy.pristineValue; + } } abstract setValue(state: SessionState): void; diff --git a/common/lib/session_state_service_impl.ts b/common/lib/session_state_service_impl.ts index 312b1bac..3c2b7b7f 100644 --- a/common/lib/session_state_service_impl.ts +++ b/common/lib/session_state_service_impl.ts @@ -61,9 +61,9 @@ export class SessionStateServiceImpl implements SessionStateService { const targetClient: ClientWrapper = newClient.targetClient; // Apply current state for all 5 states: autoCommit, readOnly, catalog, schema, transactionIsolation - for (const key of Object.keys(this.copySessionState)) { - const state = this.copySessionState[key]; - if (state.constructor === SessionStateField) { + for (const key of Object.keys(this.sessionState)) { + const state = this.sessionState[key]; + if (state instanceof SessionStateField) { await this.applyCurrentState(targetClient, state); } } @@ -93,7 +93,7 @@ export class SessionStateServiceImpl implements SessionStateService { // The states that will be set are: autoCommit, readonly, schema, catalog, transactionIsolation. for (const key of Object.keys(this.copySessionState)) { const state = this.copySessionState[key]; - if (state.constructor === SessionStateField) { + if (state instanceof SessionStateField) { await this.setPristineStateOnTarget(targetClient, state, key); } } @@ -104,12 +104,7 @@ export class SessionStateServiceImpl implements SessionStateService { } setAutoCommit(autoCommit: boolean): void { - if (!this.transferStateEnabledSetting()) { - return; - } - - this.sessionState.autoCommit.value = autoCommit; - this.logCurrentState(); + return this.setState("autoCommit", autoCommit); } setupPristineAutoCommit(): void; @@ -123,12 +118,7 @@ export class SessionStateServiceImpl implements SessionStateService { } setCatalog(catalog: string): void { - if (!this.transferStateEnabledSetting()) { - return; - } - - this.sessionState.catalog.value = catalog; - this.logCurrentState(); + return this.setState("catalog", catalog); } setupPristineCatalog(): void; @@ -142,12 +132,7 @@ export class SessionStateServiceImpl implements SessionStateService { } setReadOnly(readOnly: boolean): void { - if (!this.transferStateEnabledSetting()) { - return; - } - - this.sessionState.readOnly.value = readOnly; - this.logCurrentState(); + return this.setState("readOnly", readOnly); } setupPristineReadOnly(): void; @@ -157,11 +142,8 @@ export class SessionStateServiceImpl implements SessionStateService { } updateReadOnly(readOnly: boolean): void { - // TODO: review this - // this.pluginService.getSessionStateService().setupPristineReadOnly(readOnly); - // this.pluginService.getSessionStateService().setReadOnly(readOnly); - this.setupPristineState(this.sessionState.readOnly, readOnly); - this.setState(this.sessionState.readOnly, readOnly); + this.pluginService.getSessionStateService().setupPristineReadOnly(readOnly); + this.pluginService.getSessionStateService().setReadOnly(readOnly); } getSchema(): string | undefined { @@ -169,12 +151,7 @@ export class SessionStateServiceImpl implements SessionStateService { } setSchema(schema: string): void { - if (!this.transferStateEnabledSetting()) { - return; - } - - this.sessionState.schema.value = schema; - this.logCurrentState(); + return this.setState("schema", schema); } setupPristineSchema(): void; @@ -188,12 +165,7 @@ export class SessionStateServiceImpl implements SessionStateService { } setTransactionIsolation(transactionIsolation: number): void { - if (!this.transferStateEnabledSetting()) { - return; - } - - this.sessionState.transactionIsolation.value = transactionIsolation; - this.logCurrentState(); + return this.setState("transactionIsolation", transactionIsolation); } setupPristineTransactionIsolation(): void; diff --git a/common/lib/utils/sql_method_utils.ts b/common/lib/utils/sql_method_utils.ts index 4a5eded7..b61ab4da 100644 --- a/common/lib/utils/sql_method_utils.ts +++ b/common/lib/utils/sql_method_utils.ts @@ -47,7 +47,7 @@ export class SqlMethodUtils { } static doesSetAutoCommit(statements: string[], dialect: DatabaseDialect): boolean | undefined { - let autoCommit; + let autoCommit = undefined; for (const statement of statements) { const cleanStatement = statement .toLowerCase() @@ -60,7 +60,7 @@ export class SqlMethodUtils { } static doesSetCatalog(statements: string[], dialect: DatabaseDialect): string | undefined { - let catalog; + let catalog = undefined; for (const statement of statements) { const cleanStatement = statement .toLowerCase() @@ -73,7 +73,7 @@ export class SqlMethodUtils { } static doesSetSchema(statements: string[], dialect: DatabaseDialect): string | undefined { - let schema; + let schema = undefined; for (const statement of statements) { const cleanStatement = statement .toLowerCase() @@ -86,7 +86,7 @@ export class SqlMethodUtils { } static doesSetTransactionIsolation(statements: string[], dialect: DatabaseDialect): TransactionIsolationLevel | undefined { - let transactionIsolation; + let transactionIsolation = undefined; for (const statement of statements) { const cleanStatement = statement .toLowerCase() diff --git a/mysql/lib/client.ts b/mysql/lib/client.ts index b49f6234..a3c5406e 100644 --- a/mysql/lib/client.ts +++ b/mysql/lib/client.ts @@ -96,13 +96,12 @@ export class AwsMySQLClient extends AwsClient { async setReadOnly(readOnly: boolean): Promise { this.pluginService.getSessionStateService().setupPristineReadOnly(); const result = await this.queryWithoutUpdate({ sql: `SET SESSION TRANSACTION READ ${readOnly ? "ONLY" : "WRITE"}` }); - this.targetClient.sessionState.readOnly.value = readOnly; this.pluginService.getSessionStateService().updateReadOnly(readOnly); return result; } isReadOnly(): boolean { - return this.targetClient.sessionState.readOnly.value; + return this.pluginService.getSessionStateService().getReadOnly(); } async setAutoCommit(autoCommit: boolean): Promise { @@ -113,24 +112,22 @@ export class AwsMySQLClient extends AwsClient { setting = "0"; } const result = await this.queryWithoutUpdate({ sql: `SET AUTOCOMMIT=${setting}` }); - this.targetClient.sessionState.autoCommit.value = autoCommit; this.pluginService.getSessionStateService().setAutoCommit(autoCommit); return result; } getAutoCommit(): boolean { - return this.targetClient.sessionState.autoCommit.value; + return this.pluginService.getSessionStateService().getAutoCommit(); } async setCatalog(catalog: string): Promise { this.pluginService.getSessionStateService().setupPristineCatalog(); await this.queryWithoutUpdate({ sql: `USE ${catalog}` }); - this.targetClient.sessionState.catalog.value = catalog; this.pluginService.getSessionStateService().setCatalog(catalog); } getCatalog(): string { - return this.targetClient.sessionState.catalog.value; + return this.pluginService.getSessionStateService().getCatalog(); } async setSchema(schema: string): Promise { @@ -161,12 +158,11 @@ export class AwsMySQLClient extends AwsClient { throw new AwsWrapperError(Messages.get("Client.invalidTransactionIsolationLevel", String(level))); } - this.targetClient.sessionState.transactionIsolation.value = level; this.pluginService.getSessionStateService().setTransactionIsolation(level); } getTransactionIsolation(): number { - return this.targetClient.sessionState.transactionIsolation.value; + return this.pluginService.getSessionStateService().getTransactionIsolation(); } async end() { diff --git a/mysql/lib/dialect/mysql_database_dialect.ts b/mysql/lib/dialect/mysql_database_dialect.ts index bb352e35..755a66f1 100644 --- a/mysql/lib/dialect/mysql_database_dialect.ts +++ b/mysql/lib/dialect/mysql_database_dialect.ts @@ -17,9 +17,7 @@ import { DatabaseDialect, DatabaseType } from "../../../common/lib/database_dialect/database_dialect"; import { HostListProviderService } from "../../../common/lib/host_list_provider_service"; import { HostListProvider } from "../../../common/lib/host_list_provider/host_list_provider"; -import { - ConnectionStringHostListProvider -} from "../../../common/lib/host_list_provider/connection_string_host_list_provider"; +import { ConnectionStringHostListProvider } from "../../../common/lib/host_list_provider/connection_string_host_list_provider"; import { AwsWrapperError, UnsupportedMethodError } from "../../../common/lib/utils/errors"; import { DatabaseDialectCodes } from "../../../common/lib/database_dialect/database_dialect_codes"; import { TransactionIsolationLevel } from "../../../common/lib/utils/transaction_isolation_level"; @@ -34,7 +32,7 @@ import { Messages } from "../../../common/lib/utils/messages"; export class MySQLDatabaseDialect implements DatabaseDialect { protected dialectName: string = this.constructor.name; protected defaultPort: number = 3306; - + getDefaultPort(): number { return this.defaultPort; } diff --git a/pg/lib/client.ts b/pg/lib/client.ts index dc7c79bb..3a88e43a 100644 --- a/pg/lib/client.ts +++ b/pg/lib/client.ts @@ -87,13 +87,12 @@ export class AwsPGClient extends AwsClient { async setReadOnly(readOnly: boolean): Promise { this.pluginService.getSessionStateService().setupPristineReadOnly(); const result = await this.queryWithoutUpdate(`SET SESSION CHARACTERISTICS AS TRANSACTION READ ${readOnly ? "ONLY" : "WRITE"}`); - this.targetClient.sessionState.readOnly.value = readOnly; this.pluginService.getSessionStateService().updateReadOnly(readOnly); return result; } isReadOnly(): boolean { - return this.targetClient.sessionState.readOnly.value; + return this.pluginService.getSessionStateService().getReadOnly(); } async setAutoCommit(autoCommit: boolean): Promise { @@ -113,7 +112,7 @@ export class AwsPGClient extends AwsClient { switch (level) { case 0: - await this.queryWithoutUpdate("SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL READ COMMITTED"); + await this.queryWithoutUpdate("SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL READ UNCOMMITTED"); break; case 1: await this.queryWithoutUpdate("SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL READ COMMITTED"); @@ -127,12 +126,11 @@ export class AwsPGClient extends AwsClient { default: throw new AwsWrapperError(Messages.get("Client.invalidTransactionIsolationLevel", String(level))); } - this.targetClient.sessionState.transactionIsolation.value = level; this.pluginService.getSessionStateService().setTransactionIsolation(level); } getTransactionIsolation(): number { - return this.targetClient.sessionState.transactionIsolation.value; + return this.pluginService.getSessionStateService().getTransactionIsolation(); } async setCatalog(catalog: string): Promise { @@ -150,13 +148,12 @@ export class AwsPGClient extends AwsClient { this.pluginService.getSessionStateService().setupPristineSchema(); const result = await this.queryWithoutUpdate(`SET search_path TO ${schema};`); - this.targetClient.sessionState.schema.value = schema; this.pluginService.getSessionStateService().setSchema(schema); return result; } getSchema(): string { - return this.targetClient.sessionState.schema.value; + return this.pluginService.getSessionStateService().getSchema(); } async end() { diff --git a/tests/integration/container/tests/aurora_failover.test.ts b/tests/integration/container/tests/aurora_failover.test.ts index edd187f4..ac7c8f45 100644 --- a/tests/integration/container/tests/aurora_failover.test.ts +++ b/tests/integration/container/tests/aurora_failover.test.ts @@ -49,7 +49,7 @@ async function initDefaultConfig(host: string, port: number, connectToProxy: boo let config: any = { user: env.databaseInfo.username, host: host, - database: env.databaseInfo.default_db_name, + database: env.databaseInfo.defaultDbName, password: env.databaseInfo.password, port: port, plugins: "failover", @@ -100,77 +100,77 @@ describe("aurora failover", () => { logger.info(`Test finished: ${expect.getState().currentTestName}`); }, 1320000); - // itIf( - // "fails from writer to new writer on connection invocation", - // async () => { - // const config = await initDefaultConfig(env.databaseInfo.writerInstanceEndpoint, env.databaseInfo.instanceEndpointPort, false); - // client = initClientFunc(config); - // - // await client.connect(); - // - // const initialWriterId = await auroraTestUtility.queryInstanceId(client); - // expect(await auroraTestUtility.isDbInstanceWriter(initialWriterId)).toStrictEqual(true); - // - // // Crash instance 1 and nominate a new writer - // await auroraTestUtility.failoverClusterAndWaitUntilWriterChanged(); - // - // await expect(async () => { - // await auroraTestUtility.queryInstanceId(client); - // }).rejects.toThrow(FailoverSuccessError); - // - // // Assert that we are connected to the new writer after failover happens - // const currentConnectionId = await auroraTestUtility.queryInstanceId(client); - // expect(await auroraTestUtility.isDbInstanceWriter(currentConnectionId)).toBe(true); - // expect(currentConnectionId).not.toBe(initialWriterId); - // }, - // 1320000 - // ); - // - // itIf( - // "writer fails within transaction", - // async () => { - // const config = await initDefaultConfig(env.databaseInfo.writerInstanceEndpoint, env.databaseInfo.instanceEndpointPort, false); - // client = initClientFunc(config); - // - // await client.connect(); - // const initialWriterId = await auroraTestUtility.queryInstanceId(client); - // expect(await auroraTestUtility.isDbInstanceWriter(initialWriterId)).toStrictEqual(true); - // - // await DriverHelper.executeQuery(env.engine, client, "DROP TABLE IF EXISTS test3_3"); - // await DriverHelper.executeQuery(env.engine, client, "CREATE TABLE test3_3 (id int not null primary key, test3_3_field varchar(255) not null)"); - // - // await DriverHelper.executeQuery(env.engine, client, "START TRANSACTION"); // start transaction - // await DriverHelper.executeQuery(env.engine, client, "INSERT INTO test3_3 VALUES (1, 'test field string 1')"); - // - // // Crash instance 1 and nominate a new writer - // await auroraTestUtility.failoverClusterAndWaitUntilWriterChanged(); - // - // await expect(async () => { - // await DriverHelper.executeQuery(env.engine, client, "INSERT INTO test3_3 VALUES (2, 'test field string 2')"); - // }).rejects.toThrow(TransactionResolutionUnknownError); - // - // // Attempt to query the instance id. - // const currentConnectionId = await auroraTestUtility.queryInstanceId(client); - // - // // Assert that we are connected to the new writer after failover happens. - // expect(await auroraTestUtility.isDbInstanceWriter(currentConnectionId)).toBe(true); - // - // const nextClusterWriterId = await auroraTestUtility.getClusterWriterInstanceId(); - // expect(currentConnectionId).toBe(nextClusterWriterId); - // expect(initialWriterId).not.toBe(nextClusterWriterId); - // - // // Assert that NO row has been inserted to the table. - // const result = await DriverHelper.executeQuery(env.engine, client, "SELECT count(*) from test3_3"); - // if (env.engine === DatabaseEngine.PG) { - // expect((result as QueryResult).rows[0]["count"]).toBe("0"); - // } else if (env.engine === DatabaseEngine.MYSQL) { - // expect(JSON.parse(JSON.stringify(result))[0][0]["count(*)"]).toBe(0); - // } - // - // await DriverHelper.executeQuery(env.engine, client, "DROP TABLE IF EXISTS test3_3"); - // }, - // 2000000 - // ); + itIf( + "fails from writer to new writer on connection invocation", + async () => { + const config = await initDefaultConfig(env.databaseInfo.writerInstanceEndpoint, env.databaseInfo.instanceEndpointPort, false); + client = initClientFunc(config); + + await client.connect(); + + const initialWriterId = await auroraTestUtility.queryInstanceId(client); + expect(await auroraTestUtility.isDbInstanceWriter(initialWriterId)).toStrictEqual(true); + + // Crash instance 1 and nominate a new writer + await auroraTestUtility.failoverClusterAndWaitUntilWriterChanged(); + + await expect(async () => { + await auroraTestUtility.queryInstanceId(client); + }).rejects.toThrow(FailoverSuccessError); + + // Assert that we are connected to the new writer after failover happens + const currentConnectionId = await auroraTestUtility.queryInstanceId(client); + expect(await auroraTestUtility.isDbInstanceWriter(currentConnectionId)).toBe(true); + expect(currentConnectionId).not.toBe(initialWriterId); + }, + 1320000 + ); + + itIf( + "writer fails within transaction", + async () => { + const config = await initDefaultConfig(env.databaseInfo.writerInstanceEndpoint, env.databaseInfo.instanceEndpointPort, false); + client = initClientFunc(config); + + await client.connect(); + const initialWriterId = await auroraTestUtility.queryInstanceId(client); + expect(await auroraTestUtility.isDbInstanceWriter(initialWriterId)).toStrictEqual(true); + + await DriverHelper.executeQuery(env.engine, client, "DROP TABLE IF EXISTS test3_3"); + await DriverHelper.executeQuery(env.engine, client, "CREATE TABLE test3_3 (id int not null primary key, test3_3_field varchar(255) not null)"); + + await DriverHelper.executeQuery(env.engine, client, "START TRANSACTION"); // start transaction + await DriverHelper.executeQuery(env.engine, client, "INSERT INTO test3_3 VALUES (1, 'test field string 1')"); + + // Crash instance 1 and nominate a new writer + await auroraTestUtility.failoverClusterAndWaitUntilWriterChanged(); + + await expect(async () => { + await DriverHelper.executeQuery(env.engine, client, "INSERT INTO test3_3 VALUES (2, 'test field string 2')"); + }).rejects.toThrow(TransactionResolutionUnknownError); + + // Attempt to query the instance id. + const currentConnectionId = await auroraTestUtility.queryInstanceId(client); + + // Assert that we are connected to the new writer after failover happens. + expect(await auroraTestUtility.isDbInstanceWriter(currentConnectionId)).toBe(true); + + const nextClusterWriterId = await auroraTestUtility.getClusterWriterInstanceId(); + expect(currentConnectionId).toBe(nextClusterWriterId); + expect(initialWriterId).not.toBe(nextClusterWriterId); + + // Assert that NO row has been inserted to the table. + const result = await DriverHelper.executeQuery(env.engine, client, "SELECT count(*) from test3_3"); + if (env.engine === DatabaseEngine.PG) { + expect((result as QueryResult).rows[0]["count"]).toBe("0"); + } else if (env.engine === DatabaseEngine.MYSQL) { + expect(JSON.parse(JSON.stringify(result))[0][0]["count(*)"]).toBe(0); + } + + await DriverHelper.executeQuery(env.engine, client, "DROP TABLE IF EXISTS test3_3"); + }, + 2000000 + ); itIf( "fails from writer and transfers session state", @@ -186,10 +186,10 @@ describe("aurora failover", () => { await client.setTransactionIsolation(TransactionIsolationLevel.TRANSACTION_SERIALIZABLE); if (driver === DatabaseEngine.PG) { - await client.setSchema("test"); + await client.setSchema(env.databaseInfo.defaultDbName); } else if (driver === DatabaseEngine.MYSQL) { await client.setAutoCommit(false); - await client.setCatalog("test"); + await client.setCatalog(env.databaseInfo.defaultDbName); } // Failover cluster and nominate a new writer @@ -206,58 +206,58 @@ describe("aurora failover", () => { expect(client.isReadOnly()).toBe(true); expect(client.getTransactionIsolation()).toBe(TransactionIsolationLevel.TRANSACTION_SERIALIZABLE); if (driver === DatabaseEngine.PG) { - expect(client.getSchema()).toBe("test"); + expect(client.getSchema()).toBe(env.databaseInfo.defaultDbName); } else if (driver === DatabaseEngine.MYSQL) { expect(client.getAutoCommit()).toBe(false); - expect(client.getCatalog()).toBe("test"); + expect(client.getCatalog()).toBe(env.databaseInfo.defaultDbName); } }, 1320000 ); - // itIfTwoInstance( - // "fails from reader to writer", - // async () => { - // // Connect to writer instance - // const writerConfig = await initDefaultConfig(env.proxyDatabaseInfo.writerInstanceEndpoint, env.proxyDatabaseInfo.instanceEndpointPort, true); - // client = initClientFunc(writerConfig); - // await client.connect(); - // const initialWriterId = await auroraTestUtility.queryInstanceId(client); - // expect(await auroraTestUtility.isDbInstanceWriter(initialWriterId)).toStrictEqual(true); - // - // // Get a reader instance - // let readerInstanceHost; - // for (const host of env.proxyDatabaseInfo.instances) { - // if (host.instanceId && host.instanceId !== initialWriterId) { - // readerInstanceHost = host.host; - // } - // } - // if (!readerInstanceHost) { - // throw new Error("Could not find a reader instance"); - // } - // const readerConfig = await initDefaultConfig(readerInstanceHost, env.proxyDatabaseInfo.instanceEndpointPort, true); - // - // secondaryClient = initClientFunc(readerConfig); - // await secondaryClient.connect(); - // - // // Crash the reader instance - // const rdsUtils = new RdsUtils(); - // const readerInstanceId = rdsUtils.getRdsInstanceId(readerInstanceHost); - // if (readerInstanceId) { - // await ProxyHelper.disableConnectivity(env.engine, readerInstanceId); - // - // await expect(async () => { - // await auroraTestUtility.queryInstanceId(secondaryClient); - // }).rejects.toThrow(FailoverSuccessError); - // - // await ProxyHelper.enableConnectivity(readerInstanceId); - // - // // Assert that we are currently connected to the writer instance - // const currentConnectionId = await auroraTestUtility.queryInstanceId(secondaryClient); - // expect(await auroraTestUtility.isDbInstanceWriter(currentConnectionId)).toBe(true); - // expect(currentConnectionId).toBe(initialWriterId); - // } - // }, - // 1320000 - // ); + itIfTwoInstance( + "fails from reader to writer", + async () => { + // Connect to writer instance + const writerConfig = await initDefaultConfig(env.proxyDatabaseInfo.writerInstanceEndpoint, env.proxyDatabaseInfo.instanceEndpointPort, true); + client = initClientFunc(writerConfig); + await client.connect(); + const initialWriterId = await auroraTestUtility.queryInstanceId(client); + expect(await auroraTestUtility.isDbInstanceWriter(initialWriterId)).toStrictEqual(true); + + // Get a reader instance + let readerInstanceHost; + for (const host of env.proxyDatabaseInfo.instances) { + if (host.instanceId && host.instanceId !== initialWriterId) { + readerInstanceHost = host.host; + } + } + if (!readerInstanceHost) { + throw new Error("Could not find a reader instance"); + } + const readerConfig = await initDefaultConfig(readerInstanceHost, env.proxyDatabaseInfo.instanceEndpointPort, true); + + secondaryClient = initClientFunc(readerConfig); + await secondaryClient.connect(); + + // Crash the reader instance + const rdsUtils = new RdsUtils(); + const readerInstanceId = rdsUtils.getRdsInstanceId(readerInstanceHost); + if (readerInstanceId) { + await ProxyHelper.disableConnectivity(env.engine, readerInstanceId); + + await expect(async () => { + await auroraTestUtility.queryInstanceId(secondaryClient); + }).rejects.toThrow(FailoverSuccessError); + + await ProxyHelper.enableConnectivity(readerInstanceId); + + // Assert that we are currently connected to the writer instance + const currentConnectionId = await auroraTestUtility.queryInstanceId(secondaryClient); + expect(await auroraTestUtility.isDbInstanceWriter(currentConnectionId)).toBe(true); + expect(currentConnectionId).toBe(initialWriterId); + } + }, + 1320000 + ); }); diff --git a/tests/integration/container/tests/autoscaling.test.ts b/tests/integration/container/tests/autoscaling.test.ts index 5230b429..46818447 100644 --- a/tests/integration/container/tests/autoscaling.test.ts +++ b/tests/integration/container/tests/autoscaling.test.ts @@ -49,7 +49,7 @@ async function initDefaultConfig(host: string, port: number, provider: InternalP let config: any = { user: env.databaseInfo.username, host: host, - database: env.databaseInfo.default_db_name, + database: env.databaseInfo.defaultDbName, password: env.databaseInfo.password, port: port, plugins: "readWriteSplitting", @@ -69,7 +69,7 @@ async function initConfigWithFailover(host: string, port: number, provider: Inte let config: any = { user: env.databaseInfo.username, host: host, - database: env.databaseInfo.default_db_name, + database: env.databaseInfo.defaultDbName, password: env.databaseInfo.password, port: port, plugins: "readWriteSplitting,failover", diff --git a/tests/integration/container/tests/basic_connectivity.test.ts b/tests/integration/container/tests/basic_connectivity.test.ts index 979027f7..45aec100 100644 --- a/tests/integration/container/tests/basic_connectivity.test.ts +++ b/tests/integration/container/tests/basic_connectivity.test.ts @@ -70,7 +70,7 @@ describe("basic_connectivity", () => { let props = { user: env.databaseInfo.username, host: env.databaseInfo.clusterReadOnlyEndpoint, - database: env.databaseInfo.default_db_name, + database: env.databaseInfo.defaultDbName, password: env.databaseInfo.password, port: env.databaseInfo.clusterEndpointPort, plugins: "failover,efm", @@ -96,7 +96,7 @@ describe("basic_connectivity", () => { let props = { user: env.databaseInfo.username, host: env.databaseInfo.clusterEndpoint, - database: env.databaseInfo.default_db_name, + database: env.databaseInfo.defaultDbName, password: env.databaseInfo.password, port: env.databaseInfo.clusterEndpointPort, plugins: "failover,efm", @@ -122,7 +122,7 @@ describe("basic_connectivity", () => { let props = { user: env.databaseInfo.username, host: env.databaseInfo.instances[0].host, - database: env.databaseInfo.default_db_name, + database: env.databaseInfo.defaultDbName, password: env.databaseInfo.password, port: env.databaseInfo.clusterEndpointPort, plugins: "failover,efm", @@ -148,7 +148,7 @@ describe("basic_connectivity", () => { let props = { user: env.databaseInfo.username, host: env.databaseInfo.instances[0].host, - database: env.databaseInfo.default_db_name, + database: env.databaseInfo.defaultDbName, password: env.databaseInfo.password, port: env.databaseInfo.instanceEndpointPort, plugins: "", @@ -178,7 +178,7 @@ describe("basic_connectivity", () => { let props = { user: env.databaseInfo.username, host: env.proxyDatabaseInfo.instances[0].host, - database: env.databaseInfo.default_db_name, + database: env.databaseInfo.defaultDbName, password: env.databaseInfo.password, port: env.proxyDatabaseInfo.instanceEndpointPort, plugins: "", diff --git a/tests/integration/container/tests/iam_authentication.test.ts b/tests/integration/container/tests/iam_authentication.test.ts index a4eebc54..c9c00d7b 100644 --- a/tests/integration/container/tests/iam_authentication.test.ts +++ b/tests/integration/container/tests/iam_authentication.test.ts @@ -54,7 +54,7 @@ async function initDefaultConfig(host: string): Promise { let props = { user: "jane_doe", host: host, - database: env.databaseInfo.default_db_name, + database: env.databaseInfo.defaultDbName, password: env.databaseInfo.password, port: env.databaseInfo.instanceEndpointPort, plugins: "iam", diff --git a/tests/integration/container/tests/performance.test.ts b/tests/integration/container/tests/performance.test.ts index 5d7c47f7..6144111e 100644 --- a/tests/integration/container/tests/performance.test.ts +++ b/tests/integration/container/tests/performance.test.ts @@ -75,7 +75,7 @@ function initDefaultConfig(host: string, port: number): any { let config: any = { user: env.databaseInfo.username, host: host, - database: env.databaseInfo.default_db_name, + database: env.databaseInfo.defaultDbName, password: env.databaseInfo.password, port: port, failoverTimeoutMs: 250000 diff --git a/tests/integration/container/tests/read_write_splitting.test.ts b/tests/integration/container/tests/read_write_splitting.test.ts index 90f71ae1..d673dbb3 100644 --- a/tests/integration/container/tests/read_write_splitting.test.ts +++ b/tests/integration/container/tests/read_write_splitting.test.ts @@ -52,7 +52,7 @@ async function initDefaultConfig(host: string, port: number, connectToProxy: boo let config: any = { user: env.databaseInfo.username, host: host, - database: env.databaseInfo.default_db_name, + database: env.databaseInfo.defaultDbName, password: env.databaseInfo.password, port: port, plugins: "readWriteSplitting", @@ -72,7 +72,7 @@ async function initConfigWithFailover(host: string, port: number, connectToProxy let config: any = { user: env.databaseInfo.username, host: host, - database: env.databaseInfo.default_db_name, + database: env.databaseInfo.defaultDbName, password: env.databaseInfo.password, port: port, plugins: "readWriteSplitting,failover", diff --git a/tests/integration/container/tests/session_state.test.ts b/tests/integration/container/tests/session_state.test.ts index c5a31fa9..f5674465 100644 --- a/tests/integration/container/tests/session_state.test.ts +++ b/tests/integration/container/tests/session_state.test.ts @@ -94,7 +94,7 @@ describe("session state", () => { let props = { user: env.databaseInfo.username, host: env.databaseInfo.clusterReadOnlyEndpoint, - database: env.databaseInfo.default_db_name, + database: env.databaseInfo.defaultDbName, password: env.databaseInfo.password, port: env.databaseInfo.clusterEndpointPort }; @@ -110,28 +110,25 @@ describe("session state", () => { if (driver === TestDriver.MYSQL) { await newClient.setReadOnly(true); await newClient.setAutoCommit(false); - await newClient.setCatalog("test"); + await newClient.setCatalog(env.databaseInfo.defaultDbName); await newClient.setTransactionIsolation(TransactionIsolationLevel.TRANSACTION_SERIALIZABLE); await client.getPluginService().setCurrentClient(newClient.targetClient); - expect(client.targetClient.sessionState.readOnly.value).toBe(true); - expect(client.targetClient.sessionState.autoCommit.value).toBe(false); - expect(client.targetClient.sessionState.catalog.value).toBe("test"); - expect(client.targetClient.sessionState.schema.value).toBe(undefined); - expect(client.targetClient.sessionState.transactionIsolation.value).toBe(TransactionIsolationLevel.TRANSACTION_SERIALIZABLE); + expect(client.getAutoCommit()).toBe(false); + expect(client.getCatalog()).toBe(env.databaseInfo.defaultDbName); + expect(client.getSchema()).toBe(undefined); + expect(client.getTransactionIsolation()).toBe(TransactionIsolationLevel.TRANSACTION_SERIALIZABLE); } else if (driver === TestDriver.PG) { await newClient.setReadOnly(true); - await newClient.setSchema("test"); + await newClient.setSchema(env.databaseInfo.defaultDbName); await newClient.setTransactionIsolation(TransactionIsolationLevel.TRANSACTION_SERIALIZABLE); await client.getPluginService().setCurrentClient(newClient.targetClient); - expect(client.targetClient.sessionState.readOnly.value).toBe(true); - expect(client.targetClient.sessionState.autoCommit.value).toBe(undefined); - expect(client.targetClient.sessionState.catalog.value).toBe(undefined); - expect(client.targetClient.sessionState.schema.value).toBe("test"); - expect(client.targetClient.sessionState.transactionIsolation.value).toBe(TransactionIsolationLevel.TRANSACTION_SERIALIZABLE); + expect(client.isReadOnly()).toBe(true); + expect(client.getSchema()).toBe(env.databaseInfo.defaultDbName); + expect(client.getTransactionIsolation()).toBe(TransactionIsolationLevel.TRANSACTION_SERIALIZABLE); } } catch (e) { await client.end(); diff --git a/tests/integration/container/tests/utils/perf_util.ts b/tests/integration/container/tests/utils/perf_util.ts index 34ef7370..94ffd372 100644 --- a/tests/integration/container/tests/utils/perf_util.ts +++ b/tests/integration/container/tests/utils/perf_util.ts @@ -25,7 +25,7 @@ export class PerfTestUtility { let config: any = { user: env.databaseInfo.username, host: host, - database: env.databaseInfo.default_db_name, + database: env.databaseInfo.defaultDbName, password: env.databaseInfo.password, port: port, plugins: "connectTime,executeTime" diff --git a/tests/integration/container/tests/utils/test_database_info.ts b/tests/integration/container/tests/utils/test_database_info.ts index 19bc25ee..abfa1b5b 100644 --- a/tests/integration/container/tests/utils/test_database_info.ts +++ b/tests/integration/container/tests/utils/test_database_info.ts @@ -20,7 +20,7 @@ import { DBInstance } from "@aws-sdk/client-rds/dist-types/models/models_0"; export class TestDatabaseInfo { private readonly _username: string; private readonly _password: string; - private readonly _default_db_name: string; + private readonly _defaultDbName: string; private readonly _clusterEndpoint: string; private readonly _clusterEndpointPort: number; private readonly _clusterReadOnlyEndpoint: string; @@ -32,7 +32,7 @@ export class TestDatabaseInfo { constructor(databaseInfo: { [s: string]: any }) { this._username = String(databaseInfo["username"]); this._password = String(databaseInfo["password"]); - this._default_db_name = String(databaseInfo["defaultDbName"]); + this._defaultDbName = String(databaseInfo["defaultDbName"]); this._clusterEndpoint = String(databaseInfo["clusterEndpoint"]); this._clusterEndpointPort = Number(databaseInfo["clusterEndpointPort"]); this._clusterReadOnlyEndpoint = String(databaseInfo["clusterReadOnlyEndpoint"]); @@ -53,8 +53,8 @@ export class TestDatabaseInfo { return this._password; } - get default_db_name(): string { - return this._default_db_name; + get defaultDbName(): string { + return this._defaultDbName; } get writerInstanceEndpoint() { diff --git a/tests/integration/container/tests/utils/test_environment.ts b/tests/integration/container/tests/utils/test_environment.ts index ba0b2bb3..a0c4243f 100644 --- a/tests/integration/container/tests/utils/test_environment.ts +++ b/tests/integration/container/tests/utils/test_environment.ts @@ -111,7 +111,7 @@ export class TestEnvironment { port: info?.databaseInfo.instanceEndpointPort ?? 5432, user: info?.databaseInfo.username, password: info?.databaseInfo.password, - database: info?.databaseInfo.default_db_name, + database: info?.databaseInfo.defaultDbName, query_timeout: 3000, connectionTimeoutMillis: 3000 }); @@ -135,7 +135,7 @@ export class TestEnvironment { port: info?.databaseInfo.instanceEndpointPort ?? 3306, user: info?.databaseInfo.username, password: info?.databaseInfo.password, - database: info?.databaseInfo.default_db_name, + database: info?.databaseInfo.defaultDbName, connectTimeout: 3000 } as ConnectionOptions); diff --git a/tests/plugin_manager_benchmarks.ts b/tests/plugin_manager_benchmarks.ts index cb318e4c..6fd09a29 100644 --- a/tests/plugin_manager_benchmarks.ts +++ b/tests/plugin_manager_benchmarks.ts @@ -283,7 +283,7 @@ suite( }), add("initHostProviderWith10Plugins", async () => { - const pluginManagerWithPlugins = await initPluginManagerWithPlugins(10, instance(mockPluginService), propsWithPlugins);; + const pluginManagerWithPlugins = await initPluginManagerWithPlugins(10, instance(mockPluginService), propsWithPlugins); return async () => await pluginManagerWithPlugins.initHostProvider( new HostInfoBuilder({ hostAvailabilityStrategy: new SimpleHostAvailabilityStrategy() }).withHost("host").build(), @@ -325,7 +325,7 @@ suite( }), add("notifyConnectionChangedWith10Plugins", async () => { - const pluginManagerWithPlugins = await initPluginManagerWithPlugins(10, instance(mockPluginService), propsWithPlugins);; + const pluginManagerWithPlugins = await initPluginManagerWithPlugins(10, instance(mockPluginService), propsWithPlugins); return async () => await pluginManagerWithPlugins.notifyConnectionChanged(new Set([HostChangeOptions.INITIAL_CONNECTION]), null); }), diff --git a/tests/unit/session_state_service_impl.test.ts b/tests/unit/session_state_service_impl.test.ts index 68273c90..107dc803 100644 --- a/tests/unit/session_state_service_impl.test.ts +++ b/tests/unit/session_state_service_impl.test.ts @@ -14,7 +14,7 @@ limitations under the License. */ -import { anything, instance, mock, reset, spy, verify, when } from "ts-mockito"; +import { anything, instance, mock, reset, spy, when } from "ts-mockito"; import { SessionStateServiceImpl } from "../../common/lib/session_state_service_impl"; import { PluginService } from "../../common/lib/plugin_service"; import { AwsPGClient } from "../../pg/lib"; @@ -27,7 +27,7 @@ import { HostInfoBuilder } from "../../common/lib/host_info_builder"; import { SimpleHostAvailabilityStrategy } from "../../common/lib/host_availability/simple_host_availability_strategy"; import { MySQLDatabaseDialect } from "../../mysql/lib/dialect/mysql_database_dialect"; import { PgDatabaseDialect } from "../../pg/lib/dialect/pg_database_dialect"; -import { TransactionIsolationLevel } from "../../common/lib/utils/transaction_isolation_level"; +import { MySQL2DriverDialect } from "../../mysql/lib/dialect/mysql2_driver_dialect"; const hostInfoBuilder = new HostInfoBuilder({ hostAvailabilityStrategy: new SimpleHostAvailabilityStrategy() }); const mockPluginService = mock(PluginService); @@ -39,6 +39,7 @@ let sessionStateService: SessionStateService; const mockPgClientWrapper: PgClientWrapper = mock(PgClientWrapper); const mockMySQLClientWrapper: MySQLClientWrapper = mock(MySQLClientWrapper); const hostInfo = new HostInfoBuilder({ hostAvailabilityStrategy: new SimpleHostAvailabilityStrategy() }).withHost("host").build(); +const mockMySQLDriverDialect = mock(MySQL2DriverDialect); describe("testSessionStateServiceImpl", () => { beforeEach(() => { @@ -49,8 +50,8 @@ describe("testSessionStateServiceImpl", () => { sessionStateService = new SessionStateServiceImpl(instance(mockPluginService), new Map()); awsPGClient.targetClient = new PgClientWrapper(undefined, hostInfoBuilder.withHost("host").build(), new Map()); mockAwsPGClient.targetClient = new PgClientWrapper(undefined, hostInfoBuilder.withHost("host").build(), new Map()); - awsMySQLClient.targetClient = new MySQLClientWrapper(undefined, hostInfoBuilder.withHost("host").build(), new Map()); - mockAwsMySQLClient.targetClient = new MySQLClientWrapper(undefined, hostInfoBuilder.withHost("host").build(), new Map()); + awsMySQLClient.targetClient = new MySQLClientWrapper(undefined, hostInfoBuilder.withHost("host").build(), new Map(), mockMySQLDriverDialect); + mockAwsMySQLClient.targetClient = new MySQLClientWrapper(undefined, hostInfoBuilder.withHost("host").build(), new Map(), mockMySQLDriverDialect); when(mockMySQLClientWrapper.query(anything())).thenResolve(); when(mockPgClientWrapper.query(anything())).thenResolve(); when(mockPluginService.getSessionStateService()).thenReturn(sessionStateService); @@ -77,7 +78,7 @@ describe("testSessionStateServiceImpl", () => { awsClient.targetClient = new PgClientWrapper(undefined, hostInfo, new Map()); when(mockPluginService.getDialect()).thenReturn(new PgDatabaseDialect()); } else { - awsClient.targetClient = new MySQLClientWrapper(undefined, hostInfo, new Map()); + awsClient.targetClient = new MySQLClientWrapper(undefined, hostInfo, new Map(), mockMySQLDriverDialect); when(mockPluginService.getDialect()).thenReturn(new MySQLDatabaseDialect()); } when(mockPluginService.getCurrentClient()).thenReturn(awsClient); @@ -85,17 +86,17 @@ describe("testSessionStateServiceImpl", () => { expect(sessionStateService.getReadOnly()).toBe(undefined); sessionStateService.setupPristineReadOnly(); sessionStateService.setReadOnly(value); - const sessionStateSpy = spy(sessionStateService); expect(sessionStateService.getReadOnly()).toBe(value); sessionStateService.begin(); await sessionStateService.applyPristineSessionState(awsClient); sessionStateService.complete(); - if (shouldReset) { - verify(sessionStateSpy.setReadOnly(anything())).once(); + // Should reset to pristine value + expect(sessionStateService.getReadOnly()).toBe(pristineValue); } else { - verify(sessionStateSpy.setReadOnly(anything())).never(); + // No-op, value should stay unchanged + expect(sessionStateService.getReadOnly()).toBe(value); } }); @@ -114,7 +115,6 @@ describe("testSessionStateServiceImpl", () => { expect(sessionStateService.getAutoCommit()).toBe(undefined); sessionStateService.setupPristineAutoCommit(); sessionStateService.setAutoCommit(value); - const sessionStateSpy = spy(sessionStateService); expect(sessionStateService.getAutoCommit()).toBe(value); sessionStateService.begin(); @@ -122,9 +122,11 @@ describe("testSessionStateServiceImpl", () => { sessionStateService.complete(); if (shouldReset) { - verify(sessionStateSpy.setAutoCommit(pristineValue)).once(); + // Should reset to pristine value + expect(sessionStateService.getAutoCommit()).toBe(pristineValue); } else { - verify(sessionStateSpy.setAutoCommit(anything())).never(); + // No-op, value should stay unchanged + expect(sessionStateService.getAutoCommit()).toBe(value); } }); @@ -143,7 +145,6 @@ describe("testSessionStateServiceImpl", () => { expect(sessionStateService.getCatalog()).toBe(undefined); sessionStateService.setupPristineCatalog(); sessionStateService.setCatalog(value); - const sessionStateSpy = spy(sessionStateService); expect(sessionStateService.getCatalog()).toBe(value); sessionStateService.begin(); @@ -151,9 +152,11 @@ describe("testSessionStateServiceImpl", () => { sessionStateService.complete(); if (shouldReset) { - verify(sessionStateSpy.setCatalog(pristineValue)).once(); + // Should reset to pristine value + expect(sessionStateService.getCatalog()).toBe(pristineValue); } else { - verify(sessionStateSpy.setCatalog(anything())).never(); + // No-op, value should stay unchanged + expect(sessionStateService.getCatalog()).toBe(value); } }); @@ -172,7 +175,6 @@ describe("testSessionStateServiceImpl", () => { expect(sessionStateService.getSchema()).toBe(undefined); sessionStateService.setupPristineSchema(); sessionStateService.setSchema(value); - const sessionStateSpy = spy(sessionStateService); expect(sessionStateService.getSchema()).toBe(value); sessionStateService.begin(); @@ -180,9 +182,11 @@ describe("testSessionStateServiceImpl", () => { sessionStateService.complete(); if (shouldReset) { - verify(sessionStateSpy.setSchema(pristineValue)).once(); + // Should reset to pristine value + expect(sessionStateService.getSchema()).toBe(pristineValue); } else { - verify(sessionStateSpy.setSchema(anything())).never(); + // No-op, value should stay unchanged + expect(sessionStateService.getSchema()).toBe(value); } }); @@ -210,7 +214,6 @@ describe("testSessionStateServiceImpl", () => { expect(sessionStateService.getTransactionIsolation()).toBe(undefined); sessionStateService.setupPristineTransactionIsolation(); sessionStateService.setTransactionIsolation(value); - const sessionStateSpy = spy(sessionStateService); expect(sessionStateService.getTransactionIsolation()).toBe(value); sessionStateService.begin(); @@ -218,27 +221,11 @@ describe("testSessionStateServiceImpl", () => { sessionStateService.complete(); if (shouldReset) { - verify(sessionStateSpy.setTransactionIsolation(pristineValue)).once(); + // Should reset to pristine value + expect(sessionStateService.getTransactionIsolation()).toBe(pristineValue); } else { - verify(sessionStateSpy.setTransactionIsolation(anything())).never(); + // No-op, value should stay unchanged + expect(sessionStateService.getTransactionIsolation()).toBe(value); } }); - - it("test default pg client wrapper state", async () => { - const clientWrapper = new PgClientWrapper({}, hostInfo, new Map()); - expect(clientWrapper.sessionState.readOnly.value).toBe(false); - expect(clientWrapper.sessionState.autoCommit.value).toBe(undefined); - expect(clientWrapper.sessionState.catalog.value).toBe(undefined); - expect(clientWrapper.sessionState.schema.value).toBe(""); - expect(clientWrapper.sessionState.transactionIsolation.value).toBe(TransactionIsolationLevel.TRANSACTION_READ_COMMITTED); - }); - - it("test default mysql client wrapper state", async () => { - const clientWrapper = new MySQLClientWrapper({}, hostInfo, new Map()); - expect(clientWrapper.sessionState.readOnly.value).toBe(false); - expect(clientWrapper.sessionState.autoCommit.value).toBe(true); - expect(clientWrapper.sessionState.catalog.value).toBe(""); - expect(clientWrapper.sessionState.schema.value).toBe(undefined); - expect(clientWrapper.sessionState.transactionIsolation.value).toBe(TransactionIsolationLevel.TRANSACTION_REPEATABLE_READ); - }); });