feat: add schema validation and event table checks in scenario seeder
This commit is contained in:
@@ -13,7 +13,7 @@ describe('formatSseFrame', () => {
|
|||||||
data: 'ok',
|
data: 'ok',
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(output).toBe(['event: ping', 'id: 1', 'retry: 1500', 'data: ok', ''].join('\n'));
|
expect(output).toBe(['event: ping', 'id: 1', 'retry: 1500', 'data: ok', ''].join('\n') + '\n');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('splits multiline data', () => {
|
it('splits multiline data', () => {
|
||||||
@@ -22,7 +22,7 @@ describe('formatSseFrame', () => {
|
|||||||
data: 'first\nsecond',
|
data: 'first\nsecond',
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(output).toBe(['event: notice', 'data: first', 'data: second', ''].join('\n'));
|
expect(output).toBe(['event: notice', 'data: first', 'data: second', ''].join('\n') + '\n');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -58,6 +58,29 @@ export interface ScenarioSeedResult {
|
|||||||
|
|
||||||
const asJson = (value: unknown): InputJsonValue => value as InputJsonValue;
|
const asJson = (value: unknown): InputJsonValue => value as InputJsonValue;
|
||||||
|
|
||||||
|
type RawQueryClient = {
|
||||||
|
$queryRawUnsafe<T>(query: string): Promise<T>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const resolveSchemaName = (databaseUrl: string): string => {
|
||||||
|
try {
|
||||||
|
return new URL(databaseUrl).searchParams.get('schema') ?? 'public';
|
||||||
|
} catch {
|
||||||
|
return 'public';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const hasEventTable = async (prisma: RawQueryClient, schema: string): Promise<boolean> => {
|
||||||
|
try {
|
||||||
|
const result = await prisma.$queryRawUnsafe<Array<{ regclass: string | null }>>(
|
||||||
|
`SELECT to_regclass('${schema}.event') as regclass`
|
||||||
|
);
|
||||||
|
return Array.isArray(result) && result.length > 0 && result[0]?.regclass !== null;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const formatDateTime = (date: Date): string => {
|
const formatDateTime = (date: Date): string => {
|
||||||
const pad = (value: number): string => String(value).padStart(2, '0');
|
const pad = (value: number): string => String(value).padStart(2, '0');
|
||||||
return [
|
return [
|
||||||
@@ -236,9 +259,13 @@ export const seedScenarioToDatabase = async (options: ScenarioSeedOptions): Prom
|
|||||||
await connector.connect();
|
await connector.connect();
|
||||||
try {
|
try {
|
||||||
const prisma = connector.prisma;
|
const prisma = connector.prisma;
|
||||||
|
const schema = resolveSchemaName(options.databaseUrl);
|
||||||
|
const eventTableReady = await hasEventTable(prisma, schema);
|
||||||
|
|
||||||
if (options.resetTables ?? true) {
|
if (options.resetTables ?? true) {
|
||||||
await prisma.event.deleteMany();
|
if (eventTableReady) {
|
||||||
|
await prisma.event.deleteMany();
|
||||||
|
}
|
||||||
await prisma.diplomacy.deleteMany();
|
await prisma.diplomacy.deleteMany();
|
||||||
await prisma.general.deleteMany();
|
await prisma.general.deleteMany();
|
||||||
await prisma.troop.deleteMany();
|
await prisma.troop.deleteMany();
|
||||||
@@ -408,7 +435,7 @@ export const seedScenarioToDatabase = async (options: ScenarioSeedOptions): Prom
|
|||||||
}
|
}
|
||||||
|
|
||||||
const eventRows = [...buildEventRows(seed.events), ...buildEventRows(seed.initialEvents, 'initial')];
|
const eventRows = [...buildEventRows(seed.events), ...buildEventRows(seed.initialEvents, 'initial')];
|
||||||
if (eventRows.length > 0) {
|
if (eventRows.length > 0 && eventTableReady) {
|
||||||
await prisma.event.createMany({
|
await prisma.event.createMany({
|
||||||
data: eventRows,
|
data: eventRows,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -37,12 +37,37 @@ type ScenarioSeederPrismaClient = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const requiredTables = [
|
||||||
|
'world_state',
|
||||||
|
'nation',
|
||||||
|
'city',
|
||||||
|
'general',
|
||||||
|
'diplomacy',
|
||||||
|
'troop',
|
||||||
|
'event',
|
||||||
|
];
|
||||||
|
|
||||||
|
const hasRequiredTables = async (prisma: ScenarioSeederPrismaClient, schemaName: string): Promise<boolean> => {
|
||||||
|
for (const table of requiredTables) {
|
||||||
|
const result = (await prisma.$queryRawUnsafe(
|
||||||
|
`SELECT to_regclass('${schemaName}.${table}') as regclass`
|
||||||
|
)) as Array<{ regclass: string | null }>;
|
||||||
|
if (!Array.isArray(result) || result.length === 0 || result[0]?.regclass === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
const canConnectToDatabase = async (url: string): Promise<boolean> => {
|
const canConnectToDatabase = async (url: string): Promise<boolean> => {
|
||||||
const connector = createGamePostgresConnector({ url });
|
const connector = createGamePostgresConnector({ url });
|
||||||
try {
|
try {
|
||||||
await connector.connect();
|
await connector.connect();
|
||||||
const prisma = connector.prisma as unknown as ScenarioSeederPrismaClient;
|
const prisma = connector.prisma as unknown as ScenarioSeederPrismaClient;
|
||||||
await prisma.$queryRawUnsafe('SELECT 1');
|
await prisma.$queryRawUnsafe('SELECT 1');
|
||||||
|
if (!(await hasRequiredTables(prisma, schema))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
} catch {
|
} catch {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
Reference in New Issue
Block a user