@backstage/backend-test-utils 1.0.1-next.0 → 1.0.1-next.2
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 +29 -0
- package/dist/backend-app-api/src/lib/DependencyGraph.cjs.js +182 -0
- package/dist/backend-app-api/src/lib/DependencyGraph.cjs.js.map +1 -0
- package/dist/backend-app-api/src/wiring/ServiceRegistry.cjs.js +240 -0
- package/dist/backend-app-api/src/wiring/ServiceRegistry.cjs.js.map +1 -0
- package/dist/cache/TestCaches.cjs.js +159 -0
- package/dist/cache/TestCaches.cjs.js.map +1 -0
- package/dist/cache/memcache.cjs.js +62 -0
- package/dist/cache/memcache.cjs.js.map +1 -0
- package/dist/cache/redis.cjs.js +62 -0
- package/dist/cache/redis.cjs.js.map +1 -0
- package/dist/cache/types.cjs.js +25 -0
- package/dist/cache/types.cjs.js.map +1 -0
- package/dist/database/TestDatabases.cjs.js +128 -0
- package/dist/database/TestDatabases.cjs.js.map +1 -0
- package/dist/database/mysql.cjs.js +188 -0
- package/dist/database/mysql.cjs.js.map +1 -0
- package/dist/database/postgres.cjs.js +143 -0
- package/dist/database/postgres.cjs.js.map +1 -0
- package/dist/database/sqlite.cjs.js +40 -0
- package/dist/database/sqlite.cjs.js.map +1 -0
- package/dist/database/types.cjs.js +68 -0
- package/dist/database/types.cjs.js.map +1 -0
- package/dist/filesystem/MockDirectory.cjs.js +152 -0
- package/dist/filesystem/MockDirectory.cjs.js.map +1 -0
- package/dist/index.cjs.js +25 -2327
- package/dist/index.cjs.js.map +1 -1
- package/dist/msw/registerMswTestHooks.cjs.js +10 -0
- package/dist/msw/registerMswTestHooks.cjs.js.map +1 -0
- package/dist/next/services/MockAuthService.cjs.js +111 -0
- package/dist/next/services/MockAuthService.cjs.js.map +1 -0
- package/dist/next/services/MockHttpAuthService.cjs.js +87 -0
- package/dist/next/services/MockHttpAuthService.cjs.js.map +1 -0
- package/dist/next/services/MockRootLoggerService.cjs.js +49 -0
- package/dist/next/services/MockRootLoggerService.cjs.js.map +1 -0
- package/dist/next/services/MockUserInfoService.cjs.js +26 -0
- package/dist/next/services/MockUserInfoService.cjs.js.map +1 -0
- package/dist/next/services/mockCredentials.cjs.js +148 -0
- package/dist/next/services/mockCredentials.cjs.js.map +1 -0
- package/dist/next/services/mockServices.cjs.js +294 -0
- package/dist/next/services/mockServices.cjs.js.map +1 -0
- package/dist/next/wiring/ServiceFactoryTester.cjs.js +61 -0
- package/dist/next/wiring/ServiceFactoryTester.cjs.js.map +1 -0
- package/dist/next/wiring/TestBackend.cjs.js +258 -0
- package/dist/next/wiring/TestBackend.cjs.js.map +1 -0
- package/dist/util/errorHandler.cjs.js +18 -0
- package/dist/util/errorHandler.cjs.js.map +1 -0
- package/dist/util/getDockerImageForName.cjs.js +8 -0
- package/dist/util/getDockerImageForName.cjs.js.map +1 -0
- package/dist/util/isDockerDisabledForTests.cjs.js +8 -0
- package/dist/util/isDockerDisabledForTests.cjs.js.map +1 -0
- package/package.json +11 -11
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var errors = require('@backstage/errors');
|
|
4
|
+
var crypto = require('crypto');
|
|
5
|
+
var knexFactory = require('knex');
|
|
6
|
+
var uuid = require('uuid');
|
|
7
|
+
var yn = require('yn');
|
|
8
|
+
var types = require('./types.cjs.js');
|
|
9
|
+
|
|
10
|
+
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
11
|
+
|
|
12
|
+
var knexFactory__default = /*#__PURE__*/_interopDefaultCompat(knexFactory);
|
|
13
|
+
var yn__default = /*#__PURE__*/_interopDefaultCompat(yn);
|
|
14
|
+
|
|
15
|
+
async function waitForMysqlReady(connection) {
|
|
16
|
+
const startTime = Date.now();
|
|
17
|
+
let lastError;
|
|
18
|
+
let attempts = 0;
|
|
19
|
+
for (; ; ) {
|
|
20
|
+
attempts += 1;
|
|
21
|
+
let knex;
|
|
22
|
+
try {
|
|
23
|
+
knex = knexFactory__default.default({
|
|
24
|
+
client: "mysql2",
|
|
25
|
+
connection: {
|
|
26
|
+
// make a copy because the driver mutates this
|
|
27
|
+
...connection
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
const result = await knex.select(knex.raw("version() AS version"));
|
|
31
|
+
if (Array.isArray(result) && result[0]?.version) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
} catch (e) {
|
|
35
|
+
lastError = e;
|
|
36
|
+
} finally {
|
|
37
|
+
await knex?.destroy();
|
|
38
|
+
}
|
|
39
|
+
if (Date.now() - startTime > 3e4) {
|
|
40
|
+
throw new Error(
|
|
41
|
+
`Timed out waiting for the database to be ready for connections, ${attempts} attempts, ${lastError ? `last error was ${errors.stringifyError(lastError)}` : "(no errors thrown)"}`
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async function startMysqlContainer(image) {
|
|
48
|
+
const user = "root";
|
|
49
|
+
const password = uuid.v4();
|
|
50
|
+
const { GenericContainer } = await import('testcontainers');
|
|
51
|
+
const container = await new GenericContainer(image).withExposedPorts(3306).withEnvironment({ MYSQL_ROOT_PASSWORD: password }).withTmpFs({ "/var/lib/mysql": "rw" }).start();
|
|
52
|
+
const host = container.getHost();
|
|
53
|
+
const port = container.getMappedPort(3306);
|
|
54
|
+
const connection = { host, port, user, password };
|
|
55
|
+
const stopContainer = async () => {
|
|
56
|
+
await container.stop({ timeout: 1e4 });
|
|
57
|
+
};
|
|
58
|
+
await waitForMysqlReady(connection);
|
|
59
|
+
return { connection, stopContainer };
|
|
60
|
+
}
|
|
61
|
+
function parseMysqlConnectionString(connectionString) {
|
|
62
|
+
try {
|
|
63
|
+
const {
|
|
64
|
+
protocol,
|
|
65
|
+
username,
|
|
66
|
+
password,
|
|
67
|
+
port,
|
|
68
|
+
hostname,
|
|
69
|
+
pathname,
|
|
70
|
+
searchParams
|
|
71
|
+
} = new URL(connectionString);
|
|
72
|
+
if (protocol !== "mysql:") {
|
|
73
|
+
throw new Error(`Unknown protocol ${protocol}`);
|
|
74
|
+
} else if (!username || !password) {
|
|
75
|
+
throw new Error(`Missing username/password`);
|
|
76
|
+
} else if (!pathname.match(/^\/[^/]+$/)) {
|
|
77
|
+
throw new Error(`Expected single path segment`);
|
|
78
|
+
}
|
|
79
|
+
const result = {
|
|
80
|
+
user: username,
|
|
81
|
+
password,
|
|
82
|
+
host: hostname,
|
|
83
|
+
port: Number(port || 3306),
|
|
84
|
+
database: decodeURIComponent(pathname.substring(1))
|
|
85
|
+
};
|
|
86
|
+
const ssl = searchParams.get("ssl");
|
|
87
|
+
if (ssl) {
|
|
88
|
+
result.ssl = ssl;
|
|
89
|
+
}
|
|
90
|
+
const debug = searchParams.get("debug");
|
|
91
|
+
if (debug) {
|
|
92
|
+
result.debug = yn__default.default(debug);
|
|
93
|
+
}
|
|
94
|
+
return result;
|
|
95
|
+
} catch (e) {
|
|
96
|
+
throw new Error(`Error while parsing MySQL connection string, ${e}`, e);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
class MysqlEngine {
|
|
100
|
+
static async create(properties) {
|
|
101
|
+
const { connectionStringEnvironmentVariableName, dockerImageName } = properties;
|
|
102
|
+
if (connectionStringEnvironmentVariableName) {
|
|
103
|
+
const connectionString = process.env[connectionStringEnvironmentVariableName];
|
|
104
|
+
if (connectionString) {
|
|
105
|
+
const connection = parseMysqlConnectionString(connectionString);
|
|
106
|
+
return new MysqlEngine(
|
|
107
|
+
properties,
|
|
108
|
+
connection
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (dockerImageName) {
|
|
113
|
+
const { connection, stopContainer } = await startMysqlContainer(
|
|
114
|
+
dockerImageName
|
|
115
|
+
);
|
|
116
|
+
return new MysqlEngine(properties, connection, stopContainer);
|
|
117
|
+
}
|
|
118
|
+
throw new Error(`Test databasee for ${properties.name} not configured`);
|
|
119
|
+
}
|
|
120
|
+
#properties;
|
|
121
|
+
#connection;
|
|
122
|
+
#knexInstances;
|
|
123
|
+
#databaseNames;
|
|
124
|
+
#stopContainer;
|
|
125
|
+
constructor(properties, connection, stopContainer) {
|
|
126
|
+
this.#properties = properties;
|
|
127
|
+
this.#connection = connection;
|
|
128
|
+
this.#knexInstances = [];
|
|
129
|
+
this.#databaseNames = [];
|
|
130
|
+
this.#stopContainer = stopContainer;
|
|
131
|
+
}
|
|
132
|
+
async createDatabaseInstance() {
|
|
133
|
+
const adminConnection = this.#connectAdmin();
|
|
134
|
+
try {
|
|
135
|
+
const databaseName = `db${crypto.randomBytes(16).toString("hex")}`;
|
|
136
|
+
await adminConnection.raw("CREATE DATABASE ??", [databaseName]);
|
|
137
|
+
this.#databaseNames.push(databaseName);
|
|
138
|
+
const knexInstance = knexFactory__default.default({
|
|
139
|
+
client: this.#properties.driver,
|
|
140
|
+
connection: {
|
|
141
|
+
...this.#connection,
|
|
142
|
+
database: databaseName
|
|
143
|
+
},
|
|
144
|
+
...types.LARGER_POOL_CONFIG
|
|
145
|
+
});
|
|
146
|
+
this.#knexInstances.push(knexInstance);
|
|
147
|
+
return knexInstance;
|
|
148
|
+
} finally {
|
|
149
|
+
await adminConnection.destroy();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
async shutdown() {
|
|
153
|
+
for (const instance of this.#knexInstances) {
|
|
154
|
+
await instance.destroy();
|
|
155
|
+
}
|
|
156
|
+
const adminConnection = this.#connectAdmin();
|
|
157
|
+
try {
|
|
158
|
+
for (const databaseName of this.#databaseNames) {
|
|
159
|
+
await adminConnection.raw("DROP DATABASE ??", [databaseName]);
|
|
160
|
+
}
|
|
161
|
+
} finally {
|
|
162
|
+
await adminConnection.destroy();
|
|
163
|
+
}
|
|
164
|
+
await this.#stopContainer?.();
|
|
165
|
+
}
|
|
166
|
+
#connectAdmin() {
|
|
167
|
+
const connection = {
|
|
168
|
+
...this.#connection,
|
|
169
|
+
database: null
|
|
170
|
+
};
|
|
171
|
+
return knexFactory__default.default({
|
|
172
|
+
client: this.#properties.driver,
|
|
173
|
+
connection,
|
|
174
|
+
pool: {
|
|
175
|
+
min: 0,
|
|
176
|
+
max: 1,
|
|
177
|
+
acquireTimeoutMillis: 2e4,
|
|
178
|
+
createTimeoutMillis: 2e4,
|
|
179
|
+
createRetryIntervalMillis: 1e3
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
exports.MysqlEngine = MysqlEngine;
|
|
186
|
+
exports.parseMysqlConnectionString = parseMysqlConnectionString;
|
|
187
|
+
exports.startMysqlContainer = startMysqlContainer;
|
|
188
|
+
//# sourceMappingURL=mysql.cjs.js.map
|
|
@@ -0,0 +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 { stringifyError } from '@backstage/errors';\nimport { randomBytes } from 'crypto';\nimport knexFactory, { Knex } from 'knex';\nimport { v4 as uuid } from 'uuid';\nimport yn from 'yn';\nimport { Engine, LARGER_POOL_CONFIG, TestDatabaseProperties } from './types';\n\nasync function waitForMysqlReady(\n connection: Knex.MySqlConnectionConfig,\n): Promise<void> {\n const startTime = Date.now();\n\n let lastError: Error | undefined;\n let attempts = 0;\n for (;;) {\n attempts += 1;\n\n let knex: Knex | undefined;\n try {\n knex = knexFactory({\n client: 'mysql2',\n connection: {\n // make a copy because the driver mutates this\n ...connection,\n },\n });\n const result = await knex.select(knex.raw('version() AS version'));\n if (Array.isArray(result) && result[0]?.version) {\n return;\n }\n } catch (e) {\n lastError = e;\n } finally {\n await knex?.destroy();\n }\n\n if (Date.now() - startTime > 30_000) {\n throw new Error(\n `Timed out waiting for the database to be ready for connections, ${attempts} attempts, ${\n lastError\n ? `last error was ${stringifyError(lastError)}`\n : '(no errors thrown)'\n }`,\n );\n }\n\n await new Promise(resolve => setTimeout(resolve, 100));\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 } = await 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 .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 },\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":["knexFactory","stringifyError","uuid","yn","randomBytes","LARGER_POOL_CONFIG"],"mappings":";;;;;;;;;;;;;;AAuBA,eAAe,kBACb,UACe,EAAA;AACf,EAAM,MAAA,SAAA,GAAY,KAAK,GAAI,EAAA,CAAA;AAE3B,EAAI,IAAA,SAAA,CAAA;AACJ,EAAA,IAAI,QAAW,GAAA,CAAA,CAAA;AACf,EAAS,WAAA;AACP,IAAY,QAAA,IAAA,CAAA,CAAA;AAEZ,IAAI,IAAA,IAAA,CAAA;AACJ,IAAI,IAAA;AACF,MAAA,IAAA,GAAOA,4BAAY,CAAA;AAAA,QACjB,MAAQ,EAAA,QAAA;AAAA,QACR,UAAY,EAAA;AAAA;AAAA,UAEV,GAAG,UAAA;AAAA,SACL;AAAA,OACD,CAAA,CAAA;AACD,MAAA,MAAM,SAAS,MAAM,IAAA,CAAK,OAAO,IAAK,CAAA,GAAA,CAAI,sBAAsB,CAAC,CAAA,CAAA;AACjE,MAAA,IAAI,MAAM,OAAQ,CAAA,MAAM,KAAK,MAAO,CAAA,CAAC,GAAG,OAAS,EAAA;AAC/C,QAAA,OAAA;AAAA,OACF;AAAA,aACO,CAAG,EAAA;AACV,MAAY,SAAA,GAAA,CAAA,CAAA;AAAA,KACZ,SAAA;AACA,MAAA,MAAM,MAAM,OAAQ,EAAA,CAAA;AAAA,KACtB;AAEA,IAAA,IAAI,IAAK,CAAA,GAAA,EAAQ,GAAA,SAAA,GAAY,GAAQ,EAAA;AACnC,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,gEAAA,EAAmE,QAAQ,CACzE,WAAA,EAAA,SAAA,GACI,kBAAkBC,qBAAe,CAAA,SAAS,CAAC,CAAA,CAAA,GAC3C,oBACN,CAAA,CAAA;AAAA,OACF,CAAA;AAAA,KACF;AAEA,IAAA,MAAM,IAAI,OAAQ,CAAA,CAAA,OAAA,KAAW,UAAW,CAAA,OAAA,EAAS,GAAG,CAAC,CAAA,CAAA;AAAA,GACvD;AACF,CAAA;AAEA,eAAsB,oBAAoB,KAGvC,EAAA;AACD,EAAA,MAAM,IAAO,GAAA,MAAA,CAAA;AACb,EAAA,MAAM,WAAWC,OAAK,EAAA,CAAA;AAGtB,EAAA,MAAM,EAAE,gBAAA,EAAqB,GAAA,MAAM,OAAO,gBAAgB,CAAA,CAAA;AAE1D,EAAM,MAAA,SAAA,GAAY,MAAM,IAAI,gBAAA,CAAiB,KAAK,CAC/C,CAAA,gBAAA,CAAiB,IAAI,CACrB,CAAA,eAAA,CAAgB,EAAE,mBAAqB,EAAA,QAAA,EAAU,CACjD,CAAA,SAAA,CAAU,EAAE,gBAAkB,EAAA,IAAA,EAAM,CAAA,CACpC,KAAM,EAAA,CAAA;AAET,EAAM,MAAA,IAAA,GAAO,UAAU,OAAQ,EAAA,CAAA;AAC/B,EAAM,MAAA,IAAA,GAAO,SAAU,CAAA,aAAA,CAAc,IAAI,CAAA,CAAA;AACzC,EAAA,MAAM,UAAa,GAAA,EAAE,IAAM,EAAA,IAAA,EAAM,MAAM,QAAS,EAAA,CAAA;AAChD,EAAA,MAAM,gBAAgB,YAAY;AAChC,IAAA,MAAM,SAAU,CAAA,IAAA,CAAK,EAAE,OAAA,EAAS,KAAQ,CAAA,CAAA;AAAA,GAC1C,CAAA;AAEA,EAAA,MAAM,kBAAkB,UAAU,CAAA,CAAA;AAElC,EAAO,OAAA,EAAE,YAAY,aAAc,EAAA,CAAA;AACrC,CAAA;AAEO,SAAS,2BACd,gBAC4B,EAAA;AAC5B,EAAI,IAAA;AACF,IAAM,MAAA;AAAA,MACJ,QAAA;AAAA,MACA,QAAA;AAAA,MACA,QAAA;AAAA,MACA,IAAA;AAAA,MACA,QAAA;AAAA,MACA,QAAA;AAAA,MACA,YAAA;AAAA,KACF,GAAI,IAAI,GAAA,CAAI,gBAAgB,CAAA,CAAA;AAE5B,IAAA,IAAI,aAAa,QAAU,EAAA;AACzB,MAAA,MAAM,IAAI,KAAA,CAAM,CAAoB,iBAAA,EAAA,QAAQ,CAAE,CAAA,CAAA,CAAA;AAAA,KACrC,MAAA,IAAA,CAAC,QAAY,IAAA,CAAC,QAAU,EAAA;AACjC,MAAM,MAAA,IAAI,MAAM,CAA2B,yBAAA,CAAA,CAAA,CAAA;AAAA,KAClC,MAAA,IAAA,CAAC,QAAS,CAAA,KAAA,CAAM,WAAW,CAAG,EAAA;AACvC,MAAM,MAAA,IAAI,MAAM,CAA8B,4BAAA,CAAA,CAAA,CAAA;AAAA,KAChD;AAEA,IAAA,MAAM,MAAqC,GAAA;AAAA,MACzC,IAAM,EAAA,QAAA;AAAA,MACN,QAAA;AAAA,MACA,IAAM,EAAA,QAAA;AAAA,MACN,IAAA,EAAM,MAAO,CAAA,IAAA,IAAQ,IAAI,CAAA;AAAA,MACzB,QAAU,EAAA,kBAAA,CAAmB,QAAS,CAAA,SAAA,CAAU,CAAC,CAAC,CAAA;AAAA,KACpD,CAAA;AAEA,IAAM,MAAA,GAAA,GAAM,YAAa,CAAA,GAAA,CAAI,KAAK,CAAA,CAAA;AAClC,IAAA,IAAI,GAAK,EAAA;AACP,MAAA,MAAA,CAAO,GAAM,GAAA,GAAA,CAAA;AAAA,KACf;AAEA,IAAM,MAAA,KAAA,GAAQ,YAAa,CAAA,GAAA,CAAI,OAAO,CAAA,CAAA;AACtC,IAAA,IAAI,KAAO,EAAA;AACT,MAAO,MAAA,CAAA,KAAA,GAAQC,oBAAG,KAAK,CAAA,CAAA;AAAA,KACzB;AAEA,IAAO,OAAA,MAAA,CAAA;AAAA,WACA,CAAG,EAAA;AACV,IAAA,MAAM,IAAI,KAAA,CAAM,CAAgD,6CAAA,EAAA,CAAC,IAAI,CAAC,CAAA,CAAA;AAAA,GACxE;AACF,CAAA;AAEO,MAAM,WAA8B,CAAA;AAAA,EACzC,aAAa,OACX,UACsB,EAAA;AACtB,IAAM,MAAA,EAAE,uCAAyC,EAAA,eAAA,EAC/C,GAAA,UAAA,CAAA;AAEF,IAAA,IAAI,uCAAyC,EAAA;AAC3C,MAAM,MAAA,gBAAA,GACJ,OAAQ,CAAA,GAAA,CAAI,uCAAuC,CAAA,CAAA;AACrD,MAAA,IAAI,gBAAkB,EAAA;AACpB,QAAM,MAAA,UAAA,GAAa,2BAA2B,gBAAgB,CAAA,CAAA;AAC9D,QAAA,OAAO,IAAI,WAAA;AAAA,UACT,UAAA;AAAA,UACA,UAAA;AAAA,SACF,CAAA;AAAA,OACF;AAAA,KACF;AAEA,IAAA,IAAI,eAAiB,EAAA;AACnB,MAAA,MAAM,EAAE,UAAA,EAAY,aAAc,EAAA,GAAI,MAAM,mBAAA;AAAA,QAC1C,eAAA;AAAA,OACF,CAAA;AACA,MAAA,OAAO,IAAI,WAAA,CAAY,UAAY,EAAA,UAAA,EAAY,aAAa,CAAA,CAAA;AAAA,KAC9D;AAEA,IAAA,MAAM,IAAI,KAAA,CAAM,CAAsB,mBAAA,EAAA,UAAA,CAAW,IAAI,CAAiB,eAAA,CAAA,CAAA,CAAA;AAAA,GACxE;AAAA,EAES,WAAA,CAAA;AAAA,EACA,WAAA,CAAA;AAAA,EACA,cAAA,CAAA;AAAA,EACA,cAAA,CAAA;AAAA,EACA,cAAA,CAAA;AAAA,EAET,WAAA,CACE,UACA,EAAA,UAAA,EACA,aACA,EAAA;AACA,IAAA,IAAA,CAAK,WAAc,GAAA,UAAA,CAAA;AACnB,IAAA,IAAA,CAAK,WAAc,GAAA,UAAA,CAAA;AACnB,IAAA,IAAA,CAAK,iBAAiB,EAAC,CAAA;AACvB,IAAA,IAAA,CAAK,iBAAiB,EAAC,CAAA;AACvB,IAAA,IAAA,CAAK,cAAiB,GAAA,aAAA,CAAA;AAAA,GACxB;AAAA,EAEA,MAAM,sBAAwC,GAAA;AAC5C,IAAM,MAAA,eAAA,GAAkB,KAAK,aAAc,EAAA,CAAA;AAC3C,IAAI,IAAA;AACF,MAAA,MAAM,eAAe,CAAK,EAAA,EAAAC,kBAAA,CAAY,EAAE,CAAE,CAAA,QAAA,CAAS,KAAK,CAAC,CAAA,CAAA,CAAA;AAEzD,MAAA,MAAM,eAAgB,CAAA,GAAA,CAAI,oBAAsB,EAAA,CAAC,YAAY,CAAC,CAAA,CAAA;AAC9D,MAAK,IAAA,CAAA,cAAA,CAAe,KAAK,YAAY,CAAA,CAAA;AAErC,MAAA,MAAM,eAAeJ,4BAAY,CAAA;AAAA,QAC/B,MAAA,EAAQ,KAAK,WAAY,CAAA,MAAA;AAAA,QACzB,UAAY,EAAA;AAAA,UACV,GAAG,IAAK,CAAA,WAAA;AAAA,UACR,QAAU,EAAA,YAAA;AAAA,SACZ;AAAA,QACA,GAAGK,wBAAA;AAAA,OACJ,CAAA,CAAA;AACD,MAAK,IAAA,CAAA,cAAA,CAAe,KAAK,YAAY,CAAA,CAAA;AAErC,MAAO,OAAA,YAAA,CAAA;AAAA,KACP,SAAA;AACA,MAAA,MAAM,gBAAgB,OAAQ,EAAA,CAAA;AAAA,KAChC;AAAA,GACF;AAAA,EAEA,MAAM,QAA0B,GAAA;AAC9B,IAAW,KAAA,MAAA,QAAA,IAAY,KAAK,cAAgB,EAAA;AAC1C,MAAA,MAAM,SAAS,OAAQ,EAAA,CAAA;AAAA,KACzB;AAEA,IAAM,MAAA,eAAA,GAAkB,KAAK,aAAc,EAAA,CAAA;AAC3C,IAAI,IAAA;AACF,MAAW,KAAA,MAAA,YAAA,IAAgB,KAAK,cAAgB,EAAA;AAC9C,QAAA,MAAM,eAAgB,CAAA,GAAA,CAAI,kBAAoB,EAAA,CAAC,YAAY,CAAC,CAAA,CAAA;AAAA,OAC9D;AAAA,KACA,SAAA;AACA,MAAA,MAAM,gBAAgB,OAAQ,EAAA,CAAA;AAAA,KAChC;AAEA,IAAA,MAAM,KAAK,cAAiB,IAAA,CAAA;AAAA,GAC9B;AAAA,EAEA,aAAsB,GAAA;AACpB,IAAA,MAAM,UAAa,GAAA;AAAA,MACjB,GAAG,IAAK,CAAA,WAAA;AAAA,MACR,QAAU,EAAA,IAAA;AAAA,KACZ,CAAA;AACA,IAAA,OAAOL,4BAAY,CAAA;AAAA,MACjB,MAAA,EAAQ,KAAK,WAAY,CAAA,MAAA;AAAA,MACzB,UAAA;AAAA,MACA,IAAM,EAAA;AAAA,QACJ,GAAK,EAAA,CAAA;AAAA,QACL,GAAK,EAAA,CAAA;AAAA,QACL,oBAAsB,EAAA,GAAA;AAAA,QACtB,mBAAqB,EAAA,GAAA;AAAA,QACrB,yBAA2B,EAAA,GAAA;AAAA,OAC7B;AAAA,KACD,CAAA,CAAA;AAAA,GACH;AACF;;;;;;"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var errors = require('@backstage/errors');
|
|
4
|
+
var crypto = require('crypto');
|
|
5
|
+
var knexFactory = require('knex');
|
|
6
|
+
var pgConnectionString = require('pg-connection-string');
|
|
7
|
+
var uuid = require('uuid');
|
|
8
|
+
var types = require('./types.cjs.js');
|
|
9
|
+
|
|
10
|
+
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
11
|
+
|
|
12
|
+
var knexFactory__default = /*#__PURE__*/_interopDefaultCompat(knexFactory);
|
|
13
|
+
|
|
14
|
+
async function waitForPostgresReady(connection) {
|
|
15
|
+
const startTime = Date.now();
|
|
16
|
+
let lastError;
|
|
17
|
+
let attempts = 0;
|
|
18
|
+
for (; ; ) {
|
|
19
|
+
attempts += 1;
|
|
20
|
+
let knex;
|
|
21
|
+
try {
|
|
22
|
+
knex = knexFactory__default.default({
|
|
23
|
+
client: "pg",
|
|
24
|
+
connection: {
|
|
25
|
+
// make a copy because the driver mutates this
|
|
26
|
+
...connection
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
const result = await knex.select(knex.raw("version()"));
|
|
30
|
+
if (Array.isArray(result) && result[0]?.version) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
} catch (e) {
|
|
34
|
+
lastError = e;
|
|
35
|
+
} finally {
|
|
36
|
+
await knex?.destroy();
|
|
37
|
+
}
|
|
38
|
+
if (Date.now() - startTime > 3e4) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
`Timed out waiting for the database to be ready for connections, ${attempts} attempts, ${lastError ? `last error was ${errors.stringifyError(lastError)}` : "(no errors thrown)"}`
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async function startPostgresContainer(image) {
|
|
47
|
+
const user = "postgres";
|
|
48
|
+
const password = uuid.v4();
|
|
49
|
+
const { GenericContainer } = await import('testcontainers');
|
|
50
|
+
const container = await new GenericContainer(image).withExposedPorts(5432).withEnvironment({ POSTGRES_PASSWORD: password }).withTmpFs({ "/var/lib/postgresql/data": "rw" }).start();
|
|
51
|
+
const host = container.getHost();
|
|
52
|
+
const port = container.getMappedPort(5432);
|
|
53
|
+
const connection = { host, port, user, password };
|
|
54
|
+
const stopContainer = async () => {
|
|
55
|
+
await container.stop({ timeout: 1e4 });
|
|
56
|
+
};
|
|
57
|
+
await waitForPostgresReady(connection);
|
|
58
|
+
return { connection, stopContainer };
|
|
59
|
+
}
|
|
60
|
+
class PostgresEngine {
|
|
61
|
+
static async create(properties) {
|
|
62
|
+
const { connectionStringEnvironmentVariableName, dockerImageName } = properties;
|
|
63
|
+
if (connectionStringEnvironmentVariableName) {
|
|
64
|
+
const connectionString = process.env[connectionStringEnvironmentVariableName];
|
|
65
|
+
if (connectionString) {
|
|
66
|
+
const connection = pgConnectionString.parse(connectionString);
|
|
67
|
+
return new PostgresEngine(
|
|
68
|
+
properties,
|
|
69
|
+
connection
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (dockerImageName) {
|
|
74
|
+
const { connection, stopContainer } = await startPostgresContainer(
|
|
75
|
+
dockerImageName
|
|
76
|
+
);
|
|
77
|
+
return new PostgresEngine(properties, connection, stopContainer);
|
|
78
|
+
}
|
|
79
|
+
throw new Error(`Test databasee for ${properties.name} not configured`);
|
|
80
|
+
}
|
|
81
|
+
#properties;
|
|
82
|
+
#connection;
|
|
83
|
+
#knexInstances;
|
|
84
|
+
#databaseNames;
|
|
85
|
+
#stopContainer;
|
|
86
|
+
constructor(properties, connection, stopContainer) {
|
|
87
|
+
this.#properties = properties;
|
|
88
|
+
this.#connection = connection;
|
|
89
|
+
this.#knexInstances = [];
|
|
90
|
+
this.#databaseNames = [];
|
|
91
|
+
this.#stopContainer = stopContainer;
|
|
92
|
+
}
|
|
93
|
+
async createDatabaseInstance() {
|
|
94
|
+
const adminConnection = this.#connectAdmin();
|
|
95
|
+
try {
|
|
96
|
+
const databaseName = `db${crypto.randomBytes(16).toString("hex")}`;
|
|
97
|
+
await adminConnection.raw("CREATE DATABASE ??", [databaseName]);
|
|
98
|
+
this.#databaseNames.push(databaseName);
|
|
99
|
+
const knexInstance = knexFactory__default.default({
|
|
100
|
+
client: this.#properties.driver,
|
|
101
|
+
connection: {
|
|
102
|
+
...this.#connection,
|
|
103
|
+
database: databaseName
|
|
104
|
+
},
|
|
105
|
+
...types.LARGER_POOL_CONFIG
|
|
106
|
+
});
|
|
107
|
+
this.#knexInstances.push(knexInstance);
|
|
108
|
+
return knexInstance;
|
|
109
|
+
} finally {
|
|
110
|
+
await adminConnection.destroy();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async shutdown() {
|
|
114
|
+
for (const instance of this.#knexInstances) {
|
|
115
|
+
await instance.destroy();
|
|
116
|
+
}
|
|
117
|
+
const adminConnection = this.#connectAdmin();
|
|
118
|
+
try {
|
|
119
|
+
for (const databaseName of this.#databaseNames) {
|
|
120
|
+
await adminConnection.raw("DROP DATABASE ??", [databaseName]);
|
|
121
|
+
}
|
|
122
|
+
} finally {
|
|
123
|
+
await adminConnection.destroy();
|
|
124
|
+
}
|
|
125
|
+
await this.#stopContainer?.();
|
|
126
|
+
}
|
|
127
|
+
#connectAdmin() {
|
|
128
|
+
return knexFactory__default.default({
|
|
129
|
+
client: this.#properties.driver,
|
|
130
|
+
connection: {
|
|
131
|
+
...this.#connection,
|
|
132
|
+
database: "postgres"
|
|
133
|
+
},
|
|
134
|
+
pool: {
|
|
135
|
+
acquireTimeoutMillis: 1e4
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
exports.PostgresEngine = PostgresEngine;
|
|
142
|
+
exports.startPostgresContainer = startPostgresContainer;
|
|
143
|
+
//# sourceMappingURL=postgres.cjs.js.map
|
|
@@ -0,0 +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 { stringifyError } from '@backstage/errors';\nimport { randomBytes } from 'crypto';\nimport knexFactory, { Knex } from 'knex';\nimport { parse as parsePgConnectionString } from 'pg-connection-string';\nimport { v4 as uuid } from 'uuid';\nimport { Engine, LARGER_POOL_CONFIG, TestDatabaseProperties } from './types';\n\nasync function waitForPostgresReady(\n connection: Knex.PgConnectionConfig,\n): Promise<void> {\n const startTime = Date.now();\n\n let lastError: Error | undefined;\n let attempts = 0;\n for (;;) {\n attempts += 1;\n\n let knex: Knex | undefined;\n try {\n knex = knexFactory({\n client: 'pg',\n connection: {\n // make a copy because the driver mutates this\n ...connection,\n },\n });\n const result = await knex.select(knex.raw('version()'));\n if (Array.isArray(result) && result[0]?.version) {\n return;\n }\n } catch (e) {\n lastError = e;\n } finally {\n await knex?.destroy();\n }\n\n if (Date.now() - startTime > 30_000) {\n throw new Error(\n `Timed out waiting for the database to be ready for connections, ${attempts} attempts, ${\n lastError\n ? `last error was ${stringifyError(lastError)}`\n : '(no errors thrown)'\n }`,\n );\n }\n\n await new Promise(resolve => setTimeout(resolve, 100));\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 } = await import('testcontainers');\n\n const container = await new GenericContainer(image)\n .withExposedPorts(5432)\n .withEnvironment({ POSTGRES_PASSWORD: password })\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":["knexFactory","stringifyError","uuid","parsePgConnectionString","randomBytes","LARGER_POOL_CONFIG"],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var knexFactory = require('knex');
|
|
4
|
+
|
|
5
|
+
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
6
|
+
|
|
7
|
+
var knexFactory__default = /*#__PURE__*/_interopDefaultCompat(knexFactory);
|
|
8
|
+
|
|
9
|
+
class SqliteEngine {
|
|
10
|
+
static async create(properties) {
|
|
11
|
+
return new SqliteEngine(properties);
|
|
12
|
+
}
|
|
13
|
+
#properties;
|
|
14
|
+
#instances;
|
|
15
|
+
constructor(properties) {
|
|
16
|
+
this.#properties = properties;
|
|
17
|
+
this.#instances = [];
|
|
18
|
+
}
|
|
19
|
+
async createDatabaseInstance() {
|
|
20
|
+
const instance = knexFactory__default.default({
|
|
21
|
+
client: this.#properties.driver,
|
|
22
|
+
connection: ":memory:",
|
|
23
|
+
useNullAsDefault: true
|
|
24
|
+
});
|
|
25
|
+
instance.client.pool.on("createSuccess", (_eventId, resource) => {
|
|
26
|
+
resource.run("PRAGMA foreign_keys = ON", () => {
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
this.#instances.push(instance);
|
|
30
|
+
return instance;
|
|
31
|
+
}
|
|
32
|
+
async shutdown() {
|
|
33
|
+
for (const instance of this.#instances) {
|
|
34
|
+
await instance.destroy();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
exports.SqliteEngine = SqliteEngine;
|
|
40
|
+
//# sourceMappingURL=sqlite.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite.cjs.js","sources":["../../src/database/sqlite.ts"],"sourcesContent":["/*\n * Copyright 2024 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 knexFactory, { Knex } from 'knex';\nimport { Engine, TestDatabaseProperties } from './types';\n\nexport class SqliteEngine implements Engine {\n static async create(\n properties: TestDatabaseProperties,\n ): Promise<SqliteEngine> {\n return new SqliteEngine(properties);\n }\n\n readonly #properties: TestDatabaseProperties;\n readonly #instances: Knex[];\n\n constructor(properties: TestDatabaseProperties) {\n this.#properties = properties;\n this.#instances = [];\n }\n\n async createDatabaseInstance(): Promise<Knex> {\n const instance = knexFactory({\n client: this.#properties.driver,\n connection: ':memory:',\n useNullAsDefault: true,\n });\n\n instance.client.pool.on('createSuccess', (_eventId: any, resource: any) => {\n resource.run('PRAGMA foreign_keys = ON', () => {});\n });\n\n this.#instances.push(instance);\n return instance;\n }\n\n async shutdown(): Promise<void> {\n for (const instance of this.#instances) {\n await instance.destroy();\n }\n }\n}\n"],"names":["knexFactory"],"mappings":";;;;;;;;AAmBO,MAAM,YAA+B,CAAA;AAAA,EAC1C,aAAa,OACX,UACuB,EAAA;AACvB,IAAO,OAAA,IAAI,aAAa,UAAU,CAAA,CAAA;AAAA,GACpC;AAAA,EAES,WAAA,CAAA;AAAA,EACA,UAAA,CAAA;AAAA,EAET,YAAY,UAAoC,EAAA;AAC9C,IAAA,IAAA,CAAK,WAAc,GAAA,UAAA,CAAA;AACnB,IAAA,IAAA,CAAK,aAAa,EAAC,CAAA;AAAA,GACrB;AAAA,EAEA,MAAM,sBAAwC,GAAA;AAC5C,IAAA,MAAM,WAAWA,4BAAY,CAAA;AAAA,MAC3B,MAAA,EAAQ,KAAK,WAAY,CAAA,MAAA;AAAA,MACzB,UAAY,EAAA,UAAA;AAAA,MACZ,gBAAkB,EAAA,IAAA;AAAA,KACnB,CAAA,CAAA;AAED,IAAA,QAAA,CAAS,OAAO,IAAK,CAAA,EAAA,CAAG,eAAiB,EAAA,CAAC,UAAe,QAAkB,KAAA;AACzE,MAAS,QAAA,CAAA,GAAA,CAAI,4BAA4B,MAAM;AAAA,OAAE,CAAA,CAAA;AAAA,KAClD,CAAA,CAAA;AAED,IAAK,IAAA,CAAA,UAAA,CAAW,KAAK,QAAQ,CAAA,CAAA;AAC7B,IAAO,OAAA,QAAA,CAAA;AAAA,GACT;AAAA,EAEA,MAAM,QAA0B,GAAA;AAC9B,IAAW,KAAA,MAAA,QAAA,IAAY,KAAK,UAAY,EAAA;AACtC,MAAA,MAAM,SAAS,OAAQ,EAAA,CAAA;AAAA,KACzB;AAAA,GACF;AACF;;;;"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var getDockerImageForName = require('../util/getDockerImageForName.cjs.js');
|
|
4
|
+
|
|
5
|
+
const allDatabases = Object.freeze({
|
|
6
|
+
POSTGRES_16: {
|
|
7
|
+
name: "Postgres 16.x",
|
|
8
|
+
driver: "pg",
|
|
9
|
+
dockerImageName: getDockerImageForName.getDockerImageForName("postgres:16"),
|
|
10
|
+
connectionStringEnvironmentVariableName: "BACKSTAGE_TEST_DATABASE_POSTGRES16_CONNECTION_STRING"
|
|
11
|
+
},
|
|
12
|
+
POSTGRES_15: {
|
|
13
|
+
name: "Postgres 15.x",
|
|
14
|
+
driver: "pg",
|
|
15
|
+
dockerImageName: getDockerImageForName.getDockerImageForName("postgres:15"),
|
|
16
|
+
connectionStringEnvironmentVariableName: "BACKSTAGE_TEST_DATABASE_POSTGRES15_CONNECTION_STRING"
|
|
17
|
+
},
|
|
18
|
+
POSTGRES_14: {
|
|
19
|
+
name: "Postgres 14.x",
|
|
20
|
+
driver: "pg",
|
|
21
|
+
dockerImageName: getDockerImageForName.getDockerImageForName("postgres:14"),
|
|
22
|
+
connectionStringEnvironmentVariableName: "BACKSTAGE_TEST_DATABASE_POSTGRES14_CONNECTION_STRING"
|
|
23
|
+
},
|
|
24
|
+
POSTGRES_13: {
|
|
25
|
+
name: "Postgres 13.x",
|
|
26
|
+
driver: "pg",
|
|
27
|
+
dockerImageName: getDockerImageForName.getDockerImageForName("postgres:13"),
|
|
28
|
+
connectionStringEnvironmentVariableName: "BACKSTAGE_TEST_DATABASE_POSTGRES13_CONNECTION_STRING"
|
|
29
|
+
},
|
|
30
|
+
POSTGRES_12: {
|
|
31
|
+
name: "Postgres 12.x",
|
|
32
|
+
driver: "pg",
|
|
33
|
+
dockerImageName: getDockerImageForName.getDockerImageForName("postgres:12"),
|
|
34
|
+
connectionStringEnvironmentVariableName: "BACKSTAGE_TEST_DATABASE_POSTGRES12_CONNECTION_STRING"
|
|
35
|
+
},
|
|
36
|
+
POSTGRES_11: {
|
|
37
|
+
name: "Postgres 11.x",
|
|
38
|
+
driver: "pg",
|
|
39
|
+
dockerImageName: getDockerImageForName.getDockerImageForName("postgres:11"),
|
|
40
|
+
connectionStringEnvironmentVariableName: "BACKSTAGE_TEST_DATABASE_POSTGRES11_CONNECTION_STRING"
|
|
41
|
+
},
|
|
42
|
+
POSTGRES_9: {
|
|
43
|
+
name: "Postgres 9.x",
|
|
44
|
+
driver: "pg",
|
|
45
|
+
dockerImageName: getDockerImageForName.getDockerImageForName("postgres:9"),
|
|
46
|
+
connectionStringEnvironmentVariableName: "BACKSTAGE_TEST_DATABASE_POSTGRES9_CONNECTION_STRING"
|
|
47
|
+
},
|
|
48
|
+
MYSQL_8: {
|
|
49
|
+
name: "MySQL 8.x",
|
|
50
|
+
driver: "mysql2",
|
|
51
|
+
dockerImageName: getDockerImageForName.getDockerImageForName("mysql:8"),
|
|
52
|
+
connectionStringEnvironmentVariableName: "BACKSTAGE_TEST_DATABASE_MYSQL8_CONNECTION_STRING"
|
|
53
|
+
},
|
|
54
|
+
SQLITE_3: {
|
|
55
|
+
name: "SQLite 3.x",
|
|
56
|
+
driver: "better-sqlite3"
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
const LARGER_POOL_CONFIG = {
|
|
60
|
+
pool: {
|
|
61
|
+
min: 0,
|
|
62
|
+
max: 50
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
exports.LARGER_POOL_CONFIG = LARGER_POOL_CONFIG;
|
|
67
|
+
exports.allDatabases = allDatabases;
|
|
68
|
+
//# sourceMappingURL=types.cjs.js.map
|
|
@@ -0,0 +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_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_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 },\n};\n"],"names":["getDockerImageForName"],"mappings":";;;;AA+Ca,MAAA,YAAA,GACX,OAAO,MAAO,CAAA;AAAA,EACZ,WAAa,EAAA;AAAA,IACX,IAAM,EAAA,eAAA;AAAA,IACN,MAAQ,EAAA,IAAA;AAAA,IACR,eAAA,EAAiBA,4CAAsB,aAAa,CAAA;AAAA,IACpD,uCACE,EAAA,sDAAA;AAAA,GACJ;AAAA,EACA,WAAa,EAAA;AAAA,IACX,IAAM,EAAA,eAAA;AAAA,IACN,MAAQ,EAAA,IAAA;AAAA,IACR,eAAA,EAAiBA,4CAAsB,aAAa,CAAA;AAAA,IACpD,uCACE,EAAA,sDAAA;AAAA,GACJ;AAAA,EACA,WAAa,EAAA;AAAA,IACX,IAAM,EAAA,eAAA;AAAA,IACN,MAAQ,EAAA,IAAA;AAAA,IACR,eAAA,EAAiBA,4CAAsB,aAAa,CAAA;AAAA,IACpD,uCACE,EAAA,sDAAA;AAAA,GACJ;AAAA,EACA,WAAa,EAAA;AAAA,IACX,IAAM,EAAA,eAAA;AAAA,IACN,MAAQ,EAAA,IAAA;AAAA,IACR,eAAA,EAAiBA,4CAAsB,aAAa,CAAA;AAAA,IACpD,uCACE,EAAA,sDAAA;AAAA,GACJ;AAAA,EACA,WAAa,EAAA;AAAA,IACX,IAAM,EAAA,eAAA;AAAA,IACN,MAAQ,EAAA,IAAA;AAAA,IACR,eAAA,EAAiBA,4CAAsB,aAAa,CAAA;AAAA,IACpD,uCACE,EAAA,sDAAA;AAAA,GACJ;AAAA,EACA,WAAa,EAAA;AAAA,IACX,IAAM,EAAA,eAAA;AAAA,IACN,MAAQ,EAAA,IAAA;AAAA,IACR,eAAA,EAAiBA,4CAAsB,aAAa,CAAA;AAAA,IACpD,uCACE,EAAA,sDAAA;AAAA,GACJ;AAAA,EACA,UAAY,EAAA;AAAA,IACV,IAAM,EAAA,cAAA;AAAA,IACN,MAAQ,EAAA,IAAA;AAAA,IACR,eAAA,EAAiBA,4CAAsB,YAAY,CAAA;AAAA,IACnD,uCACE,EAAA,qDAAA;AAAA,GACJ;AAAA,EACA,OAAS,EAAA;AAAA,IACP,IAAM,EAAA,WAAA;AAAA,IACN,MAAQ,EAAA,QAAA;AAAA,IACR,eAAA,EAAiBA,4CAAsB,SAAS,CAAA;AAAA,IAChD,uCACE,EAAA,kDAAA;AAAA,GACJ;AAAA,EACA,QAAU,EAAA;AAAA,IACR,IAAM,EAAA,YAAA;AAAA,IACN,MAAQ,EAAA,gBAAA;AAAA,GACV;AACF,CAAC,EAAA;AAEI,MAAM,kBAAqB,GAAA;AAAA,EAChC,IAAM,EAAA;AAAA,IACJ,GAAK,EAAA,CAAA;AAAA,IACL,GAAK,EAAA,EAAA;AAAA,GACP;AACF;;;;;"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var os = require('os');
|
|
4
|
+
var backendPluginApi = require('@backstage/backend-plugin-api');
|
|
5
|
+
var fs = require('fs-extra');
|
|
6
|
+
var textextensions = require('textextensions');
|
|
7
|
+
var path = require('path');
|
|
8
|
+
|
|
9
|
+
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
10
|
+
|
|
11
|
+
var os__default = /*#__PURE__*/_interopDefaultCompat(os);
|
|
12
|
+
var fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
|
|
13
|
+
var textextensions__default = /*#__PURE__*/_interopDefaultCompat(textextensions);
|
|
14
|
+
|
|
15
|
+
const tmpdirMarker = Symbol("os-tmpdir-mock");
|
|
16
|
+
class MockDirectoryImpl {
|
|
17
|
+
#root;
|
|
18
|
+
constructor(root) {
|
|
19
|
+
this.#root = root;
|
|
20
|
+
}
|
|
21
|
+
get path() {
|
|
22
|
+
return this.#root;
|
|
23
|
+
}
|
|
24
|
+
resolve(...paths) {
|
|
25
|
+
return path.resolve(this.#root, ...paths);
|
|
26
|
+
}
|
|
27
|
+
setContent(root) {
|
|
28
|
+
this.remove();
|
|
29
|
+
return this.addContent(root);
|
|
30
|
+
}
|
|
31
|
+
addContent(root) {
|
|
32
|
+
const entries = this.#transformInput(root);
|
|
33
|
+
for (const entry of entries) {
|
|
34
|
+
const fullPath = path.resolve(this.#root, entry.path);
|
|
35
|
+
if (!backendPluginApi.isChildPath(this.#root, fullPath)) {
|
|
36
|
+
throw new Error(
|
|
37
|
+
`Provided path must resolve to a child path of the mock directory, got '${fullPath}'`
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
if (entry.type === "dir") {
|
|
41
|
+
fs__default.default.ensureDirSync(fullPath);
|
|
42
|
+
} else if (entry.type === "file") {
|
|
43
|
+
fs__default.default.ensureDirSync(path.dirname(fullPath));
|
|
44
|
+
fs__default.default.writeFileSync(fullPath, entry.content);
|
|
45
|
+
} else if (entry.type === "callback") {
|
|
46
|
+
fs__default.default.ensureDirSync(path.dirname(fullPath));
|
|
47
|
+
entry.callback({
|
|
48
|
+
path: fullPath,
|
|
49
|
+
symlink(target) {
|
|
50
|
+
fs__default.default.symlinkSync(target, fullPath);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
content(options) {
|
|
57
|
+
const shouldReadAsText = (typeof options?.shouldReadAsText === "boolean" ? () => options?.shouldReadAsText : options?.shouldReadAsText) ?? ((path$1) => textextensions__default.default.includes(path.extname(path$1).slice(1)));
|
|
58
|
+
const root = path.resolve(this.#root, options?.path ?? "");
|
|
59
|
+
if (!backendPluginApi.isChildPath(this.#root, root)) {
|
|
60
|
+
throw new Error(
|
|
61
|
+
`Provided path must resolve to a child path of the mock directory, got '${root}'`
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
function read(path$1) {
|
|
65
|
+
if (!fs__default.default.pathExistsSync(path$1)) {
|
|
66
|
+
return void 0;
|
|
67
|
+
}
|
|
68
|
+
const entries = fs__default.default.readdirSync(path$1, { withFileTypes: true });
|
|
69
|
+
return Object.fromEntries(
|
|
70
|
+
entries.map((entry) => {
|
|
71
|
+
const fullPath = path.resolve(path$1, entry.name);
|
|
72
|
+
if (entry.isDirectory()) {
|
|
73
|
+
return [entry.name, read(fullPath)];
|
|
74
|
+
}
|
|
75
|
+
const content = fs__default.default.readFileSync(fullPath);
|
|
76
|
+
const relativePosixPath = path.relative(root, fullPath).split(path.win32.sep).join(path.posix.sep);
|
|
77
|
+
if (shouldReadAsText(relativePosixPath, content)) {
|
|
78
|
+
return [entry.name, content.toString("utf8")];
|
|
79
|
+
}
|
|
80
|
+
return [entry.name, content];
|
|
81
|
+
})
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
return read(root);
|
|
85
|
+
}
|
|
86
|
+
clear = () => {
|
|
87
|
+
this.setContent({});
|
|
88
|
+
};
|
|
89
|
+
remove = () => {
|
|
90
|
+
fs__default.default.rmSync(this.#root, { recursive: true, force: true, maxRetries: 10 });
|
|
91
|
+
};
|
|
92
|
+
#transformInput(input) {
|
|
93
|
+
const entries = [];
|
|
94
|
+
function traverse(node, path) {
|
|
95
|
+
if (typeof node === "string") {
|
|
96
|
+
entries.push({
|
|
97
|
+
type: "file",
|
|
98
|
+
path,
|
|
99
|
+
content: Buffer.from(node, "utf8")
|
|
100
|
+
});
|
|
101
|
+
} else if (node instanceof Buffer) {
|
|
102
|
+
entries.push({ type: "file", path, content: node });
|
|
103
|
+
} else if (typeof node === "function") {
|
|
104
|
+
entries.push({ type: "callback", path, callback: node });
|
|
105
|
+
} else {
|
|
106
|
+
entries.push({ type: "dir", path });
|
|
107
|
+
for (const [name, child] of Object.entries(node)) {
|
|
108
|
+
traverse(child, path ? `${path}/${name}` : name);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
traverse(input, "");
|
|
113
|
+
return entries;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function createMockDirectory(options) {
|
|
117
|
+
const tmpDir = process.env.RUNNER_TEMP || os__default.default.tmpdir();
|
|
118
|
+
const root = fs__default.default.mkdtempSync(path.join(tmpDir, "backstage-tmp-test-dir-"));
|
|
119
|
+
const mocker = new MockDirectoryImpl(root);
|
|
120
|
+
const origTmpdir = options?.mockOsTmpDir ? os__default.default.tmpdir : void 0;
|
|
121
|
+
if (origTmpdir) {
|
|
122
|
+
if (Object.hasOwn(origTmpdir, tmpdirMarker)) {
|
|
123
|
+
throw new Error(
|
|
124
|
+
"Cannot mock os.tmpdir() when it has already been mocked"
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
const mock = Object.assign(() => mocker.path, { [tmpdirMarker]: true });
|
|
128
|
+
os__default.default.tmpdir = mock;
|
|
129
|
+
}
|
|
130
|
+
const needsCleanup = !process.env.CI;
|
|
131
|
+
if (needsCleanup) {
|
|
132
|
+
process.on("beforeExit", mocker.remove);
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
afterAll(() => {
|
|
136
|
+
if (origTmpdir) {
|
|
137
|
+
os__default.default.tmpdir = origTmpdir;
|
|
138
|
+
}
|
|
139
|
+
if (needsCleanup) {
|
|
140
|
+
mocker.remove();
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
} catch {
|
|
144
|
+
}
|
|
145
|
+
if (options?.content) {
|
|
146
|
+
mocker.setContent(options.content);
|
|
147
|
+
}
|
|
148
|
+
return mocker;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
exports.createMockDirectory = createMockDirectory;
|
|
152
|
+
//# sourceMappingURL=MockDirectory.cjs.js.map
|