Merge pull request #5650 from backstage/revert-5634-freben/database-tests

Revert "Introduce `@backstage/backend-testing`, with `TestDatabases` to start off"
This commit is contained in:
Fredrik Adelöw
2021-05-12 10:58:58 +02:00
committed by GitHub
14 changed files with 165 additions and 866 deletions
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/plugin-catalog-backend': patch
---
Use `TestDatabases` from `@backstage/backend-testing`
-3
View File
@@ -1,3 +0,0 @@
module.exports = {
extends: [require.resolve('@backstage/cli/config/eslint.backend')],
};
-18
View File
@@ -1,18 +0,0 @@
# @backstage/backend-testing
Test helpers library for Backstage backends.
## Usage
Add the library as a `devDependency` to your backend package:
```sh
# From the Backstage root directory, go to your backend package, or to a backend plugin
cd plugins/my-plugin-backend
yarn add --dev @backstage/backend-testing
```
## Documentation
- [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md)
- [Backstage Documentation](https://github.com/backstage/backstage/blob/master/docs/README.md)
-21
View File
@@ -1,21 +0,0 @@
## API Report File for "@backstage/backend-testing"
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
import { Knex } from 'knex';
// @public
export type TestDatabaseId = 'POSTGRES_13' | 'POSTGRES_9' | 'MYSQL_8' | 'SQLITE_3';
// @public
export class TestDatabases {
static create(): TestDatabases;
init(id: TestDatabaseId): Promise<Knex>;
}
// (No @packageDocumentation comment for this package)
```
-50
View File
@@ -1,50 +0,0 @@
{
"name": "@backstage/backend-testing",
"description": "Test helpers library for Backstage backends",
"version": "0.1.0",
"main": "src/index.ts",
"types": "src/index.ts",
"private": false,
"publishConfig": {
"access": "public",
"main": "dist/index.cjs.js",
"types": "dist/index.d.ts"
},
"homepage": "https://backstage.io",
"repository": {
"type": "git",
"url": "https://github.com/backstage/backstage",
"directory": "packages/backend-testing"
},
"keywords": [
"backstage",
"test"
],
"license": "Apache-2.0",
"scripts": {
"build": "backstage-cli build --outputs cjs,types",
"lint": "backstage-cli lint",
"test": "backstage-cli test",
"prepack": "backstage-cli prepack",
"postpack": "backstage-cli postpack",
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/backend-common": "^0.7.0",
"@backstage/cli": "^0.6.10",
"@backstage/config": "^0.1.5",
"knex": "^0.95.1",
"mysql2": "^2.2.5",
"pg": "^8.3.0",
"sqlite3": "^5.0.0",
"testcontainers": "^7.10.0",
"uuid": "^8.0.0"
},
"devDependencies": {
"@backstage/cli": "^0.6.10",
"jest": "^26.0.1"
},
"files": [
"dist"
]
}
@@ -1,154 +0,0 @@
/*
* Copyright 2021 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import knexFactory from 'knex';
import { GenericContainer } from 'testcontainers';
import { TestDatabases } from './TestDatabases';
describe('TestDatabases', () => {
const OLD_ENV = process.env;
beforeEach(() => {
jest.resetModules();
process.env = { ...OLD_ENV };
});
afterAll(() => {
process.env = OLD_ENV;
});
it.each([
['POSTGRES_13'],
['POSTGRES_9'],
['MYSQL_8'],
['SQLITE_3'],
] as const)(
'creates distinct %p databases',
async databaseId => {
const dbs = TestDatabases.create();
const db1 = await dbs.init(databaseId);
const db2 = await dbs.init(databaseId);
await db1.schema.createTable('a', table => table.string('x').primary());
await db2.schema.createTable('a', table => table.string('y').primary());
await expect(db1.select({ a: db1.raw('1') })).resolves.toEqual([
{ a: 1 },
]);
},
60_000,
);
it('obeys a provided connection string for postgres 13', async () => {
const dbs = TestDatabases.create();
const container = await new GenericContainer('postgres:13')
.withExposedPorts(5432)
.withEnv('POSTGRES_USER', 'user')
.withEnv('POSTGRES_PASSWORD', 'pass')
.withTmpFs({ '/var/lib/postgresql/data': 'rw' })
.start();
const host = container.getHost();
const port = container.getMappedPort(5432);
try {
// Leave a mark
process.env.BACKSTAGE_TEST_DATABASE_POSTGRES13_CONNECTION_STRING = `postgresql://user:pass@${host}:${port}`;
const input = await dbs.init('POSTGRES_13');
await input.schema.createTable('a', table => table.string('x').primary());
await input.insert({ x: 'y' }).into('a');
// Look for the mark
const output = knexFactory({
client: 'pg',
connection: {
host,
port,
user: 'user',
password: 'pass',
database: 'backstage_plugin_0',
},
});
await expect(output.select('x').from('a')).resolves.toEqual([{ x: 'y' }]);
} finally {
await container.stop({ timeout: 10_000 });
}
}, 60_000);
it('obeys a provided connection string for postgres 9', async () => {
const dbs = TestDatabases.create();
const container = await new GenericContainer('postgres:9')
.withExposedPorts(5432)
.withEnv('POSTGRES_USER', 'user')
.withEnv('POSTGRES_PASSWORD', 'pass')
.withTmpFs({ '/var/lib/postgresql/data': 'rw' })
.start();
const host = container.getHost();
const port = container.getMappedPort(5432);
try {
// Leave a mark
process.env.BACKSTAGE_TEST_DATABASE_POSTGRES9_CONNECTION_STRING = `postgresql://user:pass@${host}:${port}`;
const input = await dbs.init('POSTGRES_9');
await input.schema.createTable('a', table => table.string('x').primary());
await input.insert({ x: 'y' }).into('a');
// Look for the mark
const output = knexFactory({
client: 'pg',
connection: {
host,
port,
user: 'user',
password: 'pass',
database: 'backstage_plugin_0',
},
});
await expect(output.select('x').from('a')).resolves.toEqual([{ x: 'y' }]);
} finally {
await container.stop({ timeout: 10_000 });
}
}, 60_000);
it('obeys a provided connection string for mysql 8', async () => {
const dbs = TestDatabases.create();
const container = await new GenericContainer('mysql:8')
.withExposedPorts(3306)
.withEnv('MYSQL_ROOT_PASSWORD', 'pass')
.withTmpFs({ '/var/lib/mysql': 'rw' })
.start();
const host = container.getHost();
const port = container.getMappedPort(3306);
try {
// Leave a mark
process.env.BACKSTAGE_TEST_DATABASE_MYSQL8_CONNECTION_STRING = `mysql://root:pass@${host}:${port}/ignored`;
const input = await dbs.init('MYSQL_8');
await input.schema.createTable('a', table => table.string('x').primary());
await input.insert({ x: 'y' }).into('a');
// Look for the mark
const output = knexFactory({
client: 'mysql2',
connection: {
host,
port,
user: 'root',
password: 'pass',
database: 'backstage_plugin_0',
},
});
await expect(output.select('x').from('a')).resolves.toEqual([{ x: 'y' }]);
} finally {
await container.stop({ timeout: 10_000 });
}
}, 60_000);
});
@@ -1,278 +0,0 @@
/*
* Copyright 2021 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { SingleConnectionDatabaseManager } from '@backstage/backend-common';
import { ConfigReader } from '@backstage/config';
import { Knex } from 'knex';
import { GenericContainer, StartedTestContainer } from 'testcontainers';
import { v4 as uuid } from 'uuid';
type TestDatabaseProperties = {
name: string;
driver: string;
dockerImageName?: string;
connectionStringEnvironmentVariableName?: string;
};
type Instance = {
container?: StartedTestContainer;
databaseManager: SingleConnectionDatabaseManager;
connections: Array<Knex>;
};
const supportedDatabases = Object.freeze({
POSTGRES_13: {
name: 'Postgres 13.x',
driver: 'pg',
dockerImageName: 'postgres:13',
connectionStringEnvironmentVariableName:
'BACKSTAGE_TEST_DATABASE_POSTGRES13_CONNECTION_STRING',
},
POSTGRES_9: {
name: 'Postgres 9.x',
driver: 'pg',
dockerImageName: 'postgres:9',
connectionStringEnvironmentVariableName:
'BACKSTAGE_TEST_DATABASE_POSTGRES9_CONNECTION_STRING',
},
MYSQL_8: {
name: 'MySQL 8.x',
driver: 'mysql2',
dockerImageName: 'mysql:8',
connectionStringEnvironmentVariableName:
'BACKSTAGE_TEST_DATABASE_MYSQL8_CONNECTION_STRING',
},
SQLITE_3: {
name: 'SQLite 3.x',
driver: 'sqlite3',
},
} as const);
/**
* The possible databases to test against.
*/
export type TestDatabaseId =
| 'POSTGRES_13'
| 'POSTGRES_9'
| 'MYSQL_8'
| 'SQLITE_3';
/**
* Encapsulates the creation of ephemeral test database instances for use
* inside unit or integration tests.
*/
export class TestDatabases {
private instanceById: Map<string, Instance> = new Map();
private lastDatabaseId = 0;
/**
* Creates an empty `TestDatabases` instance, and sets up Jest to clean up
* all of its acquired resources after all tests finish.
*
* You typically want to create just a single instance like this at the top
* of your test file or `describe` block, and then call `init` many times on
* that instance inside the individual tests. Spinning up a "physical"
* database instance takes a considerable amount of time, slowing down tests.
* But initializing a new logical database inside that instance using `init`
* is very fast.
*/
static create() {
const databases = new TestDatabases();
afterAll(async () => {
await databases.shutdown();
});
return databases;
}
private constructor() {}
/**
* Returns a fresh, unique, empty logical database on an instance of the
* given database ID platform.
*
* @param id The ID of the database platform to use, e.g. 'POSTGRES_13'
* @returns A `Knex` connection object
*/
async init(id: TestDatabaseId): Promise<Knex> {
const properties = supportedDatabases[id];
if (!properties) {
const candidates = Object.keys(supportedDatabases).join(', ');
throw new Error(
`Unsupported test database ${id}, possible values are ${candidates}`,
);
}
let instance: Instance | undefined = this.instanceById.get(id);
// Ensure that a testcontainers instance is up for this ID
if (!instance) {
instance = await this.initAny(properties);
this.instanceById.set(id, instance);
}
// Ensure that a unique logical database is created in the instance
const connection = await instance.databaseManager
.forPlugin(String(this.lastDatabaseId++))
.getClient();
instance.connections.push(connection);
return connection;
}
private async initAny(properties: TestDatabaseProperties): Promise<Instance> {
// Use the connection string if provided
if (properties.driver === 'pg' || properties.driver === 'mysql2') {
const envVarName = properties.connectionStringEnvironmentVariableName;
if (envVarName) {
const connectionString = process.env[envVarName];
if (connectionString) {
const databaseManager = SingleConnectionDatabaseManager.fromConfig(
new ConfigReader({
backend: {
database: {
client: properties.driver,
connection: connectionString,
},
},
}),
);
return {
databaseManager,
connections: [],
};
}
}
}
// Otherwise start a container for the purpose
switch (properties.driver) {
case 'pg':
return this.initPostgres(properties);
case 'mysql2':
return this.initMysql(properties);
case 'sqlite3':
return this.initSqlite(properties);
default:
throw new Error(`Unknown database driver ${properties.driver}`);
}
}
private async initPostgres(
properties: TestDatabaseProperties,
): Promise<Instance> {
const password = uuid();
const container = await new GenericContainer(properties.dockerImageName!)
.withExposedPorts(5432)
.withEnv('POSTGRES_PASSWORD', password)
.withTmpFs({ '/var/lib/postgresql/data': 'rw' })
.start();
const databaseManager = SingleConnectionDatabaseManager.fromConfig(
new ConfigReader({
backend: {
database: {
client: 'pg',
connection: {
host: container.getHost(),
port: container.getMappedPort(5432),
user: 'postgres',
password,
},
},
},
}),
);
return {
container,
databaseManager,
connections: [],
};
}
private async initMysql(
properties: TestDatabaseProperties,
): Promise<Instance> {
const password = uuid();
const container = await new GenericContainer(properties.dockerImageName!)
.withExposedPorts(3306)
.withEnv('MYSQL_ROOT_PASSWORD', password)
.withTmpFs({ '/var/lib/mysql': 'rw' })
.start();
const databaseManager = SingleConnectionDatabaseManager.fromConfig(
new ConfigReader({
backend: {
database: {
client: 'mysql2',
connection: {
host: container.getHost(),
port: container.getMappedPort(3306),
user: 'root',
password,
},
},
},
}),
);
return {
container,
databaseManager,
connections: [],
};
}
private async initSqlite(
_properties: TestDatabaseProperties,
): Promise<Instance> {
const databaseManager = SingleConnectionDatabaseManager.fromConfig(
new ConfigReader({
backend: {
database: {
client: 'sqlite3',
connection: ':memory:',
},
},
}),
);
return {
databaseManager,
connections: [],
};
}
private async shutdown() {
for (const { container, connections } of this.instanceById.values()) {
try {
await Promise.all(connections.map(c => c.destroy()));
} catch {
// ignore
}
try {
await container?.stop({ timeout: 10_000 });
} catch {
// ignore
}
}
}
}
@@ -1,18 +0,0 @@
/*
* Copyright 2021 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { TestDatabases } from './TestDatabases';
export type { TestDatabaseId } from './TestDatabases';
-17
View File
@@ -1,17 +0,0 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './database';
@@ -1,17 +0,0 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export {};
-1
View File
@@ -63,7 +63,6 @@
"yup": "^0.29.3"
},
"devDependencies": {
"@backstage/backend-testing": "^0.1.0",
"@backstage/cli": "^0.6.10",
"@backstage/test-utils": "^0.1.9",
"@types/core-js": "^2.5.4",
+161 -180
View File
@@ -15,7 +15,7 @@
*/
import { getVoidLogger } from '@backstage/backend-common';
import { TestDatabases } from '@backstage/backend-testing';
import { Knex } from 'knex';
import { DatabaseManager } from './database/DatabaseManager';
import {
DbRefreshStateReferencesRow,
@@ -26,200 +26,181 @@ import { DbSearchRow } from './search';
import { DbFinalEntitiesRow, Stitcher } from './Stitcher';
describe('Stitcher', () => {
const databases = TestDatabases.create();
let db: Knex;
const logger = getVoidLogger();
it.each([
['POSTGRES_13'],
['POSTGRES_9'],
['SQLITE_3'],
// TODO(freben): mysql:8 is not ready to use yet
] as const)(
'runs the happy path for %p',
async databaseId => {
const db = await databases.init(databaseId);
await DatabaseManager.createDatabase(db);
beforeEach(async () => {
db = await DatabaseManager.createTestDatabaseConnection();
await DatabaseManager.createDatabase(db);
});
const stitcher = new Stitcher(db, logger);
it('runs the happy path', async () => {
const stitcher = new Stitcher(db, logger);
await db.transaction(async tx => {
await tx<DbRefreshStateRow>('refresh_state').insert([
await db.transaction(async tx => {
await tx<DbRefreshStateRow>('refresh_state').insert([
{
entity_id: 'my-id',
entity_ref: 'k:ns/n',
unprocessed_entity: JSON.stringify({}),
processed_entity: JSON.stringify({
apiVersion: 'a',
kind: 'k',
metadata: {
name: 'n',
namespace: 'ns',
},
spec: {
k: 'v',
},
}),
errors: '[]',
next_update_at: tx.fn.now(),
last_discovery_at: tx.fn.now(),
},
]);
await tx<DbRefreshStateReferencesRow>('refresh_state_references').insert([
{ source_key: 'a', target_entity_ref: 'k:ns/n' },
]);
await tx<DbRelationsRow>('relations').insert([
{
originating_entity_id: 'my-id',
source_entity_ref: 'k:ns/n',
type: 'looksAt',
target_entity_ref: 'k:ns/other',
},
]);
});
await stitcher.stitch(new Set(['k:ns/n']));
let firstHash: string;
await db.transaction(async tx => {
const entities = await tx<DbFinalEntitiesRow>('final_entities');
expect(entities.length).toBe(1);
const entity = JSON.parse(entities[0].final_entity);
expect(entity).toEqual({
relations: [
{
entity_id: 'my-id',
entity_ref: 'k:ns/n',
unprocessed_entity: JSON.stringify({}),
processed_entity: JSON.stringify({
apiVersion: 'a',
type: 'looksAt',
target: {
kind: 'k',
metadata: {
name: 'n',
namespace: 'ns',
},
spec: {
k: 'v',
},
}),
errors: '[]',
next_update_at: tx.fn.now(),
last_discovery_at: tx.fn.now(),
namespace: 'ns',
name: 'other',
},
},
]);
await tx<DbRefreshStateReferencesRow>(
'refresh_state_references',
).insert([{ source_key: 'a', target_entity_ref: 'k:ns/n' }]);
await tx<DbRelationsRow>('relations').insert([
],
apiVersion: 'a',
kind: 'k',
metadata: {
name: 'n',
namespace: 'ns',
etag: expect.any(String),
generation: 1,
uid: 'my-id',
},
spec: {
k: 'v',
},
});
expect(entity.metadata.etag).toEqual(entities[0].hash);
firstHash = entities[0].hash;
const search = await tx<DbSearchRow>('search');
expect(search).toEqual(
expect.arrayContaining([
{ entity_id: 'my-id', key: 'relations.looksat', value: 'k:ns/other' },
{ entity_id: 'my-id', key: 'apiversion', value: 'a' },
{ entity_id: 'my-id', key: 'kind', value: 'k' },
{ entity_id: 'my-id', key: 'metadata.name', value: 'n' },
{ entity_id: 'my-id', key: 'metadata.namespace', value: 'ns' },
{ entity_id: 'my-id', key: 'metadata.uid', value: 'my-id' },
{ entity_id: 'my-id', key: 'spec.k', value: 'v' },
]),
);
});
// Re-stitch without any changes
await stitcher.stitch(new Set(['k:ns/n']));
await db.transaction(async tx => {
const entities = await tx<DbFinalEntitiesRow>('final_entities');
expect(entities.length).toBe(1);
const entity = JSON.parse(entities[0].final_entity);
expect(entities[0].hash).toEqual(firstHash);
expect(entity.metadata.etag).toEqual(firstHash);
});
// Now add one more relation and re-stitch
await db.transaction(async tx => {
await tx<DbRelationsRow>('relations').insert([
{
originating_entity_id: 'my-id',
source_entity_ref: 'k:ns/n',
type: 'looksAt',
target_entity_ref: 'k:ns/third',
},
]);
});
await stitcher.stitch(new Set(['k:ns/n']));
await db.transaction(async tx => {
const entities = await tx<DbFinalEntitiesRow>('final_entities');
expect(entities.length).toBe(1);
const entity = JSON.parse(entities[0].final_entity);
expect(entity).toEqual({
relations: expect.arrayContaining([
{
originating_entity_id: 'my-id',
source_entity_ref: 'k:ns/n',
type: 'looksAt',
target_entity_ref: 'k:ns/other',
},
]);
});
await stitcher.stitch(new Set(['k:ns/n']));
let firstHash: string;
await db.transaction(async tx => {
const entities = await tx<DbFinalEntitiesRow>('final_entities');
expect(entities.length).toBe(1);
const entity = JSON.parse(entities[0].final_entity);
expect(entity).toEqual({
relations: [
{
type: 'looksAt',
target: {
kind: 'k',
namespace: 'ns',
name: 'other',
},
target: {
kind: 'k',
namespace: 'ns',
name: 'other',
},
],
apiVersion: 'a',
kind: 'k',
metadata: {
name: 'n',
namespace: 'ns',
etag: expect.any(String),
generation: 1,
uid: 'my-id',
},
spec: {
k: 'v',
},
});
expect(entity.metadata.etag).toEqual(entities[0].hash);
firstHash = entities[0].hash;
const search = await tx<DbSearchRow>('search');
expect(search).toEqual(
expect.arrayContaining([
{
entity_id: 'my-id',
key: 'relations.looksat',
value: 'k:ns/other',
},
{ entity_id: 'my-id', key: 'apiversion', value: 'a' },
{ entity_id: 'my-id', key: 'kind', value: 'k' },
{ entity_id: 'my-id', key: 'metadata.name', value: 'n' },
{ entity_id: 'my-id', key: 'metadata.namespace', value: 'ns' },
{ entity_id: 'my-id', key: 'metadata.uid', value: 'my-id' },
{ entity_id: 'my-id', key: 'spec.k', value: 'v' },
]),
);
});
// Re-stitch without any changes
await stitcher.stitch(new Set(['k:ns/n']));
await db.transaction(async tx => {
const entities = await tx<DbFinalEntitiesRow>('final_entities');
expect(entities.length).toBe(1);
const entity = JSON.parse(entities[0].final_entity);
expect(entities[0].hash).toEqual(firstHash);
expect(entity.metadata.etag).toEqual(firstHash);
});
// Now add one more relation and re-stitch
await db.transaction(async tx => {
await tx<DbRelationsRow>('relations').insert([
{
originating_entity_id: 'my-id',
source_entity_ref: 'k:ns/n',
type: 'looksAt',
target_entity_ref: 'k:ns/third',
target: {
kind: 'k',
namespace: 'ns',
name: 'third',
},
},
]);
]),
apiVersion: 'a',
kind: 'k',
metadata: {
name: 'n',
namespace: 'ns',
etag: expect.any(String),
generation: 1,
uid: 'my-id',
},
spec: {
k: 'v',
},
});
await stitcher.stitch(new Set(['k:ns/n']));
expect(entities[0].hash).not.toEqual(firstHash);
expect(entities[0].hash).toEqual(entity.metadata.etag);
await db.transaction(async tx => {
const entities = await tx<DbFinalEntitiesRow>('final_entities');
expect(entities.length).toBe(1);
const entity = JSON.parse(entities[0].final_entity);
expect(entity).toEqual({
relations: expect.arrayContaining([
{
type: 'looksAt',
target: {
kind: 'k',
namespace: 'ns',
name: 'other',
},
},
{
type: 'looksAt',
target: {
kind: 'k',
namespace: 'ns',
name: 'third',
},
},
]),
apiVersion: 'a',
kind: 'k',
metadata: {
name: 'n',
namespace: 'ns',
etag: expect.any(String),
generation: 1,
uid: 'my-id',
},
spec: {
k: 'v',
},
});
expect(entities[0].hash).not.toEqual(firstHash);
expect(entities[0].hash).toEqual(entity.metadata.etag);
const search = await tx<DbSearchRow>('search');
expect(search).toEqual(
expect.arrayContaining([
{
entity_id: 'my-id',
key: 'relations.looksat',
value: 'k:ns/other',
},
{
entity_id: 'my-id',
key: 'relations.looksat',
value: 'k:ns/third',
},
{ entity_id: 'my-id', key: 'apiversion', value: 'a' },
{ entity_id: 'my-id', key: 'kind', value: 'k' },
{ entity_id: 'my-id', key: 'metadata.name', value: 'n' },
{ entity_id: 'my-id', key: 'metadata.namespace', value: 'ns' },
{ entity_id: 'my-id', key: 'metadata.uid', value: 'my-id' },
{ entity_id: 'my-id', key: 'spec.k', value: 'v' },
]),
);
});
},
30000,
);
const search = await tx<DbSearchRow>('search');
expect(search).toEqual(
expect.arrayContaining([
{ entity_id: 'my-id', key: 'relations.looksat', value: 'k:ns/other' },
{ entity_id: 'my-id', key: 'relations.looksat', value: 'k:ns/third' },
{ entity_id: 'my-id', key: 'apiversion', value: 'a' },
{ entity_id: 'my-id', key: 'kind', value: 'k' },
{ entity_id: 'my-id', key: 'metadata.name', value: 'n' },
{ entity_id: 'my-id', key: 'metadata.namespace', value: 'ns' },
{ entity_id: 'my-id', key: 'metadata.uid', value: 'my-id' },
{ entity_id: 'my-id', key: 'spec.k', value: 'v' },
]),
);
});
});
});
-1
View File
@@ -62,7 +62,6 @@ PackageJsonLookup.prototype.tryGetPackageJsonFilePathFor = function tryGetPackag
const DOCUMENTED_PACKAGES = [
'packages/backend-common',
'packages/backend-testing',
'packages/catalog-client',
'packages/catalog-model',
'packages/cli-common',
+4 -103
View File
@@ -6763,21 +6763,6 @@
resolved "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9"
integrity sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==
"@types/ssh2-streams@*":
version "0.1.8"
resolved "https://registry.npmjs.org/@types/ssh2-streams/-/ssh2-streams-0.1.8.tgz#142af404dae059931aea7fcd1511b5478964feb6"
integrity sha512-I7gixRPUvVIyJuCEvnmhr3KvA2dC0639kKswqD4H5b4/FOcnPtNU+qWLiXdKIqqX9twUvi5j0U1mwKE5CUsrfA==
dependencies:
"@types/node" "*"
"@types/ssh2@^0.5.45":
version "0.5.46"
resolved "https://registry.npmjs.org/@types/ssh2/-/ssh2-0.5.46.tgz#e12341a242aea0e98ac2dec89e039bf421fd3584"
integrity sha512-1pC8FHrMPYdkLoUOwTYYifnSEPzAFZRsp3JFC/vokQ+dRrVI+hDBwz0SNmQ3pL6h39OSZlPs0uCG7wKJkftnaA==
dependencies:
"@types/node" "*"
"@types/ssh2-streams" "*"
"@types/stack-utils@^1.0.1":
version "1.0.1"
resolved "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e"
@@ -7621,7 +7606,7 @@ any-observable@^0.3.0:
resolved "https://registry.npmjs.org/any-observable/-/any-observable-0.3.0.tgz#af933475e5806a67d0d7df090dd5e8bef65d119b"
integrity sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog==
any-promise@^1.0.0, any-promise@^1.1.0:
any-promise@^1.0.0:
version "1.3.0"
resolved "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
integrity sha1-q8av7tzqUugJzcA3au0845Y10X8=
@@ -7898,19 +7883,6 @@ archiver@^5.0.2:
tar-stream "^2.1.4"
zip-stream "^4.0.4"
archiver@^5.2.0:
version "5.3.0"
resolved "https://registry.npmjs.org/archiver/-/archiver-5.3.0.tgz#dd3e097624481741df626267564f7dd8640a45ba"
integrity sha512-iUw+oDwK0fgNpvveEsdQ0Ase6IIKztBJU2U0E9MzszMfmVVUyv1QJhS2ITW9ZCqx8dktAxVAjWWkKehuZE8OPg==
dependencies:
archiver-utils "^2.1.0"
async "^3.2.0"
buffer-crc32 "^0.2.1"
readable-stream "^3.6.0"
readdir-glob "^1.0.0"
tar-stream "^2.2.0"
zip-stream "^4.1.0"
are-we-there-yet@~1.1.2:
version "1.1.5"
resolved "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21"
@@ -10146,16 +10118,6 @@ compress-commons@^4.0.2:
normalize-path "^3.0.0"
readable-stream "^3.6.0"
compress-commons@^4.1.0:
version "4.1.0"
resolved "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.0.tgz#25ec7a4528852ccd1d441a7d4353cd0ece11371b"
integrity sha512-ofaaLqfraD1YRTkrRKPCrGJ1pFeDG/MVCkVVV2FNGeWquSlqw5wOrwOfPQ1xF2u+blpeWASie5EubHz+vsNIgA==
dependencies:
buffer-crc32 "^0.2.13"
crc32-stream "^4.0.1"
normalize-path "^3.0.0"
readable-stream "^3.6.0"
compressible@^2.0.12, compressible@~2.0.16:
version "2.0.18"
resolved "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba"
@@ -11720,13 +11682,6 @@ dns-txt@^2.0.2:
dependencies:
buffer-indexof "^1.0.0"
docker-compose@^0.23.5:
version "0.23.9"
resolved "https://registry.npmjs.org/docker-compose/-/docker-compose-0.23.9.tgz#d24343397792562f72a53eb895e76f5c7c751ee8"
integrity sha512-ucDcqBoavyQMuxPOpc8y0LqtALc5Y7h/Ga/FEiZ8m+ZqaVf3WuCh5HODtqvGO8HZMGrcH7HPyBqJ+gRJG+dBvg==
dependencies:
yaml "^1.10.2"
docker-modem@^2.1.0:
version "2.1.3"
resolved "https://registry.npmjs.org/docker-modem/-/docker-modem-2.1.3.tgz#15432225f63db02eb5de4bb9a621b7293e5f264d"
@@ -24258,14 +24213,6 @@ sqlstring@^2.3.2:
resolved "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.2.tgz#cdae7169389a1375b18e885f2e60b3e460809514"
integrity sha512-vF4ZbYdKS8OnoJAWBmMxCQDkiEBkGQYU7UZPtL8flbDRSNkhaXvRJ279ZtI6M+zDaQovVU4tuRgzK5fVhvFAhg==
ssh-remote-port-forward@^1.0.3:
version "1.0.3"
resolved "https://registry.npmjs.org/ssh-remote-port-forward/-/ssh-remote-port-forward-1.0.3.tgz#074cebed6d54a42ce4659d6aa24987e433ff5fef"
integrity sha512-PJ6qGFmB6n0iMCQIp+qzx8qVUE5cgacopvEh63t3NJjEQHOaza/JT9zywmmVlaol/eGtCmpvBXx2A03ih1Y+xg==
dependencies:
"@types/ssh2" "^0.5.45"
ssh2 "^0.8.9"
ssh2-streams@~0.4.10:
version "0.4.10"
resolved "https://registry.npmjs.org/ssh2-streams/-/ssh2-streams-0.4.10.tgz#48ef7e8a0e39d8f2921c30521d56dacb31d23a34"
@@ -24275,7 +24222,7 @@ ssh2-streams@~0.4.10:
bcrypt-pbkdf "^1.0.2"
streamsearch "~0.1.2"
ssh2@^0.8.7, ssh2@^0.8.9:
ssh2@^0.8.7:
version "0.8.9"
resolved "https://registry.npmjs.org/ssh2/-/ssh2-0.8.9.tgz#54da3a6c4ba3daf0d8477a538a481326091815f3"
integrity sha512-GmoNPxWDMkVpMFa9LVVzQZHF6EW3WKmBwL+4/GeILf2hFmix5Isxm7Amamo8o7bHiU0tC+wXsGcUXOxp8ChPaw==
@@ -24479,13 +24426,6 @@ stream-shift@^1.0.0:
resolved "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d"
integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==
stream-to-array@^2.3.0:
version "2.3.0"
resolved "https://registry.npmjs.org/stream-to-array/-/stream-to-array-2.3.0.tgz#bbf6b39f5f43ec30bc71babcb37557acecf34353"
integrity sha1-u/azn19D7DC8cbq8s3VXrOzzQ1M=
dependencies:
any-promise "^1.1.0"
stream-transform@^2.0.1:
version "2.0.2"
resolved "https://registry.npmjs.org/stream-transform/-/stream-transform-2.0.2.tgz#3cb7a14c802eb39bc40caaab0535e584f3a65caf"
@@ -25040,7 +24980,7 @@ tapable@^1.0.0, tapable@^1.1.3:
resolved "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2"
integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==
tar-fs@^2.0.0, tar-fs@^2.1.1:
tar-fs@^2.0.0:
version "2.1.1"
resolved "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784"
integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==
@@ -25071,17 +25011,6 @@ tar-stream@^2.0.0, tar-stream@^2.1.4:
inherits "^2.0.3"
readable-stream "^3.1.1"
tar-stream@^2.2.0:
version "2.2.0"
resolved "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287"
integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==
dependencies:
bl "^4.0.3"
end-of-stream "^1.4.1"
fs-constants "^1.0.0"
inherits "^2.0.3"
readable-stream "^3.1.1"
tar@^2.0.0:
version "2.2.2"
resolved "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz#0ca8848562c7299b8b446ff6a4d60cdbb23edc40"
@@ -25239,25 +25168,6 @@ test-exclude@^6.0.0:
glob "^7.1.4"
minimatch "^3.0.4"
testcontainers@^7.10.0:
version "7.10.0"
resolved "https://registry.npmjs.org/testcontainers/-/testcontainers-7.10.0.tgz#d0306ac9e54c24006e0de0e4505ca7d4a5a0db9e"
integrity sha512-xwz1dzRUmaYTNL4qH8//YlJzSGfVmooGtJNc0ynFm0mtUTyYu4HJLpPZB9e7+/ZiQlWp73LSTH/gSwuNtl8FXw==
dependencies:
"@types/archiver" "^5.1.0"
"@types/dockerode" "^3.2.1"
archiver "^5.2.0"
byline "^5.0.0"
debug "^4.3.1"
docker-compose "^0.23.5"
dockerode "^3.2.1"
get-port "^5.1.1"
glob "^7.1.6"
slash "^3.0.0"
ssh-remote-port-forward "^1.0.3"
stream-to-array "^2.3.0"
tar-fs "^2.1.1"
text-encoding-utf-8@^1.0.1:
version "1.0.2"
resolved "https://registry.npmjs.org/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz#585b62197b0ae437e3c7b5d0af27ac1021e10d13"
@@ -27212,7 +27122,7 @@ yaml-ast-parser@0.0.43, yaml-ast-parser@^0.0.43:
resolved "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz#e8a23e6fb4c38076ab92995c5dca33f3d3d7c9bb"
integrity sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==
yaml@^1.10.0, yaml@^1.10.2, yaml@^1.7.2, yaml@^1.9.2:
yaml@^1.10.0, yaml@^1.7.2, yaml@^1.9.2:
version "1.10.2"
resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
@@ -27394,15 +27304,6 @@ zip-stream@^4.0.4:
compress-commons "^4.0.2"
readable-stream "^3.6.0"
zip-stream@^4.1.0:
version "4.1.0"
resolved "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.0.tgz#51dd326571544e36aa3f756430b313576dc8fc79"
integrity sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A==
dependencies:
archiver-utils "^2.1.0"
compress-commons "^4.1.0"
readable-stream "^3.6.0"
zombie@^6.1.4:
version "6.1.4"
resolved "https://registry.npmjs.org/zombie/-/zombie-6.1.4.tgz#9f0f53f3d9a032beb7f3fe5b382146a3475a4d47"