fix(reference): select verified station deterministically
This commit is contained in:
@@ -202,6 +202,7 @@ export function createReferenceSchema(db: Database): void {
|
|||||||
CREATE INDEX systems_name_idx ON systems(name);
|
CREATE INDEX systems_name_idx ON systems(name);
|
||||||
CREATE INDEX gates_destination_idx ON gates(destination_system_id);
|
CREATE INDEX gates_destination_idx ON gates(destination_system_id);
|
||||||
CREATE INDEX stations_system_idx ON stations(system_id);
|
CREATE INDEX stations_system_idx ON stations(system_id);
|
||||||
|
CREATE INDEX stations_source_verified_idx ON stations(source_verified, station_id);
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,7 +210,7 @@ export function createReferenceSchema(db: Database): void {
|
|||||||
export function assertReferenceReaderSchema(db: Database): void {
|
export function assertReferenceReaderSchema(db: Database): void {
|
||||||
const version = Number((db.query<{ user_version: number }, []>('PRAGMA user_version').get() as { user_version: number } | null)?.user_version ?? 0);
|
const version = Number((db.query<{ user_version: number }, []>('PRAGMA user_version').get() as { user_version: number } | null)?.user_version ?? 0);
|
||||||
const row = db.query<{ present: number }, []>("SELECT COUNT(*) AS present FROM sqlite_master WHERE type='table' AND name IN ('types','type_search','type_search_fts','categories','groups','regions','constellations','systems','stations')").get();
|
const row = db.query<{ present: number }, []>("SELECT COUNT(*) AS present FROM sqlite_master WHERE type='table' AND name IN ('types','type_search','type_search_fts','categories','groups','regions','constellations','systems','stations')").get();
|
||||||
const index = db.query<{ present: number }, []>("SELECT COUNT(*) AS present FROM sqlite_master WHERE type='index' AND name='type_search_normalized_name_idx'").get();
|
const indexes = db.query<{ present: number }, []>("SELECT COUNT(*) AS present FROM sqlite_master WHERE type='index' AND name IN ('type_search_normalized_name_idx','stations_source_verified_idx')").get();
|
||||||
const requiredColumns: Record<string, string[]> = {
|
const requiredColumns: Record<string, string[]> = {
|
||||||
types: ['type_id', 'group_id', 'category_id', 'name_json', 'published', 'volume'],
|
types: ['type_id', 'group_id', 'category_id', 'name_json', 'published', 'volume'],
|
||||||
type_search: ['type_id', 'language', 'name', 'normalized_name'],
|
type_search: ['type_id', 'language', 'name', 'normalized_name'],
|
||||||
@@ -225,7 +226,7 @@ export function assertReferenceReaderSchema(db: Database): void {
|
|||||||
const present = new Set(db.query<{ name: string }, []>(`PRAGMA table_info(${table})`).all().map((column) => column.name));
|
const present = new Set(db.query<{ name: string }, []>(`PRAGMA table_info(${table})`).all().map((column) => column.name));
|
||||||
return columns.every((column) => present.has(column));
|
return columns.every((column) => present.has(column));
|
||||||
});
|
});
|
||||||
if (version !== SCHEMA_VERSION || !row || Number(row.present) !== 9 || !index || Number(index.present) !== 1 || !columnsValid) throw new Error(`active reference database has an incompatible schema version (expected ${SCHEMA_VERSION})`);
|
if (version !== SCHEMA_VERSION || !row || Number(row.present) !== 9 || !indexes || Number(indexes.present) !== 2 || !columnsValid) throw new Error(`active reference database has an incompatible schema version (expected ${SCHEMA_VERSION})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool = (value: boolean | undefined): number | null => value === undefined ? null : value ? 1 : 0;
|
const bool = (value: boolean | undefined): number | null => value === undefined ? null : value ? 1 : 0;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { expect, test } from 'bun:test';
|
import { expect, test } from 'bun:test';
|
||||||
import { cp, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
|
import { cp, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
|
||||||
|
import { Database } from 'bun:sqlite';
|
||||||
import { join } from 'node:path';
|
import { join } from 'node:path';
|
||||||
import { tmpdir } from 'node:os';
|
import { tmpdir } from 'node:os';
|
||||||
import { ReferenceCompiler, SqliteReferenceRepository, readReferenceManifest } from '../src/lib/server/reference';
|
import { ReferenceCompiler, SqliteReferenceRepository, readReferenceManifest } from '../src/lib/server/reference';
|
||||||
@@ -15,8 +16,8 @@ test('reference readers search normalized types and expose group/category/system
|
|||||||
const universe = JSON.parse(await readFile(universePath, 'utf8')) as Record<string, unknown>;
|
const universe = JSON.parse(await readFile(universePath, 'utf8')) as Record<string, unknown>;
|
||||||
universe.regions = { '10000001': { region_id: 10000001, name: 'The Forge' } };
|
universe.regions = { '10000001': { region_id: 10000001, name: 'The Forge' } };
|
||||||
universe.constellations = { '20000020': { constellation_id: 20000020, name: 'Kimotoro', region_id: 10000001 } };
|
universe.constellations = { '20000020': { constellation_id: 20000020, name: 'Kimotoro', region_id: 10000001 } };
|
||||||
universe.stations = { '60003760': { station_id: 60003760, system_id: 30000142, name: 'Jita IV - Moon 4 - Caldari Navy Assembly Plant', type_id: 587, owner_id: 1000035, position: { x: 1, y: 2, z: 3 } } };
|
universe.stations = { '60003761': { station_id: 60003761, system_id: 30000142, name: 'Jita IV - Moon 4 - Caldari Navy Assembly Plant', type_id: 587, owner_id: 1000035, position: { x: 1, y: 2, z: 3 } } };
|
||||||
(universe.systems as Record<string, Record<string, unknown>>)['30000142'].stations = [60003761];
|
(universe.systems as Record<string, Record<string, unknown>>)['30000142'].stations = [60003760];
|
||||||
await writeFile(universePath, JSON.stringify(universe));
|
await writeFile(universePath, JSON.stringify(universe));
|
||||||
|
|
||||||
const result = await new ReferenceCompiler({ directory, profile: 'fixture', sources: fixtureSources(sources) }).compile();
|
const result = await new ReferenceCompiler({ directory, profile: 'fixture', sources: fixtureSources(sources) }).compile();
|
||||||
@@ -32,10 +33,13 @@ test('reference readers search normalized types and expose group/category/system
|
|||||||
const stations = await reader.stations({ systemId: 30000142 });
|
const stations = await reader.stations({ systemId: 30000142 });
|
||||||
expect(stations).toHaveLength(2);
|
expect(stations).toHaveLength(2);
|
||||||
expect(stations).toEqual([
|
expect(stations).toEqual([
|
||||||
{ id: 60003760, systemId: 30000142, systemName: 'Jita', regionId: 10000001, evidence: 'verified', sourceVerified: true, name: 'Jita IV - Moon 4 - Caldari Navy Assembly Plant', typeId: 587, ownerId: 1000035, x: 1, y: 2, z: 3 },
|
{ id: 60003760, systemId: 30000142, systemName: 'Jita', regionId: 10000001, evidence: 'system_listed', sourceVerified: false, name: null, typeId: null, ownerId: null, x: null, y: null, z: null },
|
||||||
{ id: 60003761, systemId: 30000142, systemName: 'Jita', regionId: 10000001, evidence: 'system_listed', sourceVerified: false, name: null, typeId: null, ownerId: null, x: null, y: null, z: null },
|
{ id: 60003761, systemId: 30000142, systemName: 'Jita', regionId: 10000001, evidence: 'verified', sourceVerified: true, name: 'Jita IV - Moon 4 - Caldari Navy Assembly Plant', typeId: 587, ownerId: 1000035, x: 1, y: 2, z: 3 },
|
||||||
]);
|
]);
|
||||||
expect(await reader.stations({ query: 'assembly' })).toEqual([stations[0]]);
|
const db = new Database(reader.databasePath, { readonly: true, strict: true });
|
||||||
|
try { expect(db.query<{ station_id: number }, []>('SELECT station_id FROM stations WHERE source_verified = 1 ORDER BY station_id LIMIT 1').get()?.station_id).toBe(60003761); }
|
||||||
|
finally { db.close(); }
|
||||||
|
expect(await reader.stations({ query: 'assembly' })).toEqual([stations[1]]);
|
||||||
const pinned = reader.pin();
|
const pinned = reader.pin();
|
||||||
try { expect((await pinned.searchTypesByName('pyerite'))[0]?.id).toBe(35); }
|
try { expect((await pinned.searchTypesByName('pyerite'))[0]?.id).toBe(35); }
|
||||||
finally { pinned.close(); }
|
finally { pinned.close(); }
|
||||||
|
|||||||
@@ -27,8 +27,10 @@ try {
|
|||||||
assert(verifiedStations > 0);
|
assert(verifiedStations > 0);
|
||||||
const joinedStations = Number((db.query<{ n: number }, []>('SELECT COUNT(*) AS n FROM stations st JOIN systems s ON s.system_id = st.system_id JOIN constellations c ON c.constellation_id = s.constellation_id JOIN regions r ON r.region_id = c.region_id').get() as { n: number }).n);
|
const joinedStations = Number((db.query<{ n: number }, []>('SELECT COUNT(*) AS n FROM stations st JOIN systems s ON s.system_id = st.system_id JOIN constellations c ON c.constellation_id = s.constellation_id JOIN regions r ON r.region_id = c.region_id').get() as { n: number }).n);
|
||||||
assert.equal(joinedStations, counts.stations, 'every station must join through system, constellation, and region');
|
assert.equal(joinedStations, counts.stations, 'every station must join through system, constellation, and region');
|
||||||
const verifiedStation = (await repository.stations({ limit: 1 })).find((station) => station.sourceVerified);
|
const verifiedStationId = db.query<{ station_id: number }, []>('SELECT station_id FROM stations WHERE source_verified = 1 ORDER BY station_id LIMIT 1').get()?.station_id;
|
||||||
assert(verifiedStation, 'reader must expose a verified station row');
|
assert(verifiedStationId !== undefined, 'published artifact must contain a verified station row');
|
||||||
|
const verifiedStation = (await repository.stations([verifiedStationId]))[0];
|
||||||
|
assert(verifiedStation?.sourceVerified, 'reader must expose the selected verified station row');
|
||||||
assert.deepEqual(await repository.stations([Number.MAX_SAFE_INTEGER]), [], 'unknown station IDs must remain absent rather than fabricated');
|
assert.deepEqual(await repository.stations([Number.MAX_SAFE_INTEGER]), [], 'unknown station IDs must remain absent rather than fabricated');
|
||||||
const integrity = db.query<{ integrity_check: string }, []>('PRAGMA integrity_check').all();
|
const integrity = db.query<{ integrity_check: string }, []>('PRAGMA integrity_check').all();
|
||||||
assert.deepEqual(integrity.map(row => row.integrity_check), ['ok']);
|
assert.deepEqual(integrity.map(row => row.integrity_check), ['ok']);
|
||||||
|
|||||||
Reference in New Issue
Block a user