@backstage/backend-test-utils 0.4.0-next.1 → 0.4.0-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 +14 -0
- package/dist/index.cjs.js +354 -224
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +2 -5
- package/package.json +9 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @backstage/backend-test-utils
|
|
2
2
|
|
|
3
|
+
## 0.4.0-next.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 0634fdc: Refactored `TestDatabases` to no longer depend on `backend-common`
|
|
8
|
+
- Updated dependencies
|
|
9
|
+
- @backstage/backend-plugin-api@0.6.19-next.2
|
|
10
|
+
- @backstage/backend-app-api@0.7.6-next.2
|
|
11
|
+
- @backstage/plugin-auth-node@0.4.14-next.2
|
|
12
|
+
- @backstage/plugin-events-node@0.3.5-next.1
|
|
13
|
+
- @backstage/config@1.2.0
|
|
14
|
+
- @backstage/errors@1.2.4
|
|
15
|
+
- @backstage/types@1.1.1
|
|
16
|
+
|
|
3
17
|
## 0.4.0-next.1
|
|
4
18
|
|
|
5
19
|
### Minor Changes
|
package/dist/index.cjs.js
CHANGED
|
@@ -4,17 +4,18 @@ var Keyv = require('keyv');
|
|
|
4
4
|
var KeyvMemcache = require('@keyv/memcache');
|
|
5
5
|
var uuid = require('uuid');
|
|
6
6
|
var KeyvRedis = require('@keyv/redis');
|
|
7
|
-
var
|
|
8
|
-
var config = require('@backstage/config');
|
|
7
|
+
var errors = require('@backstage/errors');
|
|
9
8
|
var crypto = require('crypto');
|
|
10
|
-
var
|
|
9
|
+
var knexFactory = require('knex');
|
|
10
|
+
var yn = require('yn');
|
|
11
|
+
var pgConnectionString = require('pg-connection-string');
|
|
11
12
|
var os = require('os');
|
|
12
13
|
var backendPluginApi = require('@backstage/backend-plugin-api');
|
|
13
14
|
var fs = require('fs-extra');
|
|
14
15
|
var textextensions = require('textextensions');
|
|
15
16
|
var path = require('path');
|
|
16
17
|
var backendAppApi = require('@backstage/backend-app-api');
|
|
17
|
-
var
|
|
18
|
+
var config = require('@backstage/config');
|
|
18
19
|
var cookie = require('cookie');
|
|
19
20
|
var pluginEventsNode = require('@backstage/plugin-events-node');
|
|
20
21
|
var express = require('express');
|
|
@@ -24,7 +25,8 @@ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'defau
|
|
|
24
25
|
var Keyv__default = /*#__PURE__*/_interopDefaultCompat(Keyv);
|
|
25
26
|
var KeyvMemcache__default = /*#__PURE__*/_interopDefaultCompat(KeyvMemcache);
|
|
26
27
|
var KeyvRedis__default = /*#__PURE__*/_interopDefaultCompat(KeyvRedis);
|
|
27
|
-
var
|
|
28
|
+
var knexFactory__default = /*#__PURE__*/_interopDefaultCompat(knexFactory);
|
|
29
|
+
var yn__default = /*#__PURE__*/_interopDefaultCompat(yn);
|
|
28
30
|
var os__default = /*#__PURE__*/_interopDefaultCompat(os);
|
|
29
31
|
var fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
|
|
30
32
|
var textextensions__default = /*#__PURE__*/_interopDefaultCompat(textextensions);
|
|
@@ -298,80 +300,6 @@ class TestCaches {
|
|
|
298
300
|
}
|
|
299
301
|
}
|
|
300
302
|
|
|
301
|
-
async function waitForMysqlReady(connection) {
|
|
302
|
-
const startTime = Date.now();
|
|
303
|
-
const db = createConnection__default.default({ client: "mysql2", connection });
|
|
304
|
-
try {
|
|
305
|
-
for (; ; ) {
|
|
306
|
-
try {
|
|
307
|
-
const result = await db.select(db.raw("version() AS version"));
|
|
308
|
-
if (result[0]?.version) {
|
|
309
|
-
return;
|
|
310
|
-
}
|
|
311
|
-
} catch (e) {
|
|
312
|
-
if (Date.now() - startTime > 3e4) {
|
|
313
|
-
throw new Error(
|
|
314
|
-
`Timed out waiting for the database to be ready for connections, ${e}`
|
|
315
|
-
);
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
319
|
-
}
|
|
320
|
-
} finally {
|
|
321
|
-
db.destroy();
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
async function startMysqlContainer(image) {
|
|
325
|
-
const user = "root";
|
|
326
|
-
const password = uuid.v4();
|
|
327
|
-
const { GenericContainer } = await import('testcontainers');
|
|
328
|
-
const container = await new GenericContainer(image).withExposedPorts(3306).withEnvironment({ MYSQL_ROOT_PASSWORD: password }).withTmpFs({ "/var/lib/mysql": "rw" }).start();
|
|
329
|
-
const host = container.getHost();
|
|
330
|
-
const port = container.getMappedPort(3306);
|
|
331
|
-
const stop = async () => {
|
|
332
|
-
await container.stop({ timeout: 1e4 });
|
|
333
|
-
};
|
|
334
|
-
await waitForMysqlReady({ host, port, user, password });
|
|
335
|
-
return { host, port, user, password, stop };
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
async function waitForPostgresReady(connection) {
|
|
339
|
-
const startTime = Date.now();
|
|
340
|
-
const db = createConnection__default.default({ client: "pg", connection });
|
|
341
|
-
try {
|
|
342
|
-
for (; ; ) {
|
|
343
|
-
try {
|
|
344
|
-
const result = await db.select(db.raw("version()"));
|
|
345
|
-
if (Array.isArray(result) && result[0]?.version) {
|
|
346
|
-
return;
|
|
347
|
-
}
|
|
348
|
-
} catch (e) {
|
|
349
|
-
if (Date.now() - startTime > 3e4) {
|
|
350
|
-
throw new Error(
|
|
351
|
-
`Timed out waiting for the database to be ready for connections, ${e}`
|
|
352
|
-
);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
356
|
-
}
|
|
357
|
-
} finally {
|
|
358
|
-
db.destroy();
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
async function startPostgresContainer(image) {
|
|
362
|
-
const user = "postgres";
|
|
363
|
-
const password = uuid.v4();
|
|
364
|
-
const { GenericContainer } = await import('testcontainers');
|
|
365
|
-
const container = await new GenericContainer(image).withExposedPorts(5432).withEnvironment({ POSTGRES_PASSWORD: password }).withTmpFs({ "/var/lib/postgresql/data": "rw" }).start();
|
|
366
|
-
const host = container.getHost();
|
|
367
|
-
const port = container.getMappedPort(5432);
|
|
368
|
-
const stop = async () => {
|
|
369
|
-
await container.stop({ timeout: 1e4 });
|
|
370
|
-
};
|
|
371
|
-
await waitForPostgresReady({ host, port, user, password });
|
|
372
|
-
return { host, port, user, password, stop };
|
|
373
|
-
}
|
|
374
|
-
|
|
375
303
|
const allDatabases = Object.freeze({
|
|
376
304
|
POSTGRES_16: {
|
|
377
305
|
name: "Postgres 16.x",
|
|
@@ -426,15 +354,345 @@ const allDatabases = Object.freeze({
|
|
|
426
354
|
driver: "better-sqlite3"
|
|
427
355
|
}
|
|
428
356
|
});
|
|
429
|
-
|
|
430
357
|
const LARGER_POOL_CONFIG = {
|
|
431
358
|
pool: {
|
|
432
359
|
min: 0,
|
|
433
360
|
max: 50
|
|
434
361
|
}
|
|
435
362
|
};
|
|
363
|
+
|
|
364
|
+
async function waitForMysqlReady(connection) {
|
|
365
|
+
const startTime = Date.now();
|
|
366
|
+
let lastError;
|
|
367
|
+
let attempts = 0;
|
|
368
|
+
for (; ; ) {
|
|
369
|
+
attempts += 1;
|
|
370
|
+
let knex;
|
|
371
|
+
try {
|
|
372
|
+
knex = knexFactory__default.default({
|
|
373
|
+
client: "mysql2",
|
|
374
|
+
connection: {
|
|
375
|
+
// make a copy because the driver mutates this
|
|
376
|
+
...connection
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
const result = await knex.select(knex.raw("version() AS version"));
|
|
380
|
+
if (Array.isArray(result) && result[0]?.version) {
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
} catch (e) {
|
|
384
|
+
lastError = e;
|
|
385
|
+
} finally {
|
|
386
|
+
await knex?.destroy();
|
|
387
|
+
}
|
|
388
|
+
if (Date.now() - startTime > 3e4) {
|
|
389
|
+
throw new Error(
|
|
390
|
+
`Timed out waiting for the database to be ready for connections, ${attempts} attempts, ${lastError ? `last error was ${errors.stringifyError(lastError)}` : "(no errors thrown)"}`
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
async function startMysqlContainer(image) {
|
|
397
|
+
const user = "root";
|
|
398
|
+
const password = uuid.v4();
|
|
399
|
+
const { GenericContainer } = await import('testcontainers');
|
|
400
|
+
const container = await new GenericContainer(image).withExposedPorts(3306).withEnvironment({ MYSQL_ROOT_PASSWORD: password }).withTmpFs({ "/var/lib/mysql": "rw" }).start();
|
|
401
|
+
const host = container.getHost();
|
|
402
|
+
const port = container.getMappedPort(3306);
|
|
403
|
+
const connection = { host, port, user, password };
|
|
404
|
+
const stopContainer = async () => {
|
|
405
|
+
await container.stop({ timeout: 1e4 });
|
|
406
|
+
};
|
|
407
|
+
await waitForMysqlReady(connection);
|
|
408
|
+
return { connection, stopContainer };
|
|
409
|
+
}
|
|
410
|
+
function parseMysqlConnectionString(connectionString) {
|
|
411
|
+
try {
|
|
412
|
+
const {
|
|
413
|
+
protocol,
|
|
414
|
+
username,
|
|
415
|
+
password,
|
|
416
|
+
port,
|
|
417
|
+
hostname,
|
|
418
|
+
pathname,
|
|
419
|
+
searchParams
|
|
420
|
+
} = new URL(connectionString);
|
|
421
|
+
if (protocol !== "mysql:") {
|
|
422
|
+
throw new Error(`Unknown protocol ${protocol}`);
|
|
423
|
+
} else if (!username || !password) {
|
|
424
|
+
throw new Error(`Missing username/password`);
|
|
425
|
+
} else if (!pathname.match(/^\/[^/]+$/)) {
|
|
426
|
+
throw new Error(`Expected single path segment`);
|
|
427
|
+
}
|
|
428
|
+
const result = {
|
|
429
|
+
user: username,
|
|
430
|
+
password,
|
|
431
|
+
host: hostname,
|
|
432
|
+
port: Number(port || 3306),
|
|
433
|
+
database: decodeURIComponent(pathname.substring(1))
|
|
434
|
+
};
|
|
435
|
+
const ssl = searchParams.get("ssl");
|
|
436
|
+
if (ssl) {
|
|
437
|
+
result.ssl = ssl;
|
|
438
|
+
}
|
|
439
|
+
const debug = searchParams.get("debug");
|
|
440
|
+
if (debug) {
|
|
441
|
+
result.debug = yn__default.default(debug);
|
|
442
|
+
}
|
|
443
|
+
return result;
|
|
444
|
+
} catch (e) {
|
|
445
|
+
throw new Error(`Error while parsing MySQL connection string, ${e}`, e);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
class MysqlEngine {
|
|
449
|
+
static async create(properties) {
|
|
450
|
+
const { connectionStringEnvironmentVariableName, dockerImageName } = properties;
|
|
451
|
+
if (connectionStringEnvironmentVariableName) {
|
|
452
|
+
const connectionString = process.env[connectionStringEnvironmentVariableName];
|
|
453
|
+
if (connectionString) {
|
|
454
|
+
const connection = parseMysqlConnectionString(connectionString);
|
|
455
|
+
return new MysqlEngine(
|
|
456
|
+
properties,
|
|
457
|
+
connection
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
if (dockerImageName) {
|
|
462
|
+
const { connection, stopContainer } = await startMysqlContainer(
|
|
463
|
+
dockerImageName
|
|
464
|
+
);
|
|
465
|
+
return new MysqlEngine(properties, connection, stopContainer);
|
|
466
|
+
}
|
|
467
|
+
throw new Error(`Test databasee for ${properties.name} not configured`);
|
|
468
|
+
}
|
|
469
|
+
#properties;
|
|
470
|
+
#connection;
|
|
471
|
+
#knexInstances;
|
|
472
|
+
#databaseNames;
|
|
473
|
+
#stopContainer;
|
|
474
|
+
constructor(properties, connection, stopContainer) {
|
|
475
|
+
this.#properties = properties;
|
|
476
|
+
this.#connection = connection;
|
|
477
|
+
this.#knexInstances = [];
|
|
478
|
+
this.#databaseNames = [];
|
|
479
|
+
this.#stopContainer = stopContainer;
|
|
480
|
+
}
|
|
481
|
+
async createDatabaseInstance() {
|
|
482
|
+
const adminConnection = this.#connectAdmin();
|
|
483
|
+
try {
|
|
484
|
+
const databaseName = `db${crypto.randomBytes(16).toString("hex")}`;
|
|
485
|
+
await adminConnection.raw("CREATE DATABASE ??", [databaseName]);
|
|
486
|
+
this.#databaseNames.push(databaseName);
|
|
487
|
+
const knexInstance = knexFactory__default.default({
|
|
488
|
+
client: this.#properties.driver,
|
|
489
|
+
connection: {
|
|
490
|
+
...this.#connection,
|
|
491
|
+
database: databaseName
|
|
492
|
+
},
|
|
493
|
+
...LARGER_POOL_CONFIG
|
|
494
|
+
});
|
|
495
|
+
this.#knexInstances.push(knexInstance);
|
|
496
|
+
return knexInstance;
|
|
497
|
+
} finally {
|
|
498
|
+
await adminConnection.destroy();
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
async shutdown() {
|
|
502
|
+
for (const instance of this.#knexInstances) {
|
|
503
|
+
await instance.destroy();
|
|
504
|
+
}
|
|
505
|
+
const adminConnection = this.#connectAdmin();
|
|
506
|
+
try {
|
|
507
|
+
for (const databaseName of this.#databaseNames) {
|
|
508
|
+
await adminConnection.raw("DROP DATABASE ??", [databaseName]);
|
|
509
|
+
}
|
|
510
|
+
} finally {
|
|
511
|
+
await adminConnection.destroy();
|
|
512
|
+
}
|
|
513
|
+
await this.#stopContainer?.();
|
|
514
|
+
}
|
|
515
|
+
#connectAdmin() {
|
|
516
|
+
const connection = {
|
|
517
|
+
...this.#connection,
|
|
518
|
+
database: null
|
|
519
|
+
};
|
|
520
|
+
return knexFactory__default.default({
|
|
521
|
+
client: this.#properties.driver,
|
|
522
|
+
connection,
|
|
523
|
+
pool: {
|
|
524
|
+
acquireTimeoutMillis: 1e4
|
|
525
|
+
}
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
async function waitForPostgresReady(connection) {
|
|
531
|
+
const startTime = Date.now();
|
|
532
|
+
let lastError;
|
|
533
|
+
let attempts = 0;
|
|
534
|
+
for (; ; ) {
|
|
535
|
+
attempts += 1;
|
|
536
|
+
let knex;
|
|
537
|
+
try {
|
|
538
|
+
knex = knexFactory__default.default({
|
|
539
|
+
client: "pg",
|
|
540
|
+
connection: {
|
|
541
|
+
// make a copy because the driver mutates this
|
|
542
|
+
...connection
|
|
543
|
+
}
|
|
544
|
+
});
|
|
545
|
+
const result = await knex.select(knex.raw("version()"));
|
|
546
|
+
if (Array.isArray(result) && result[0]?.version) {
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
} catch (e) {
|
|
550
|
+
lastError = e;
|
|
551
|
+
} finally {
|
|
552
|
+
await knex?.destroy();
|
|
553
|
+
}
|
|
554
|
+
if (Date.now() - startTime > 3e4) {
|
|
555
|
+
throw new Error(
|
|
556
|
+
`Timed out waiting for the database to be ready for connections, ${attempts} attempts, ${lastError ? `last error was ${errors.stringifyError(lastError)}` : "(no errors thrown)"}`
|
|
557
|
+
);
|
|
558
|
+
}
|
|
559
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
async function startPostgresContainer(image) {
|
|
563
|
+
const user = "postgres";
|
|
564
|
+
const password = uuid.v4();
|
|
565
|
+
const { GenericContainer } = await import('testcontainers');
|
|
566
|
+
const container = await new GenericContainer(image).withExposedPorts(5432).withEnvironment({ POSTGRES_PASSWORD: password }).withTmpFs({ "/var/lib/postgresql/data": "rw" }).start();
|
|
567
|
+
const host = container.getHost();
|
|
568
|
+
const port = container.getMappedPort(5432);
|
|
569
|
+
const connection = { host, port, user, password };
|
|
570
|
+
const stopContainer = async () => {
|
|
571
|
+
await container.stop({ timeout: 1e4 });
|
|
572
|
+
};
|
|
573
|
+
await waitForPostgresReady(connection);
|
|
574
|
+
return { connection, stopContainer };
|
|
575
|
+
}
|
|
576
|
+
class PostgresEngine {
|
|
577
|
+
static async create(properties) {
|
|
578
|
+
const { connectionStringEnvironmentVariableName, dockerImageName } = properties;
|
|
579
|
+
if (connectionStringEnvironmentVariableName) {
|
|
580
|
+
const connectionString = process.env[connectionStringEnvironmentVariableName];
|
|
581
|
+
if (connectionString) {
|
|
582
|
+
const connection = pgConnectionString.parse(connectionString);
|
|
583
|
+
return new PostgresEngine(
|
|
584
|
+
properties,
|
|
585
|
+
connection
|
|
586
|
+
);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
if (dockerImageName) {
|
|
590
|
+
const { connection, stopContainer } = await startPostgresContainer(
|
|
591
|
+
dockerImageName
|
|
592
|
+
);
|
|
593
|
+
return new PostgresEngine(properties, connection, stopContainer);
|
|
594
|
+
}
|
|
595
|
+
throw new Error(`Test databasee for ${properties.name} not configured`);
|
|
596
|
+
}
|
|
597
|
+
#properties;
|
|
598
|
+
#connection;
|
|
599
|
+
#knexInstances;
|
|
600
|
+
#databaseNames;
|
|
601
|
+
#stopContainer;
|
|
602
|
+
constructor(properties, connection, stopContainer) {
|
|
603
|
+
this.#properties = properties;
|
|
604
|
+
this.#connection = connection;
|
|
605
|
+
this.#knexInstances = [];
|
|
606
|
+
this.#databaseNames = [];
|
|
607
|
+
this.#stopContainer = stopContainer;
|
|
608
|
+
}
|
|
609
|
+
async createDatabaseInstance() {
|
|
610
|
+
const adminConnection = this.#connectAdmin();
|
|
611
|
+
try {
|
|
612
|
+
const databaseName = `db${crypto.randomBytes(16).toString("hex")}`;
|
|
613
|
+
await adminConnection.raw("CREATE DATABASE ??", [databaseName]);
|
|
614
|
+
this.#databaseNames.push(databaseName);
|
|
615
|
+
const knexInstance = knexFactory__default.default({
|
|
616
|
+
client: this.#properties.driver,
|
|
617
|
+
connection: {
|
|
618
|
+
...this.#connection,
|
|
619
|
+
database: databaseName
|
|
620
|
+
},
|
|
621
|
+
...LARGER_POOL_CONFIG
|
|
622
|
+
});
|
|
623
|
+
this.#knexInstances.push(knexInstance);
|
|
624
|
+
return knexInstance;
|
|
625
|
+
} finally {
|
|
626
|
+
await adminConnection.destroy();
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
async shutdown() {
|
|
630
|
+
for (const instance of this.#knexInstances) {
|
|
631
|
+
await instance.destroy();
|
|
632
|
+
}
|
|
633
|
+
const adminConnection = this.#connectAdmin();
|
|
634
|
+
try {
|
|
635
|
+
for (const databaseName of this.#databaseNames) {
|
|
636
|
+
await adminConnection.raw("DROP DATABASE ??", [databaseName]);
|
|
637
|
+
}
|
|
638
|
+
} finally {
|
|
639
|
+
await adminConnection.destroy();
|
|
640
|
+
}
|
|
641
|
+
await this.#stopContainer?.();
|
|
642
|
+
}
|
|
643
|
+
#connectAdmin() {
|
|
644
|
+
return knexFactory__default.default({
|
|
645
|
+
client: this.#properties.driver,
|
|
646
|
+
connection: {
|
|
647
|
+
...this.#connection,
|
|
648
|
+
database: "postgres"
|
|
649
|
+
},
|
|
650
|
+
pool: {
|
|
651
|
+
acquireTimeoutMillis: 1e4
|
|
652
|
+
}
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
class SqliteEngine {
|
|
658
|
+
static async create(properties) {
|
|
659
|
+
return new SqliteEngine(properties);
|
|
660
|
+
}
|
|
661
|
+
#properties;
|
|
662
|
+
#instances;
|
|
663
|
+
constructor(properties) {
|
|
664
|
+
this.#properties = properties;
|
|
665
|
+
this.#instances = [];
|
|
666
|
+
}
|
|
667
|
+
async createDatabaseInstance() {
|
|
668
|
+
const instance = knexFactory__default.default({
|
|
669
|
+
client: this.#properties.driver,
|
|
670
|
+
connection: ":memory:",
|
|
671
|
+
useNullAsDefault: true
|
|
672
|
+
});
|
|
673
|
+
instance.client.pool.on("createSuccess", (_eventId, resource) => {
|
|
674
|
+
resource.run("PRAGMA foreign_keys = ON", () => {
|
|
675
|
+
});
|
|
676
|
+
});
|
|
677
|
+
this.#instances.push(instance);
|
|
678
|
+
return instance;
|
|
679
|
+
}
|
|
680
|
+
async shutdown() {
|
|
681
|
+
for (const instance of this.#instances) {
|
|
682
|
+
await instance.destroy();
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
436
687
|
class TestDatabases {
|
|
437
|
-
|
|
688
|
+
engineFactoryByDriver = {
|
|
689
|
+
pg: PostgresEngine.create,
|
|
690
|
+
mysql: MysqlEngine.create,
|
|
691
|
+
mysql2: MysqlEngine.create,
|
|
692
|
+
"better-sqlite3": SqliteEngine.create,
|
|
693
|
+
sqlite3: SqliteEngine.create
|
|
694
|
+
};
|
|
695
|
+
engineByTestDatabaseId;
|
|
438
696
|
supportedIds;
|
|
439
697
|
static defaultIds;
|
|
440
698
|
/**
|
|
@@ -487,7 +745,7 @@ class TestDatabases {
|
|
|
487
745
|
TestDatabases.defaultIds = options.ids;
|
|
488
746
|
}
|
|
489
747
|
constructor(supportedIds) {
|
|
490
|
-
this.
|
|
748
|
+
this.engineByTestDatabaseId = /* @__PURE__ */ new Map();
|
|
491
749
|
this.supportedIds = supportedIds;
|
|
492
750
|
}
|
|
493
751
|
supports(id) {
|
|
@@ -517,154 +775,26 @@ class TestDatabases {
|
|
|
517
775
|
`Unsupported test database ${id} for this environment, possible values are ${candidates}`
|
|
518
776
|
);
|
|
519
777
|
}
|
|
520
|
-
let
|
|
521
|
-
if (!
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
}
|
|
525
|
-
const databaseName = `db${crypto.randomBytes(16).toString("hex")}`;
|
|
526
|
-
const connection = await instance.databaseManager.forPlugin(databaseName).getClient();
|
|
527
|
-
instance.connections.push(connection);
|
|
528
|
-
instance.databaseNames.push(databaseName);
|
|
529
|
-
return connection;
|
|
530
|
-
}
|
|
531
|
-
async initAny(properties) {
|
|
532
|
-
if (properties.driver === "pg" || properties.driver === "mysql2") {
|
|
533
|
-
const envVarName = properties.connectionStringEnvironmentVariableName;
|
|
534
|
-
if (envVarName) {
|
|
535
|
-
const connectionString = process.env[envVarName];
|
|
536
|
-
if (connectionString) {
|
|
537
|
-
const config$1 = new config.ConfigReader({
|
|
538
|
-
backend: {
|
|
539
|
-
database: {
|
|
540
|
-
knexConfig: properties.driver.includes("sqlite") ? {} : LARGER_POOL_CONFIG,
|
|
541
|
-
client: properties.driver,
|
|
542
|
-
connection: connectionString
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
});
|
|
546
|
-
const databaseManager = backendCommon.DatabaseManager.fromConfig(config$1);
|
|
547
|
-
const databaseNames = [];
|
|
548
|
-
return {
|
|
549
|
-
dropDatabases: async () => {
|
|
550
|
-
await backendCommon.dropDatabase(
|
|
551
|
-
config$1.getConfig("backend.database"),
|
|
552
|
-
...databaseNames.map(
|
|
553
|
-
(databaseName) => `backstage_plugin_${databaseName}`
|
|
554
|
-
)
|
|
555
|
-
);
|
|
556
|
-
},
|
|
557
|
-
databaseManager,
|
|
558
|
-
databaseNames,
|
|
559
|
-
connections: []
|
|
560
|
-
};
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
switch (properties.driver) {
|
|
565
|
-
case "pg":
|
|
566
|
-
return this.initPostgres(properties);
|
|
567
|
-
case "mysql2":
|
|
568
|
-
return this.initMysql(properties);
|
|
569
|
-
case "better-sqlite3":
|
|
570
|
-
case "sqlite3":
|
|
571
|
-
return this.initSqlite(properties);
|
|
572
|
-
default:
|
|
778
|
+
let engine = this.engineByTestDatabaseId.get(id);
|
|
779
|
+
if (!engine) {
|
|
780
|
+
const factory = this.engineFactoryByDriver[properties.driver];
|
|
781
|
+
if (!factory) {
|
|
573
782
|
throw new Error(`Unknown database driver ${properties.driver}`);
|
|
783
|
+
}
|
|
784
|
+
engine = await factory(properties);
|
|
785
|
+
this.engineByTestDatabaseId.set(id, engine);
|
|
574
786
|
}
|
|
575
|
-
|
|
576
|
-
async initPostgres(properties) {
|
|
577
|
-
const { host, port, user, password, stop } = await startPostgresContainer(
|
|
578
|
-
properties.dockerImageName
|
|
579
|
-
);
|
|
580
|
-
const databaseManager = backendCommon.DatabaseManager.fromConfig(
|
|
581
|
-
new config.ConfigReader({
|
|
582
|
-
backend: {
|
|
583
|
-
database: {
|
|
584
|
-
knexConfig: LARGER_POOL_CONFIG,
|
|
585
|
-
client: "pg",
|
|
586
|
-
connection: { host, port, user, password }
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
})
|
|
590
|
-
);
|
|
591
|
-
return {
|
|
592
|
-
stopContainer: stop,
|
|
593
|
-
databaseManager,
|
|
594
|
-
databaseNames: [],
|
|
595
|
-
connections: []
|
|
596
|
-
};
|
|
597
|
-
}
|
|
598
|
-
async initMysql(properties) {
|
|
599
|
-
const { host, port, user, password, stop } = await startMysqlContainer(
|
|
600
|
-
properties.dockerImageName
|
|
601
|
-
);
|
|
602
|
-
const databaseManager = backendCommon.DatabaseManager.fromConfig(
|
|
603
|
-
new config.ConfigReader({
|
|
604
|
-
backend: {
|
|
605
|
-
database: {
|
|
606
|
-
knexConfig: LARGER_POOL_CONFIG,
|
|
607
|
-
client: "mysql2",
|
|
608
|
-
connection: { host, port, user, password }
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
})
|
|
612
|
-
);
|
|
613
|
-
return {
|
|
614
|
-
stopContainer: stop,
|
|
615
|
-
databaseManager,
|
|
616
|
-
databaseNames: [],
|
|
617
|
-
connections: []
|
|
618
|
-
};
|
|
619
|
-
}
|
|
620
|
-
async initSqlite(properties) {
|
|
621
|
-
const databaseManager = backendCommon.DatabaseManager.fromConfig(
|
|
622
|
-
new config.ConfigReader({
|
|
623
|
-
backend: {
|
|
624
|
-
database: {
|
|
625
|
-
client: properties.driver,
|
|
626
|
-
connection: ":memory:"
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
})
|
|
630
|
-
);
|
|
631
|
-
return {
|
|
632
|
-
databaseManager,
|
|
633
|
-
databaseNames: [],
|
|
634
|
-
connections: []
|
|
635
|
-
};
|
|
787
|
+
return await engine.createDatabaseInstance();
|
|
636
788
|
}
|
|
637
789
|
async shutdown() {
|
|
638
|
-
const
|
|
639
|
-
this.
|
|
640
|
-
for (const {
|
|
641
|
-
stopContainer,
|
|
642
|
-
dropDatabases,
|
|
643
|
-
connections,
|
|
644
|
-
databaseManager
|
|
645
|
-
} of instances) {
|
|
646
|
-
for (const connection of connections) {
|
|
647
|
-
try {
|
|
648
|
-
await connection.destroy();
|
|
649
|
-
} catch (error) {
|
|
650
|
-
console.warn(`TestDatabases: Failed to destroy connection`, {
|
|
651
|
-
connection,
|
|
652
|
-
error
|
|
653
|
-
});
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
try {
|
|
657
|
-
await dropDatabases?.();
|
|
658
|
-
} catch (error) {
|
|
659
|
-
console.warn(`TestDatabases: Failed to drop databases`, {
|
|
660
|
-
error
|
|
661
|
-
});
|
|
662
|
-
}
|
|
790
|
+
const engines = [...this.engineByTestDatabaseId.values()];
|
|
791
|
+
this.engineByTestDatabaseId.clear();
|
|
792
|
+
for (const engine of engines) {
|
|
663
793
|
try {
|
|
664
|
-
await
|
|
794
|
+
await engine.shutdown();
|
|
665
795
|
} catch (error) {
|
|
666
|
-
console.warn(`TestDatabases: Failed to
|
|
667
|
-
|
|
796
|
+
console.warn(`TestDatabases: Failed to shutdown engine`, {
|
|
797
|
+
engine,
|
|
668
798
|
error
|
|
669
799
|
});
|
|
670
800
|
}
|