@backstage/backend-test-utils 1.11.4 → 1.11.5-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @backstage/backend-test-utils
2
2
 
3
+ ## 1.11.5-next.0
4
+
5
+ ### Patch Changes
6
+
7
+ - 41c56b3: Fixed MySQL test database failures by pinning the Docker image from the floating `mysql:8` tag to `mysql:8.4` and replacing a startup flag that was removed in MySQL 8.4. Connection pool reduced from 50 to 5 per test database, idle connections are now reaped after 5 seconds, and container connection limits raised to 1000 for both MySQL and Postgres to handle parallel Jest workers on high-core machines.
8
+ - Updated dependencies
9
+ - @backstage/backend-defaults@0.17.4-next.0
10
+ - @backstage/plugin-auth-node@0.7.3-next.0
11
+ - @backstage/backend-app-api@1.7.2-next.0
12
+ - @backstage/backend-plugin-api@1.9.3-next.0
13
+ - @backstage/plugin-events-node@0.4.24-next.0
14
+
3
15
  ## 1.11.4
4
16
 
5
17
  ### Patch Changes
@@ -12,29 +12,35 @@ var knexFactory__default = /*#__PURE__*/_interopDefaultCompat(knexFactory);
12
12
  var yn__default = /*#__PURE__*/_interopDefaultCompat(yn);
13
13
 
14
14
  async function waitForMysqlReady(connection) {
15
- await waitForReady.waitForReady(async () => {
16
- const knex = knexFactory__default.default({
17
- client: "mysql2",
18
- connection: {
19
- // make a copy because the driver mutates this
20
- ...connection
21
- }
22
- });
23
- try {
24
- const result = await knex.select(knex.raw("version() AS version"));
25
- return Array.isArray(result) && Boolean(result[0]?.version);
26
- } finally {
27
- await knex.destroy();
28
- }
29
- }, "the database");
15
+ const knex = knexFactory__default.default({
16
+ client: "mysql2",
17
+ connection: {
18
+ ...connection,
19
+ connectTimeout: 5e3
20
+ },
21
+ pool: { min: 0, max: 1 }
22
+ });
23
+ try {
24
+ await waitForReady.waitForReady(
25
+ async () => {
26
+ const result = await knex.select(knex.raw("version() AS version"));
27
+ return Array.isArray(result) && Boolean(result[0]?.version);
28
+ },
29
+ "the database",
30
+ 6e4
31
+ );
32
+ } finally {
33
+ await knex.destroy();
34
+ }
30
35
  }
31
36
  async function startMysqlContainer(image) {
32
37
  const user = "root";
33
38
  const password = node_crypto.randomUUID();
34
39
  const { GenericContainer } = require("testcontainers");
35
40
  const container = await new GenericContainer(image).withExposedPorts(3306).withEnvironment({ MYSQL_ROOT_PASSWORD: password }).withTmpFs({ "/var/lib/mysql": "rw" }).withCommand([
36
- "--default-authentication-plugin=mysql_native_password",
37
- "--skip-log-bin"
41
+ "--skip-log-bin",
42
+ "--max-connections=1000",
43
+ "--mysql-native-password=ON"
38
44
  ]).start();
39
45
  const host = container.getHost();
40
46
  const port = container.getMappedPort(3306);
@@ -127,9 +133,9 @@ class MysqlEngine {
127
133
  connection: {
128
134
  ...this.#connection,
129
135
  database: databaseName,
130
- connectTimeout: 3e4
136
+ connectTimeout: 1e4
131
137
  },
132
- ...types.LARGER_POOL_CONFIG
138
+ ...types.TEST_POOL_CONFIG
133
139
  });
134
140
  this.#knexInstances.push(knexInstance);
135
141
  return knexInstance;
@@ -139,22 +145,35 @@ class MysqlEngine {
139
145
  }
140
146
  async shutdown() {
141
147
  for (const instance of this.#knexInstances) {
142
- await instance.destroy();
148
+ try {
149
+ await instance.destroy();
150
+ } catch {
151
+ }
143
152
  }
144
- const adminConnection = this.#connectAdmin();
153
+ let adminConnection;
145
154
  try {
155
+ adminConnection = this.#connectAdmin();
146
156
  for (const databaseName of this.#databaseNames) {
147
- await adminConnection.raw("DROP DATABASE ??", [databaseName]);
157
+ try {
158
+ await adminConnection.raw("DROP DATABASE ??", [databaseName]);
159
+ } catch {
160
+ }
148
161
  }
162
+ } catch {
149
163
  } finally {
150
- await adminConnection.destroy();
164
+ await adminConnection?.destroy().catch(() => {
165
+ });
166
+ }
167
+ try {
168
+ await this.#stopContainer?.();
169
+ } catch {
151
170
  }
152
- await this.#stopContainer?.();
153
171
  }
154
172
  #connectAdmin() {
155
173
  const connection = {
156
174
  ...this.#connection,
157
- database: null
175
+ database: null,
176
+ connectTimeout: 1e4
158
177
  };
159
178
  return knexFactory__default.default({
160
179
  client: this.#properties.driver,
@@ -162,9 +181,10 @@ class MysqlEngine {
162
181
  pool: {
163
182
  min: 0,
164
183
  max: 1,
165
- acquireTimeoutMillis: 2e4,
166
- createTimeoutMillis: 2e4,
167
- createRetryIntervalMillis: 1e3
184
+ acquireTimeoutMillis: 3e4,
185
+ createTimeoutMillis: 3e4,
186
+ createRetryIntervalMillis: 1e3,
187
+ destroyTimeoutMillis: 5e3
168
188
  }
169
189
  });
170
190
  }
@@ -1 +1 @@
1
- {"version":3,"file":"mysql.cjs.js","sources":["../../src/database/mysql.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { randomBytes } from 'node:crypto';\nimport knexFactory, { Knex } from 'knex';\nimport { randomUUID as uuid } from 'node:crypto';\nimport yn from 'yn';\nimport { waitForReady } from '../util/waitForReady';\nimport { Engine, LARGER_POOL_CONFIG, TestDatabaseProperties } from './types';\n\nasync function waitForMysqlReady(\n connection: Knex.MySqlConnectionConfig,\n): Promise<void> {\n await waitForReady(async () => {\n const knex = knexFactory({\n client: 'mysql2',\n connection: {\n // make a copy because the driver mutates this\n ...connection,\n },\n });\n try {\n const result = await knex.select(knex.raw('version() AS version'));\n return Array.isArray(result) && Boolean(result[0]?.version);\n } finally {\n await knex.destroy();\n }\n }, 'the database');\n}\n\nexport async function startMysqlContainer(image: string): Promise<{\n connection: Knex.MySqlConnectionConfig;\n stopContainer: () => Promise<void>;\n}> {\n const user = 'root';\n const password = uuid();\n\n // Lazy-load to avoid side-effect of importing testcontainers\n const { GenericContainer } =\n require('testcontainers') as typeof import('testcontainers');\n\n const container = await new GenericContainer(image)\n .withExposedPorts(3306)\n .withEnvironment({ MYSQL_ROOT_PASSWORD: password })\n .withTmpFs({ '/var/lib/mysql': 'rw' })\n .withCommand([\n '--default-authentication-plugin=mysql_native_password',\n '--skip-log-bin',\n ])\n .start();\n\n const host = container.getHost();\n const port = container.getMappedPort(3306);\n const connection = { host, port, user, password };\n const stopContainer = async () => {\n await container.stop({ timeout: 10_000 });\n };\n\n await waitForMysqlReady(connection);\n\n return { connection, stopContainer };\n}\n\nexport function parseMysqlConnectionString(\n connectionString: string,\n): Knex.MySqlConnectionConfig {\n try {\n const {\n protocol,\n username,\n password,\n port,\n hostname,\n pathname,\n searchParams,\n } = new URL(connectionString);\n\n if (protocol !== 'mysql:') {\n throw new Error(`Unknown protocol ${protocol}`);\n } else if (!username || !password) {\n throw new Error(`Missing username/password`);\n } else if (!pathname.match(/^\\/[^/]+$/)) {\n throw new Error(`Expected single path segment`);\n }\n\n const result: Knex.MySqlConnectionConfig = {\n user: username,\n password,\n host: hostname,\n port: Number(port || 3306),\n database: decodeURIComponent(pathname.substring(1)),\n };\n\n const ssl = searchParams.get('ssl');\n if (ssl) {\n result.ssl = ssl;\n }\n\n const debug = searchParams.get('debug');\n if (debug) {\n result.debug = yn(debug);\n }\n\n return result;\n } catch (e) {\n throw new Error(`Error while parsing MySQL connection string, ${e}`, e);\n }\n}\n\nexport class MysqlEngine implements Engine {\n static async create(\n properties: TestDatabaseProperties,\n ): Promise<MysqlEngine> {\n const { connectionStringEnvironmentVariableName, dockerImageName } =\n properties;\n\n if (connectionStringEnvironmentVariableName) {\n const connectionString =\n process.env[connectionStringEnvironmentVariableName];\n if (connectionString) {\n const connection = parseMysqlConnectionString(connectionString);\n return new MysqlEngine(\n properties,\n connection as Knex.MySqlConnectionConfig,\n );\n }\n }\n\n if (dockerImageName) {\n const { connection, stopContainer } = await startMysqlContainer(\n dockerImageName,\n );\n return new MysqlEngine(properties, connection, stopContainer);\n }\n\n throw new Error(`Test databasee for ${properties.name} not configured`);\n }\n\n readonly #properties: TestDatabaseProperties;\n readonly #connection: Knex.MySqlConnectionConfig;\n readonly #knexInstances: Knex[];\n readonly #databaseNames: string[];\n readonly #stopContainer?: () => Promise<void>;\n\n constructor(\n properties: TestDatabaseProperties,\n connection: Knex.MySqlConnectionConfig,\n stopContainer?: () => Promise<void>,\n ) {\n this.#properties = properties;\n this.#connection = connection;\n this.#knexInstances = [];\n this.#databaseNames = [];\n this.#stopContainer = stopContainer;\n }\n\n async createDatabaseInstance(): Promise<Knex> {\n const adminConnection = this.#connectAdmin();\n try {\n const databaseName = `db${randomBytes(16).toString('hex')}`;\n\n await adminConnection.raw('CREATE DATABASE ??', [databaseName]);\n this.#databaseNames.push(databaseName);\n\n const knexInstance = knexFactory({\n client: this.#properties.driver,\n connection: {\n ...this.#connection,\n database: databaseName,\n connectTimeout: 30_000,\n },\n ...LARGER_POOL_CONFIG,\n });\n this.#knexInstances.push(knexInstance);\n\n return knexInstance;\n } finally {\n await adminConnection.destroy();\n }\n }\n\n async shutdown(): Promise<void> {\n for (const instance of this.#knexInstances) {\n await instance.destroy();\n }\n\n const adminConnection = this.#connectAdmin();\n try {\n for (const databaseName of this.#databaseNames) {\n await adminConnection.raw('DROP DATABASE ??', [databaseName]);\n }\n } finally {\n await adminConnection.destroy();\n }\n\n await this.#stopContainer?.();\n }\n\n #connectAdmin(): Knex {\n const connection = {\n ...this.#connection,\n database: null as unknown as string,\n };\n return knexFactory({\n client: this.#properties.driver,\n connection,\n pool: {\n min: 0,\n max: 1,\n acquireTimeoutMillis: 20_000,\n createTimeoutMillis: 20_000,\n createRetryIntervalMillis: 1_000,\n },\n });\n }\n}\n"],"names":["waitForReady","knexFactory","uuid","yn","randomBytes","LARGER_POOL_CONFIG"],"mappings":"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"}
1
+ {"version":3,"file":"mysql.cjs.js","sources":["../../src/database/mysql.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { randomBytes } from 'node:crypto';\nimport knexFactory, { Knex } from 'knex';\nimport { randomUUID as uuid } from 'node:crypto';\nimport yn from 'yn';\nimport { waitForReady } from '../util/waitForReady';\nimport { Engine, TEST_POOL_CONFIG, TestDatabaseProperties } from './types';\n\nasync function waitForMysqlReady(\n connection: Knex.MySqlConnectionConfig,\n): Promise<void> {\n const knex = knexFactory({\n client: 'mysql2',\n connection: {\n ...connection,\n connectTimeout: 5_000,\n },\n pool: { min: 0, max: 1 },\n });\n try {\n await waitForReady(\n async () => {\n const result = await knex.select(knex.raw('version() AS version'));\n return Array.isArray(result) && Boolean(result[0]?.version);\n },\n 'the database',\n 60_000,\n );\n } finally {\n await knex.destroy();\n }\n}\n\nexport async function startMysqlContainer(image: string): Promise<{\n connection: Knex.MySqlConnectionConfig;\n stopContainer: () => Promise<void>;\n}> {\n const user = 'root';\n const password = uuid();\n\n // Lazy-load to avoid side-effect of importing testcontainers\n const { GenericContainer } =\n require('testcontainers') as typeof import('testcontainers');\n\n // Note: testcontainers supports .withReuse() to share a single container\n // across parallel Jest workers, which would reduce memory from ~640 MB per\n // worker to one shared instance. We intentionally don't enable it because\n // reused containers bypass ryuk cleanup and linger indefinitely until\n // manually stopped. See https://github.com/backstage/backstage/pull/34653\n // for the exploration and tradeoffs.\n const container = await new GenericContainer(image)\n .withExposedPorts(3306)\n .withEnvironment({ MYSQL_ROOT_PASSWORD: password })\n .withTmpFs({ '/var/lib/mysql': 'rw' })\n .withCommand([\n '--skip-log-bin',\n '--max-connections=1000',\n '--mysql-native-password=ON',\n ])\n .start();\n\n const host = container.getHost();\n const port = container.getMappedPort(3306);\n const connection = { host, port, user, password };\n const stopContainer = async () => {\n await container.stop({ timeout: 10_000 });\n };\n\n await waitForMysqlReady(connection);\n\n return { connection, stopContainer };\n}\n\nexport function parseMysqlConnectionString(\n connectionString: string,\n): Knex.MySqlConnectionConfig {\n try {\n const {\n protocol,\n username,\n password,\n port,\n hostname,\n pathname,\n searchParams,\n } = new URL(connectionString);\n\n if (protocol !== 'mysql:') {\n throw new Error(`Unknown protocol ${protocol}`);\n } else if (!username || !password) {\n throw new Error(`Missing username/password`);\n } else if (!pathname.match(/^\\/[^/]+$/)) {\n throw new Error(`Expected single path segment`);\n }\n\n const result: Knex.MySqlConnectionConfig = {\n user: username,\n password,\n host: hostname,\n port: Number(port || 3306),\n database: decodeURIComponent(pathname.substring(1)),\n };\n\n const ssl = searchParams.get('ssl');\n if (ssl) {\n result.ssl = ssl;\n }\n\n const debug = searchParams.get('debug');\n if (debug) {\n result.debug = yn(debug);\n }\n\n return result;\n } catch (e) {\n throw new Error(`Error while parsing MySQL connection string, ${e}`, e);\n }\n}\n\nexport class MysqlEngine implements Engine {\n static async create(\n properties: TestDatabaseProperties,\n ): Promise<MysqlEngine> {\n const { connectionStringEnvironmentVariableName, dockerImageName } =\n properties;\n\n if (connectionStringEnvironmentVariableName) {\n const connectionString =\n process.env[connectionStringEnvironmentVariableName];\n if (connectionString) {\n const connection = parseMysqlConnectionString(connectionString);\n return new MysqlEngine(\n properties,\n connection as Knex.MySqlConnectionConfig,\n );\n }\n }\n\n if (dockerImageName) {\n const { connection, stopContainer } = await startMysqlContainer(\n dockerImageName,\n );\n return new MysqlEngine(properties, connection, stopContainer);\n }\n\n throw new Error(`Test databasee for ${properties.name} not configured`);\n }\n\n readonly #properties: TestDatabaseProperties;\n readonly #connection: Knex.MySqlConnectionConfig;\n readonly #knexInstances: Knex[];\n readonly #databaseNames: string[];\n readonly #stopContainer?: () => Promise<void>;\n\n constructor(\n properties: TestDatabaseProperties,\n connection: Knex.MySqlConnectionConfig,\n stopContainer?: () => Promise<void>,\n ) {\n this.#properties = properties;\n this.#connection = connection;\n this.#knexInstances = [];\n this.#databaseNames = [];\n this.#stopContainer = stopContainer;\n }\n\n async createDatabaseInstance(): Promise<Knex> {\n const adminConnection = this.#connectAdmin();\n try {\n const databaseName = `db${randomBytes(16).toString('hex')}`;\n\n await adminConnection.raw('CREATE DATABASE ??', [databaseName]);\n this.#databaseNames.push(databaseName);\n\n const knexInstance = knexFactory({\n client: this.#properties.driver,\n connection: {\n ...this.#connection,\n database: databaseName,\n connectTimeout: 10_000,\n },\n ...TEST_POOL_CONFIG,\n });\n this.#knexInstances.push(knexInstance);\n\n return knexInstance;\n } finally {\n await adminConnection.destroy();\n }\n }\n\n async shutdown(): Promise<void> {\n for (const instance of this.#knexInstances) {\n try {\n await instance.destroy();\n } catch {\n // Best-effort — the connection may already be dead\n }\n }\n\n let adminConnection: Knex | undefined;\n try {\n adminConnection = this.#connectAdmin();\n for (const databaseName of this.#databaseNames) {\n try {\n await adminConnection.raw('DROP DATABASE ??', [databaseName]);\n } catch {\n // Best-effort — the database may already be gone\n }\n }\n } catch {\n // Best-effort — the container may already be stopped\n } finally {\n await adminConnection?.destroy().catch(() => {});\n }\n\n try {\n await this.#stopContainer?.();\n } catch {\n // Best-effort\n }\n }\n\n #connectAdmin(): Knex {\n const connection = {\n ...this.#connection,\n database: null as unknown as string,\n connectTimeout: 10_000,\n };\n return knexFactory({\n client: this.#properties.driver,\n connection,\n pool: {\n min: 0,\n max: 1,\n acquireTimeoutMillis: 30_000,\n createTimeoutMillis: 30_000,\n createRetryIntervalMillis: 1_000,\n destroyTimeoutMillis: 5_000,\n },\n });\n }\n}\n"],"names":["knexFactory","waitForReady","uuid","yn","randomBytes","TEST_POOL_CONFIG"],"mappings":"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"}
@@ -11,21 +11,23 @@ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'defau
11
11
  var knexFactory__default = /*#__PURE__*/_interopDefaultCompat(knexFactory);
12
12
 
13
13
  async function waitForPostgresReady(connection) {
14
- await waitForReady.waitForReady(async () => {
15
- const knex = knexFactory__default.default({
16
- client: "pg",
17
- connection: {
18
- // make a copy because the driver mutates this
19
- ...connection
20
- }
21
- });
22
- try {
23
- const result = await knex.select(knex.raw("version()"));
24
- return Array.isArray(result) && Boolean(result[0]?.version);
25
- } finally {
26
- await knex.destroy();
27
- }
28
- }, "the database");
14
+ const knex = knexFactory__default.default({
15
+ client: "pg",
16
+ connection: { ...connection },
17
+ pool: { min: 0, max: 1 }
18
+ });
19
+ try {
20
+ await waitForReady.waitForReady(
21
+ async () => {
22
+ const result = await knex.select(knex.raw("version()"));
23
+ return Array.isArray(result) && Boolean(result[0]?.version);
24
+ },
25
+ "the database",
26
+ 6e4
27
+ );
28
+ } finally {
29
+ await knex.destroy();
30
+ }
29
31
  }
30
32
  async function startPostgresContainer(image) {
31
33
  const user = "postgres";
@@ -35,7 +37,7 @@ async function startPostgresContainer(image) {
35
37
  // Since postgres 18, the default directory changed - so we pin it here
36
38
  PGDATA: "/var/lib/postgresql/data",
37
39
  POSTGRES_PASSWORD: password
38
- }).withTmpFs({ "/var/lib/postgresql/data": "rw" }).start();
40
+ }).withTmpFs({ "/var/lib/postgresql/data": "rw" }).withCommand(["-c", "max_connections=1000"]).start();
39
41
  const host = container.getHost();
40
42
  const port = container.getMappedPort(5432);
41
43
  const connection = { host, port, user, password };
@@ -90,7 +92,7 @@ class PostgresEngine {
90
92
  ...this.#connection,
91
93
  database: databaseName
92
94
  },
93
- ...types.LARGER_POOL_CONFIG
95
+ ...types.TEST_POOL_CONFIG
94
96
  });
95
97
  this.#knexInstances.push(knexInstance);
96
98
  return knexInstance;
@@ -100,17 +102,29 @@ class PostgresEngine {
100
102
  }
101
103
  async shutdown() {
102
104
  for (const instance of this.#knexInstances) {
103
- await instance.destroy();
105
+ try {
106
+ await instance.destroy();
107
+ } catch {
108
+ }
104
109
  }
105
- const adminConnection = this.#connectAdmin();
110
+ let adminConnection;
106
111
  try {
112
+ adminConnection = this.#connectAdmin();
107
113
  for (const databaseName of this.#databaseNames) {
108
- await adminConnection.raw("DROP DATABASE ??", [databaseName]);
114
+ try {
115
+ await adminConnection.raw("DROP DATABASE ??", [databaseName]);
116
+ } catch {
117
+ }
109
118
  }
119
+ } catch {
110
120
  } finally {
111
- await adminConnection.destroy();
121
+ await adminConnection?.destroy().catch(() => {
122
+ });
123
+ }
124
+ try {
125
+ await this.#stopContainer?.();
126
+ } catch {
112
127
  }
113
- await this.#stopContainer?.();
114
128
  }
115
129
  #connectAdmin() {
116
130
  return knexFactory__default.default({
@@ -1 +1 @@
1
- {"version":3,"file":"postgres.cjs.js","sources":["../../src/database/postgres.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { randomBytes } from 'node:crypto';\nimport knexFactory, { Knex } from 'knex';\nimport { parse as parsePgConnectionString } from 'pg-connection-string';\nimport { randomUUID as uuid } from 'node:crypto';\nimport { waitForReady } from '../util/waitForReady';\nimport { Engine, LARGER_POOL_CONFIG, TestDatabaseProperties } from './types';\n\nasync function waitForPostgresReady(\n connection: Knex.PgConnectionConfig,\n): Promise<void> {\n await waitForReady(async () => {\n const knex = knexFactory({\n client: 'pg',\n connection: {\n // make a copy because the driver mutates this\n ...connection,\n },\n });\n try {\n const result = await knex.select(knex.raw('version()'));\n return Array.isArray(result) && Boolean(result[0]?.version);\n } finally {\n await knex.destroy();\n }\n }, 'the database');\n}\n\nexport async function startPostgresContainer(image: string): Promise<{\n connection: Knex.PgConnectionConfig;\n stopContainer: () => Promise<void>;\n}> {\n const user = 'postgres';\n const password = uuid();\n\n // Lazy-load to avoid side-effect of importing testcontainers\n const { GenericContainer } =\n require('testcontainers') as typeof import('testcontainers');\n\n const container = await new GenericContainer(image)\n .withExposedPorts(5432)\n .withEnvironment({\n // Since postgres 18, the default directory changed - so we pin it here\n PGDATA: '/var/lib/postgresql/data',\n POSTGRES_PASSWORD: password,\n })\n .withTmpFs({ '/var/lib/postgresql/data': 'rw' })\n .start();\n\n const host = container.getHost();\n const port = container.getMappedPort(5432);\n const connection = { host, port, user, password };\n const stopContainer = async () => {\n await container.stop({ timeout: 10_000 });\n };\n\n await waitForPostgresReady(connection);\n\n return { connection, stopContainer };\n}\n\nexport class PostgresEngine implements Engine {\n static async create(\n properties: TestDatabaseProperties,\n ): Promise<PostgresEngine> {\n const { connectionStringEnvironmentVariableName, dockerImageName } =\n properties;\n\n if (connectionStringEnvironmentVariableName) {\n const connectionString =\n process.env[connectionStringEnvironmentVariableName];\n if (connectionString) {\n const connection = parsePgConnectionString(connectionString);\n return new PostgresEngine(\n properties,\n connection as Knex.PgConnectionConfig,\n );\n }\n }\n\n if (dockerImageName) {\n const { connection, stopContainer } = await startPostgresContainer(\n dockerImageName,\n );\n return new PostgresEngine(properties, connection, stopContainer);\n }\n\n throw new Error(`Test databasee for ${properties.name} not configured`);\n }\n\n readonly #properties: TestDatabaseProperties;\n readonly #connection: Knex.PgConnectionConfig;\n readonly #knexInstances: Knex[];\n readonly #databaseNames: string[];\n readonly #stopContainer?: () => Promise<void>;\n\n constructor(\n properties: TestDatabaseProperties,\n connection: Knex.PgConnectionConfig,\n stopContainer?: () => Promise<void>,\n ) {\n this.#properties = properties;\n this.#connection = connection;\n this.#knexInstances = [];\n this.#databaseNames = [];\n this.#stopContainer = stopContainer;\n }\n\n async createDatabaseInstance(): Promise<Knex> {\n const adminConnection = this.#connectAdmin();\n try {\n const databaseName = `db${randomBytes(16).toString('hex')}`;\n\n await adminConnection.raw('CREATE DATABASE ??', [databaseName]);\n this.#databaseNames.push(databaseName);\n\n const knexInstance = knexFactory({\n client: this.#properties.driver,\n connection: {\n ...this.#connection,\n database: databaseName,\n },\n ...LARGER_POOL_CONFIG,\n });\n this.#knexInstances.push(knexInstance);\n\n return knexInstance;\n } finally {\n await adminConnection.destroy();\n }\n }\n\n async shutdown(): Promise<void> {\n for (const instance of this.#knexInstances) {\n await instance.destroy();\n }\n\n const adminConnection = this.#connectAdmin();\n try {\n for (const databaseName of this.#databaseNames) {\n await adminConnection.raw('DROP DATABASE ??', [databaseName]);\n }\n } finally {\n await adminConnection.destroy();\n }\n\n await this.#stopContainer?.();\n }\n\n #connectAdmin(): Knex {\n return knexFactory({\n client: this.#properties.driver,\n connection: {\n ...this.#connection,\n database: 'postgres',\n },\n pool: {\n acquireTimeoutMillis: 10000,\n },\n });\n }\n}\n"],"names":["waitForReady","knexFactory","uuid","parsePgConnectionString","randomBytes","LARGER_POOL_CONFIG"],"mappings":"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"}
1
+ {"version":3,"file":"postgres.cjs.js","sources":["../../src/database/postgres.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { randomBytes } from 'node:crypto';\nimport knexFactory, { Knex } from 'knex';\nimport { parse as parsePgConnectionString } from 'pg-connection-string';\nimport { randomUUID as uuid } from 'node:crypto';\nimport { waitForReady } from '../util/waitForReady';\nimport { Engine, TEST_POOL_CONFIG, TestDatabaseProperties } from './types';\n\nasync function waitForPostgresReady(\n connection: Knex.PgConnectionConfig,\n): Promise<void> {\n const knex = knexFactory({\n client: 'pg',\n connection: { ...connection },\n pool: { min: 0, max: 1 },\n });\n try {\n await waitForReady(\n async () => {\n const result = await knex.select(knex.raw('version()'));\n return Array.isArray(result) && Boolean(result[0]?.version);\n },\n 'the database',\n 60_000,\n );\n } finally {\n await knex.destroy();\n }\n}\n\nexport async function startPostgresContainer(image: string): Promise<{\n connection: Knex.PgConnectionConfig;\n stopContainer: () => Promise<void>;\n}> {\n const user = 'postgres';\n const password = uuid();\n\n // Lazy-load to avoid side-effect of importing testcontainers\n const { GenericContainer } =\n require('testcontainers') as typeof import('testcontainers');\n\n // See note in mysql.ts about why we don't enable .withReuse() here.\n const container = await new GenericContainer(image)\n .withExposedPorts(5432)\n .withEnvironment({\n // Since postgres 18, the default directory changed - so we pin it here\n PGDATA: '/var/lib/postgresql/data',\n POSTGRES_PASSWORD: password,\n })\n .withTmpFs({ '/var/lib/postgresql/data': 'rw' })\n .withCommand(['-c', 'max_connections=1000'])\n .start();\n\n const host = container.getHost();\n const port = container.getMappedPort(5432);\n const connection = { host, port, user, password };\n const stopContainer = async () => {\n await container.stop({ timeout: 10_000 });\n };\n\n await waitForPostgresReady(connection);\n\n return { connection, stopContainer };\n}\n\nexport class PostgresEngine implements Engine {\n static async create(\n properties: TestDatabaseProperties,\n ): Promise<PostgresEngine> {\n const { connectionStringEnvironmentVariableName, dockerImageName } =\n properties;\n\n if (connectionStringEnvironmentVariableName) {\n const connectionString =\n process.env[connectionStringEnvironmentVariableName];\n if (connectionString) {\n const connection = parsePgConnectionString(connectionString);\n return new PostgresEngine(\n properties,\n connection as Knex.PgConnectionConfig,\n );\n }\n }\n\n if (dockerImageName) {\n const { connection, stopContainer } = await startPostgresContainer(\n dockerImageName,\n );\n return new PostgresEngine(properties, connection, stopContainer);\n }\n\n throw new Error(`Test databasee for ${properties.name} not configured`);\n }\n\n readonly #properties: TestDatabaseProperties;\n readonly #connection: Knex.PgConnectionConfig;\n readonly #knexInstances: Knex[];\n readonly #databaseNames: string[];\n readonly #stopContainer?: () => Promise<void>;\n\n constructor(\n properties: TestDatabaseProperties,\n connection: Knex.PgConnectionConfig,\n stopContainer?: () => Promise<void>,\n ) {\n this.#properties = properties;\n this.#connection = connection;\n this.#knexInstances = [];\n this.#databaseNames = [];\n this.#stopContainer = stopContainer;\n }\n\n async createDatabaseInstance(): Promise<Knex> {\n const adminConnection = this.#connectAdmin();\n try {\n const databaseName = `db${randomBytes(16).toString('hex')}`;\n\n await adminConnection.raw('CREATE DATABASE ??', [databaseName]);\n this.#databaseNames.push(databaseName);\n\n const knexInstance = knexFactory({\n client: this.#properties.driver,\n connection: {\n ...this.#connection,\n database: databaseName,\n },\n ...TEST_POOL_CONFIG,\n });\n this.#knexInstances.push(knexInstance);\n\n return knexInstance;\n } finally {\n await adminConnection.destroy();\n }\n }\n\n async shutdown(): Promise<void> {\n for (const instance of this.#knexInstances) {\n try {\n await instance.destroy();\n } catch {\n // Best-effort — the connection may already be dead\n }\n }\n\n let adminConnection: Knex | undefined;\n try {\n adminConnection = this.#connectAdmin();\n for (const databaseName of this.#databaseNames) {\n try {\n await adminConnection.raw('DROP DATABASE ??', [databaseName]);\n } catch {\n // Best-effort — the database may already be gone\n }\n }\n } catch {\n // Best-effort — the container may already be stopped\n } finally {\n await adminConnection?.destroy().catch(() => {});\n }\n\n try {\n await this.#stopContainer?.();\n } catch {\n // Best-effort\n }\n }\n\n #connectAdmin(): Knex {\n return knexFactory({\n client: this.#properties.driver,\n connection: {\n ...this.#connection,\n database: 'postgres',\n },\n pool: {\n acquireTimeoutMillis: 10000,\n },\n });\n }\n}\n"],"names":["knexFactory","waitForReady","uuid","parsePgConnectionString","randomBytes","TEST_POOL_CONFIG"],"mappings":"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"}
@@ -60,7 +60,7 @@ const allDatabases = Object.freeze({
60
60
  MYSQL_8: {
61
61
  name: "MySQL 8.x",
62
62
  driver: "mysql2",
63
- dockerImageName: getDockerImageForName.getDockerImageForName("mysql:8"),
63
+ dockerImageName: getDockerImageForName.getDockerImageForName("mysql:8.4"),
64
64
  connectionStringEnvironmentVariableName: "BACKSTAGE_TEST_DATABASE_MYSQL8_CONNECTION_STRING"
65
65
  },
66
66
  SQLITE_3: {
@@ -68,15 +68,18 @@ const allDatabases = Object.freeze({
68
68
  driver: "better-sqlite3"
69
69
  }
70
70
  });
71
- const LARGER_POOL_CONFIG = {
71
+ const TEST_POOL_CONFIG = {
72
72
  pool: {
73
73
  min: 0,
74
- max: 50,
74
+ max: 5,
75
75
  acquireTimeoutMillis: 3e4,
76
- createTimeoutMillis: 3e4
76
+ createTimeoutMillis: 3e4,
77
+ destroyTimeoutMillis: 5e3,
78
+ idleTimeoutMillis: 5e3,
79
+ reapIntervalMillis: 1e3
77
80
  }
78
81
  };
79
82
 
80
- exports.LARGER_POOL_CONFIG = LARGER_POOL_CONFIG;
83
+ exports.TEST_POOL_CONFIG = TEST_POOL_CONFIG;
81
84
  exports.allDatabases = allDatabases;
82
85
  //# sourceMappingURL=types.cjs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.cjs.js","sources":["../../src/database/types.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Knex } from 'knex';\nimport { getDockerImageForName } from '../util/getDockerImageForName';\n\nexport interface Engine {\n createDatabaseInstance(): Promise<Knex>;\n shutdown(): Promise<void>;\n}\n\n/**\n * The possible databases to test against.\n *\n * @public\n */\nexport type TestDatabaseId =\n | 'POSTGRES_18'\n | 'POSTGRES_17'\n | 'POSTGRES_16'\n | 'POSTGRES_15'\n | 'POSTGRES_14'\n | 'POSTGRES_13'\n | 'POSTGRES_12'\n | 'POSTGRES_11'\n | 'POSTGRES_9'\n | 'MYSQL_8'\n | 'SQLITE_3';\n\nexport type TestDatabaseProperties = {\n name: string;\n driver: string;\n dockerImageName?: string;\n connectionStringEnvironmentVariableName?: string;\n};\n\nexport const allDatabases: Record<TestDatabaseId, TestDatabaseProperties> =\n Object.freeze({\n POSTGRES_18: {\n name: 'Postgres 18.x',\n driver: 'pg',\n dockerImageName: getDockerImageForName('postgres:18'),\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_POSTGRES18_CONNECTION_STRING',\n },\n POSTGRES_17: {\n name: 'Postgres 17.x',\n driver: 'pg',\n dockerImageName: getDockerImageForName('postgres:17'),\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_POSTGRES17_CONNECTION_STRING',\n },\n POSTGRES_16: {\n name: 'Postgres 16.x',\n driver: 'pg',\n dockerImageName: getDockerImageForName('postgres:16'),\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_POSTGRES16_CONNECTION_STRING',\n },\n POSTGRES_15: {\n name: 'Postgres 15.x',\n driver: 'pg',\n dockerImageName: getDockerImageForName('postgres:15'),\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_POSTGRES15_CONNECTION_STRING',\n },\n POSTGRES_14: {\n name: 'Postgres 14.x',\n driver: 'pg',\n dockerImageName: getDockerImageForName('postgres:14'),\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_POSTGRES14_CONNECTION_STRING',\n },\n POSTGRES_13: {\n name: 'Postgres 13.x',\n driver: 'pg',\n dockerImageName: getDockerImageForName('postgres:13'),\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_POSTGRES13_CONNECTION_STRING',\n },\n POSTGRES_12: {\n name: 'Postgres 12.x',\n driver: 'pg',\n dockerImageName: getDockerImageForName('postgres:12'),\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_POSTGRES12_CONNECTION_STRING',\n },\n POSTGRES_11: {\n name: 'Postgres 11.x',\n driver: 'pg',\n dockerImageName: getDockerImageForName('postgres:11'),\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_POSTGRES11_CONNECTION_STRING',\n },\n POSTGRES_9: {\n name: 'Postgres 9.x',\n driver: 'pg',\n dockerImageName: getDockerImageForName('postgres:9'),\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_POSTGRES9_CONNECTION_STRING',\n },\n MYSQL_8: {\n name: 'MySQL 8.x',\n driver: 'mysql2',\n dockerImageName: getDockerImageForName('mysql:8'),\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_MYSQL8_CONNECTION_STRING',\n },\n SQLITE_3: {\n name: 'SQLite 3.x',\n driver: 'better-sqlite3',\n },\n });\n\nexport const LARGER_POOL_CONFIG = {\n pool: {\n min: 0,\n max: 50,\n acquireTimeoutMillis: 30_000,\n createTimeoutMillis: 30_000,\n },\n};\n"],"names":["getDockerImageForName"],"mappings":";;;;AAiDO,MAAM,YAAA,GACX,OAAO,MAAA,CAAO;AAAA,EACZ,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,eAAA;AAAA,IACN,MAAA,EAAQ,IAAA;AAAA,IACR,eAAA,EAAiBA,4CAAsB,aAAa,CAAA;AAAA,IACpD,uCAAA,EACE;AAAA,GACJ;AAAA,EACA,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,eAAA;AAAA,IACN,MAAA,EAAQ,IAAA;AAAA,IACR,eAAA,EAAiBA,4CAAsB,aAAa,CAAA;AAAA,IACpD,uCAAA,EACE;AAAA,GACJ;AAAA,EACA,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,eAAA;AAAA,IACN,MAAA,EAAQ,IAAA;AAAA,IACR,eAAA,EAAiBA,4CAAsB,aAAa,CAAA;AAAA,IACpD,uCAAA,EACE;AAAA,GACJ;AAAA,EACA,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,eAAA;AAAA,IACN,MAAA,EAAQ,IAAA;AAAA,IACR,eAAA,EAAiBA,4CAAsB,aAAa,CAAA;AAAA,IACpD,uCAAA,EACE;AAAA,GACJ;AAAA,EACA,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,eAAA;AAAA,IACN,MAAA,EAAQ,IAAA;AAAA,IACR,eAAA,EAAiBA,4CAAsB,aAAa,CAAA;AAAA,IACpD,uCAAA,EACE;AAAA,GACJ;AAAA,EACA,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,eAAA;AAAA,IACN,MAAA,EAAQ,IAAA;AAAA,IACR,eAAA,EAAiBA,4CAAsB,aAAa,CAAA;AAAA,IACpD,uCAAA,EACE;AAAA,GACJ;AAAA,EACA,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,eAAA;AAAA,IACN,MAAA,EAAQ,IAAA;AAAA,IACR,eAAA,EAAiBA,4CAAsB,aAAa,CAAA;AAAA,IACpD,uCAAA,EACE;AAAA,GACJ;AAAA,EACA,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,eAAA;AAAA,IACN,MAAA,EAAQ,IAAA;AAAA,IACR,eAAA,EAAiBA,4CAAsB,aAAa,CAAA;AAAA,IACpD,uCAAA,EACE;AAAA,GACJ;AAAA,EACA,UAAA,EAAY;AAAA,IACV,IAAA,EAAM,cAAA;AAAA,IACN,MAAA,EAAQ,IAAA;AAAA,IACR,eAAA,EAAiBA,4CAAsB,YAAY,CAAA;AAAA,IACnD,uCAAA,EACE;AAAA,GACJ;AAAA,EACA,OAAA,EAAS;AAAA,IACP,IAAA,EAAM,WAAA;AAAA,IACN,MAAA,EAAQ,QAAA;AAAA,IACR,eAAA,EAAiBA,4CAAsB,SAAS,CAAA;AAAA,IAChD,uCAAA,EACE;AAAA,GACJ;AAAA,EACA,QAAA,EAAU;AAAA,IACR,IAAA,EAAM,YAAA;AAAA,IACN,MAAA,EAAQ;AAAA;AAEZ,CAAC;AAEI,MAAM,kBAAA,GAAqB;AAAA,EAChC,IAAA,EAAM;AAAA,IACJ,GAAA,EAAK,CAAA;AAAA,IACL,GAAA,EAAK,EAAA;AAAA,IACL,oBAAA,EAAsB,GAAA;AAAA,IACtB,mBAAA,EAAqB;AAAA;AAEzB;;;;;"}
1
+ {"version":3,"file":"types.cjs.js","sources":["../../src/database/types.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Knex } from 'knex';\nimport { getDockerImageForName } from '../util/getDockerImageForName';\n\nexport interface Engine {\n createDatabaseInstance(): Promise<Knex>;\n shutdown(): Promise<void>;\n}\n\n/**\n * The possible databases to test against.\n *\n * @public\n */\nexport type TestDatabaseId =\n | 'POSTGRES_18'\n | 'POSTGRES_17'\n | 'POSTGRES_16'\n | 'POSTGRES_15'\n | 'POSTGRES_14'\n | 'POSTGRES_13'\n | 'POSTGRES_12'\n | 'POSTGRES_11'\n | 'POSTGRES_9'\n | 'MYSQL_8'\n | 'SQLITE_3';\n\nexport type TestDatabaseProperties = {\n name: string;\n driver: string;\n dockerImageName?: string;\n connectionStringEnvironmentVariableName?: string;\n};\n\nexport const allDatabases: Record<TestDatabaseId, TestDatabaseProperties> =\n Object.freeze({\n POSTGRES_18: {\n name: 'Postgres 18.x',\n driver: 'pg',\n dockerImageName: getDockerImageForName('postgres:18'),\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_POSTGRES18_CONNECTION_STRING',\n },\n POSTGRES_17: {\n name: 'Postgres 17.x',\n driver: 'pg',\n dockerImageName: getDockerImageForName('postgres:17'),\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_POSTGRES17_CONNECTION_STRING',\n },\n POSTGRES_16: {\n name: 'Postgres 16.x',\n driver: 'pg',\n dockerImageName: getDockerImageForName('postgres:16'),\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_POSTGRES16_CONNECTION_STRING',\n },\n POSTGRES_15: {\n name: 'Postgres 15.x',\n driver: 'pg',\n dockerImageName: getDockerImageForName('postgres:15'),\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_POSTGRES15_CONNECTION_STRING',\n },\n POSTGRES_14: {\n name: 'Postgres 14.x',\n driver: 'pg',\n dockerImageName: getDockerImageForName('postgres:14'),\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_POSTGRES14_CONNECTION_STRING',\n },\n POSTGRES_13: {\n name: 'Postgres 13.x',\n driver: 'pg',\n dockerImageName: getDockerImageForName('postgres:13'),\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_POSTGRES13_CONNECTION_STRING',\n },\n POSTGRES_12: {\n name: 'Postgres 12.x',\n driver: 'pg',\n dockerImageName: getDockerImageForName('postgres:12'),\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_POSTGRES12_CONNECTION_STRING',\n },\n POSTGRES_11: {\n name: 'Postgres 11.x',\n driver: 'pg',\n dockerImageName: getDockerImageForName('postgres:11'),\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_POSTGRES11_CONNECTION_STRING',\n },\n POSTGRES_9: {\n name: 'Postgres 9.x',\n driver: 'pg',\n dockerImageName: getDockerImageForName('postgres:9'),\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_POSTGRES9_CONNECTION_STRING',\n },\n MYSQL_8: {\n name: 'MySQL 8.x',\n driver: 'mysql2',\n dockerImageName: getDockerImageForName('mysql:8.4'),\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_MYSQL8_CONNECTION_STRING',\n },\n SQLITE_3: {\n name: 'SQLite 3.x',\n driver: 'better-sqlite3',\n },\n });\n\nexport const TEST_POOL_CONFIG = {\n pool: {\n min: 0,\n max: 5,\n acquireTimeoutMillis: 30_000,\n createTimeoutMillis: 30_000,\n destroyTimeoutMillis: 5_000,\n idleTimeoutMillis: 5_000,\n reapIntervalMillis: 1_000,\n },\n};\n\n/** @deprecated Use TEST_POOL_CONFIG instead */\nexport const LARGER_POOL_CONFIG = TEST_POOL_CONFIG;\n"],"names":["getDockerImageForName"],"mappings":";;;;AAiDO,MAAM,YAAA,GACX,OAAO,MAAA,CAAO;AAAA,EACZ,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,eAAA;AAAA,IACN,MAAA,EAAQ,IAAA;AAAA,IACR,eAAA,EAAiBA,4CAAsB,aAAa,CAAA;AAAA,IACpD,uCAAA,EACE;AAAA,GACJ;AAAA,EACA,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,eAAA;AAAA,IACN,MAAA,EAAQ,IAAA;AAAA,IACR,eAAA,EAAiBA,4CAAsB,aAAa,CAAA;AAAA,IACpD,uCAAA,EACE;AAAA,GACJ;AAAA,EACA,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,eAAA;AAAA,IACN,MAAA,EAAQ,IAAA;AAAA,IACR,eAAA,EAAiBA,4CAAsB,aAAa,CAAA;AAAA,IACpD,uCAAA,EACE;AAAA,GACJ;AAAA,EACA,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,eAAA;AAAA,IACN,MAAA,EAAQ,IAAA;AAAA,IACR,eAAA,EAAiBA,4CAAsB,aAAa,CAAA;AAAA,IACpD,uCAAA,EACE;AAAA,GACJ;AAAA,EACA,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,eAAA;AAAA,IACN,MAAA,EAAQ,IAAA;AAAA,IACR,eAAA,EAAiBA,4CAAsB,aAAa,CAAA;AAAA,IACpD,uCAAA,EACE;AAAA,GACJ;AAAA,EACA,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,eAAA;AAAA,IACN,MAAA,EAAQ,IAAA;AAAA,IACR,eAAA,EAAiBA,4CAAsB,aAAa,CAAA;AAAA,IACpD,uCAAA,EACE;AAAA,GACJ;AAAA,EACA,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,eAAA;AAAA,IACN,MAAA,EAAQ,IAAA;AAAA,IACR,eAAA,EAAiBA,4CAAsB,aAAa,CAAA;AAAA,IACpD,uCAAA,EACE;AAAA,GACJ;AAAA,EACA,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,eAAA;AAAA,IACN,MAAA,EAAQ,IAAA;AAAA,IACR,eAAA,EAAiBA,4CAAsB,aAAa,CAAA;AAAA,IACpD,uCAAA,EACE;AAAA,GACJ;AAAA,EACA,UAAA,EAAY;AAAA,IACV,IAAA,EAAM,cAAA;AAAA,IACN,MAAA,EAAQ,IAAA;AAAA,IACR,eAAA,EAAiBA,4CAAsB,YAAY,CAAA;AAAA,IACnD,uCAAA,EACE;AAAA,GACJ;AAAA,EACA,OAAA,EAAS;AAAA,IACP,IAAA,EAAM,WAAA;AAAA,IACN,MAAA,EAAQ,QAAA;AAAA,IACR,eAAA,EAAiBA,4CAAsB,WAAW,CAAA;AAAA,IAClD,uCAAA,EACE;AAAA,GACJ;AAAA,EACA,QAAA,EAAU;AAAA,IACR,IAAA,EAAM,YAAA;AAAA,IACN,MAAA,EAAQ;AAAA;AAEZ,CAAC;AAEI,MAAM,gBAAA,GAAmB;AAAA,EAC9B,IAAA,EAAM;AAAA,IACJ,GAAA,EAAK,CAAA;AAAA,IACL,GAAA,EAAK,CAAA;AAAA,IACL,oBAAA,EAAsB,GAAA;AAAA,IACtB,mBAAA,EAAqB,GAAA;AAAA,IACrB,oBAAA,EAAsB,GAAA;AAAA,IACtB,iBAAA,EAAmB,GAAA;AAAA,IACnB,kBAAA,EAAoB;AAAA;AAExB;;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/backend-test-utils",
3
- "version": "1.11.4",
3
+ "version": "1.11.5-next.0",
4
4
  "description": "Test helpers library for Backstage backends",
5
5
  "backstage": {
6
6
  "role": "node-library"
@@ -57,15 +57,15 @@
57
57
  "test": "backstage-cli package test"
58
58
  },
59
59
  "dependencies": {
60
- "@backstage/backend-app-api": "^1.7.1",
61
- "@backstage/backend-defaults": "^0.17.3",
62
- "@backstage/backend-plugin-api": "^1.9.2",
63
- "@backstage/config": "^1.3.8",
64
- "@backstage/errors": "^1.3.1",
65
- "@backstage/plugin-auth-node": "^0.7.2",
66
- "@backstage/plugin-events-node": "^0.4.23",
67
- "@backstage/plugin-permission-common": "^0.9.9",
68
- "@backstage/types": "^1.2.2",
60
+ "@backstage/backend-app-api": "1.7.2-next.0",
61
+ "@backstage/backend-defaults": "0.17.4-next.0",
62
+ "@backstage/backend-plugin-api": "1.9.3-next.0",
63
+ "@backstage/config": "1.3.8",
64
+ "@backstage/errors": "1.3.1",
65
+ "@backstage/plugin-auth-node": "0.7.3-next.0",
66
+ "@backstage/plugin-events-node": "0.4.24-next.0",
67
+ "@backstage/plugin-permission-common": "0.9.9",
68
+ "@backstage/types": "1.2.2",
69
69
  "@keyv/memcache": "^2.0.1",
70
70
  "@keyv/redis": "^4.0.1",
71
71
  "@keyv/valkey": "^1.0.1",
@@ -90,7 +90,7 @@
90
90
  "zod-to-json-schema": "^3.25.1"
91
91
  },
92
92
  "devDependencies": {
93
- "@backstage/cli": "^0.36.3",
93
+ "@backstage/cli": "0.36.4-next.0",
94
94
  "@types/jest": "*",
95
95
  "@types/lodash": "^4.14.151",
96
96
  "@types/supertest": "^2.0.8",