fix: preserve seeded location identities

This commit is contained in:
Bastian Wagner
2026-08-18 19:40:13 +02:00
parent 067134b684
commit c35859a4de
2 changed files with 159 additions and 57 deletions

View File

@@ -1,4 +1,7 @@
import { DataSource } from 'typeorm'; import { DataSource } from 'typeorm';
import { Character } from '../../characters/entities/character.entity';
import { LocationConnection } from '../../world/entities/location-connection.entity';
import { LocationDefinition } from '../../world/entities/location-definition.entity';
import { seedVisibleVerticalSlice } from './vertical-slice.seed'; import { seedVisibleVerticalSlice } from './vertical-slice.seed';
type Row = Record<string, unknown>; type Row = Record<string, unknown>;
@@ -39,6 +42,41 @@ class InMemoryRepository {
readonly insert = jest.fn(async (value: Row) => { readonly insert = jest.fn(async (value: Row) => {
this.rows.push({ ...value }); this.rows.push({ ...value });
}); });
readonly update = jest.fn(async (criteria: string | Row, value: Row) => {
const row = this.rows.find((candidate) =>
typeof criteria === 'string'
? candidate.id === criteria
: Object.entries(criteria).every(
([key, expected]) => candidate[key] === expected,
),
);
if (row) {
Object.assign(row, value);
}
});
}
function createDataSource(
locationRepository: InMemoryRepository,
connectionRepository: InMemoryRepository,
characterRepository: InMemoryRepository,
): DataSource {
return {
getRepository: jest.fn((entity: unknown) => {
if (entity === LocationDefinition) {
return locationRepository;
}
if (entity === LocationConnection) {
return connectionRepository;
}
if (entity === Character) {
return characterRepository;
}
throw new Error('Unexpected repository');
}),
} as unknown as DataSource;
} }
describe('seedVisibleVerticalSlice', () => { describe('seedVisibleVerticalSlice', () => {
@@ -46,27 +84,20 @@ describe('seedVisibleVerticalSlice', () => {
const locationRepository = new InMemoryRepository(); const locationRepository = new InMemoryRepository();
const connectionRepository = new InMemoryRepository(); const connectionRepository = new InMemoryRepository();
const characterRepository = new InMemoryRepository(); const characterRepository = new InMemoryRepository();
const dataSource = { const dataSource = createDataSource(
getRepository: jest locationRepository,
.fn() connectionRepository,
.mockReturnValueOnce(locationRepository) characterRepository,
.mockReturnValueOnce(connectionRepository)
.mockReturnValueOnce(characterRepository)
.mockReturnValueOnce(locationRepository)
.mockReturnValueOnce(connectionRepository)
.mockReturnValueOnce(characterRepository),
} as unknown as DataSource;
await seedVisibleVerticalSlice(dataSource);
await seedVisibleVerticalSlice(dataSource);
expect(locationRepository.upsert).toHaveBeenCalledWith(
expect.arrayContaining([
expect.objectContaining({ id: SOUTH_GATE_ID, key: 'south-gate' }),
expect.objectContaining({ id: BURNED_ROAD_ID, key: 'burned-road' }),
]),
['key'],
); );
await seedVisibleVerticalSlice(dataSource);
Object.assign(characterRepository.rows[0], {
currentLocationId: BURNED_ROAD_ID,
currentHp: 57,
experience: 39,
});
await seedVisibleVerticalSlice(dataSource);
expect(connectionRepository.upsert).toHaveBeenCalledWith( expect(connectionRepository.upsert).toHaveBeenCalledWith(
expect.arrayContaining([ expect.arrayContaining([
expect.objectContaining({ expect.objectContaining({
@@ -86,5 +117,57 @@ describe('seedVisibleVerticalSlice', () => {
expect(locationRepository.rows).toHaveLength(2); expect(locationRepository.rows).toHaveLength(2);
expect(connectionRepository.rows).toHaveLength(2); expect(connectionRepository.rows).toHaveLength(2);
expect(characterRepository.rows).toHaveLength(1); expect(characterRepository.rows).toHaveLength(1);
expect(characterRepository.rows[0]).toEqual(
expect.objectContaining({
currentLocationId: BURNED_ROAD_ID,
currentHp: 57,
experience: 39,
}),
);
});
it('preserves existing location IDs and uses them for the directed connections', async () => {
const locationRepository = new InMemoryRepository();
const connectionRepository = new InMemoryRepository();
const characterRepository = new InMemoryRepository();
const persistedSouthGateId = '30000000-0000-4000-8000-000000000001';
locationRepository.rows.push({
id: persistedSouthGateId,
key: 'south-gate',
name: 'Veraltetes Südtor',
});
const dataSource = createDataSource(
locationRepository,
connectionRepository,
characterRepository,
);
await seedVisibleVerticalSlice(dataSource);
expect(locationRepository.rows).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: persistedSouthGateId,
key: 'south-gate',
name: 'Südtor von Graufurt',
}),
expect.objectContaining({
id: BURNED_ROAD_ID,
key: 'burned-road',
}),
]),
);
expect(connectionRepository.rows).toEqual(
expect.arrayContaining([
expect.objectContaining({
fromLocationId: persistedSouthGateId,
toLocationId: BURNED_ROAD_ID,
}),
expect.objectContaining({
fromLocationId: BURNED_ROAD_ID,
toLocationId: persistedSouthGateId,
}),
]),
);
}); });
}); });

View File

@@ -12,8 +12,7 @@ export async function seedVisibleVerticalSlice(
const connectionRepository = dataSource.getRepository(LocationConnection); const connectionRepository = dataSource.getRepository(LocationConnection);
const characterRepository = dataSource.getRepository(Character); const characterRepository = dataSource.getRepository(Character);
await locationRepository.upsert( const locations = [
[
{ {
id: SOUTH_GATE_ID, id: SOUTH_GATE_ID,
key: 'south-gate', key: 'south-gate',
@@ -42,22 +41,42 @@ export async function seedVisibleVerticalSlice(
huntingEnabled: true, huntingEnabled: true,
artworkPath: '/assets/locations/burned-road.webp', artworkPath: '/assets/locations/burned-road.webp',
}, },
], ];
['key'], let southGateId = SOUTH_GATE_ID;
); let burnedRoadId = BURNED_ROAD_ID;
for (const location of locations) {
const existing = await locationRepository.findOneBy({
key: location.key,
});
const { id, key, ...definition } = location;
const persistedId = existing?.id ?? id;
if (existing) {
await locationRepository.update(existing.id, definition);
} else {
await locationRepository.insert(location);
}
if (key === 'south-gate') {
southGateId = persistedId;
} else {
burnedRoadId = persistedId;
}
}
await connectionRepository.upsert( await connectionRepository.upsert(
[ [
{ {
fromLocationId: SOUTH_GATE_ID, fromLocationId: southGateId,
toLocationId: BURNED_ROAD_ID, toLocationId: burnedRoadId,
travelDurationSeconds: 10, travelDurationSeconds: 10,
ambushChance: '0.0500', ambushChance: '0.0500',
enabled: true, enabled: true,
}, },
{ {
fromLocationId: BURNED_ROAD_ID, fromLocationId: burnedRoadId,
toLocationId: SOUTH_GATE_ID, toLocationId: southGateId,
travelDurationSeconds: 10, travelDurationSeconds: 10,
ambushChance: '0.0500', ambushChance: '0.0500',
enabled: true, enabled: true,