chore(tests): Added some more tests for DefaultLocationStore and fixing migration so it will run properly on PG

Signed-off-by: blam <ben@blam.sh>
This commit is contained in:
blam
2021-04-27 21:23:35 +02:00
parent 5c60c66ff5
commit d798708943
3 changed files with 161 additions and 13 deletions
@@ -160,7 +160,7 @@ exports.up = async function up(knex) {
'Flattened key-values from the entities, used for quick filtering',
);
table
.uuid('entity_id')
.text('entity_id')
.references('entity_id')
.inTable('refresh_state')
.onDelete('CASCADE')
@@ -0,0 +1,146 @@
/*
* 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 { DatabaseManager } from './database/DatabaseManager';
import { DefaultLocationStore } from './DefaultLocationStore';
import { v4 } from 'uuid';
describe('Default Location Store', () => {
const createLocationStore = async () => {
const db = await DatabaseManager.createTestDatabase();
const connection = { applyMutation: jest.fn() };
const store = new DefaultLocationStore(db);
await store.connect(connection);
return { store, connection };
};
it('should do a full sync with the locations on connect', async () => {
const { connection } = await createLocationStore();
expect(connection.applyMutation).toHaveBeenCalledWith({
type: 'full',
entities: [],
});
});
describe('listLocations', () => {
it('lists empty locations when there is no locations', async () => {
const { store } = await createLocationStore();
expect(await store.listLocations()).toEqual([]);
});
it('lists locations that are added to the db', async () => {
const { store } = await createLocationStore();
await store.createLocation({
target:
'https://github.com/backstage/demo/blob/master/catalog-info.yml',
type: 'url',
});
const listLocations = await store.listLocations();
expect(listLocations).toHaveLength(1);
expect(listLocations).toEqual(
expect.arrayContaining([
expect.objectContaining({
target:
'https://github.com/backstage/demo/blob/master/catalog-info.yml',
type: 'url',
}),
]),
);
});
});
describe('createLocation', () => {
it('throws when the location already exists', async () => {
const { store } = await createLocationStore();
const spec = {
target:
'https://github.com/backstage/demo/blob/master/catalog-info.yml',
type: 'url',
};
await store.createLocation(spec);
await expect(() => store.createLocation(spec)).rejects.toThrow(
new RegExp(`Location ${spec.type}:${spec.target} already exists`),
);
});
it('calls apply mutation when adding a new location', async () => {
const { store, connection } = await createLocationStore();
await store.createLocation({
target:
'https://github.com/backstage/demo/blob/master/catalog-info.yml',
type: 'url',
});
expect(connection.applyMutation).toHaveBeenCalledWith({
type: 'delta',
removed: [],
added: expect.arrayContaining([
expect.objectContaining({
spec: {
target:
'https://github.com/backstage/demo/blob/master/catalog-info.yml',
type: 'url',
},
}),
]),
});
});
});
describe('deleteLocation', () => {
it('throws if the location does not exist', async () => {
const { store } = await createLocationStore();
const id = v4();
await expect(() => store.deleteLocation(id)).rejects.toThrow(
new RegExp(`Found no location with ID ${id}`),
);
});
it('calls apply mutation when adding a new location', async () => {
const { store, connection } = await createLocationStore();
const location = await store.createLocation({
target:
'https://github.com/backstage/demo/blob/master/catalog-info.yml',
type: 'url',
});
await store.deleteLocation(location.id);
expect(connection.applyMutation).toHaveBeenCalledWith({
type: 'delta',
added: [],
removed: expect.arrayContaining([
expect.objectContaining({
spec: {
target:
'https://github.com/backstage/demo/blob/master/catalog-info.yml',
type: 'url',
},
}),
]),
});
});
});
});
@@ -34,10 +34,8 @@ export class DefaultLocationStore implements LocationStore, EntityProvider {
return 'DefaultLocationStore';
}
createLocation(spec: LocationSpec): Promise<Location> {
async createLocation(spec: LocationSpec): Promise<Location> {
return this.db.transaction(async tx => {
// TODO: id should really be type and target combined and not a uuid.
// Attempt to find a previous location matching the spec
const previousLocations = await this.listLocations();
const previousLocation = previousLocations.some(
@@ -50,6 +48,7 @@ export class DefaultLocationStore implements LocationStore, EntityProvider {
);
}
// TODO: id should really be type and target combined and not a uuid.
const location = await this.db.addLocation(tx, {
id: uuidv4(),
type: spec.type,
@@ -68,11 +67,17 @@ export class DefaultLocationStore implements LocationStore, EntityProvider {
async listLocations(): Promise<Location[]> {
const dbLocations = await this.db.locations();
return dbLocations.map(item => ({
id: item.id,
target: item.target,
type: item.type,
}));
return (
dbLocations
// TODO(blam): We should create a mutation to remove this location for everyone
// eventually when it's all done and dusted
.filter(({ type }) => type !== 'bootstrap')
.map(item => ({
id: item.id,
target: item.target,
type: item.type,
}))
);
}
getLocation(id: string): Promise<Location> {
@@ -86,9 +91,6 @@ export class DefaultLocationStore implements LocationStore, EntityProvider {
return this.db.transaction(async tx => {
const location = await this.db.location(id);
if (!location) {
throw new ConflictError(`No location found with id: ${id}`);
}
await this.db.removeLocation(tx, id);
await this.connection.applyMutation({
type: 'delta',
@@ -108,7 +110,7 @@ export class DefaultLocationStore implements LocationStore, EntityProvider {
async connect(connection: EntityProviderConnection): Promise<void> {
this._connection = connection;
const locations = await this.db.locations();
const locations = await this.listLocations();
const entities = locations.map(location => {
return locationSpecToLocationEntity(location);
});