mirror of
https://github.com/bookorbit/bookorbit.git
synced 2026-11-03 00:11:37 -06:00
fix(server): fix migration executor bugs and schema corrections
This commit is contained in:
@@ -272,19 +272,19 @@ export async function seedCanonicalScenario(ctx: MigrationBookloreE2EContext): P
|
||||
null,
|
||||
'Hash description',
|
||||
null,
|
||||
101,
|
||||
null,
|
||||
-5,
|
||||
null,
|
||||
null,
|
||||
12,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
-30,
|
||||
0,
|
||||
null,
|
||||
],
|
||||
|
||||
@@ -216,9 +216,11 @@ export async function waitForMigrationToFinish(
|
||||
};
|
||||
report: {
|
||||
run: { id: number; state: string; currentStage: string | null };
|
||||
totals: { processed: number; imported: number; skipped: number; unresolved: number; failed: number };
|
||||
metrics: Array<Record<string, unknown>>;
|
||||
plan: Record<string, unknown> | null;
|
||||
summary: Record<string, unknown> | null;
|
||||
details: Record<string, unknown>;
|
||||
};
|
||||
}> {
|
||||
let latestProgress:
|
||||
@@ -255,9 +257,11 @@ export async function waitForMigrationToFinish(
|
||||
|
||||
const reportResponse = await apiJson<{
|
||||
run: { id: number; state: string; currentStage: string | null };
|
||||
totals: { processed: number; imported: number; skipped: number; unresolved: number; failed: number };
|
||||
metrics: Array<Record<string, unknown>>;
|
||||
plan: Record<string, unknown> | null;
|
||||
summary: Record<string, unknown> | null;
|
||||
details: Record<string, unknown>;
|
||||
}>(ctx, {
|
||||
method: 'GET',
|
||||
url: `/api/v1/migration/runs/${runId}/report`,
|
||||
|
||||
@@ -369,7 +369,25 @@ describe('Migration Booklore API to DB (e2e)', { timeout: 240_000 }, () => {
|
||||
});
|
||||
expect(jsonExport.statusCode).toBe(200);
|
||||
expect(jsonExport.headers['content-type']).toContain('application/json');
|
||||
expect(jsonExport.body).toContain('"state": "completed"');
|
||||
const parsedJsonExport = JSON.parse(jsonExport.body) as {
|
||||
run: { state: string };
|
||||
details: {
|
||||
matchedBooks: Array<{ sourceBookId: string; sourceTitle: string | null; targetBookId: number; strategy: string }>;
|
||||
userPreview: unknown[];
|
||||
};
|
||||
};
|
||||
expect(parsedJsonExport.run.state).toBe('completed');
|
||||
expect(parsedJsonExport.details.matchedBooks).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
sourceBookId: '101',
|
||||
sourceTitle: 'Imported ISBN Title',
|
||||
targetBookId: scenario.books.isbn.bookId,
|
||||
strategy: 'isbn',
|
||||
}),
|
||||
]),
|
||||
);
|
||||
expect(parsedJsonExport.details.userPreview).toHaveLength(2);
|
||||
|
||||
const csvExport = await apiText(ctx, {
|
||||
method: 'GET',
|
||||
@@ -378,7 +396,44 @@ describe('Migration Booklore API to DB (e2e)', { timeout: 240_000 }, () => {
|
||||
});
|
||||
expect(csvExport.statusCode).toBe(200);
|
||||
expect(csvExport.headers['content-type']).toContain('text/csv');
|
||||
expect(csvExport.body).toContain('section,stage,entityType,processed,imported,skipped,unresolved,failed,code,message,createdAt');
|
||||
expect(csvExport.body).toContain(
|
||||
'section,stage,entityType,processed,imported,skipped,unresolved,failed,sourceBookId,sourceTitle,sourceAuthor,targetBookId,targetTitle,strategy,sourceUserId,targetUserId,username,reason,details,code,message,createdAt',
|
||||
);
|
||||
expect(csvExport.body).toContain('matched_books,shared_overlays,book_match');
|
||||
expect(csvExport.body).toContain('Imported ISBN Title');
|
||||
|
||||
const reportDetails = finished.report.details as {
|
||||
matchedBooks?: Array<{ sourceBookId: string; sourceTitle: string | null; targetBookId: number; strategy: string }>;
|
||||
userPreview?: Array<{ username: string; counts: Record<string, number> }>;
|
||||
};
|
||||
expect(reportDetails.matchedBooks).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
sourceBookId: '101',
|
||||
sourceTitle: 'Imported ISBN Title',
|
||||
targetBookId: scenario.books.isbn.bookId,
|
||||
strategy: 'isbn',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
sourceBookId: '102',
|
||||
sourceTitle: 'Imported Hash Title',
|
||||
targetBookId: scenario.books.hash.bookId,
|
||||
strategy: 'file_hash',
|
||||
}),
|
||||
]),
|
||||
);
|
||||
expect(reportDetails.userPreview).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
username: 'alice',
|
||||
counts: expect.objectContaining({ statuses: 2, fileProgress: 2, bookmarks: 1, annotations: 1, shelves: 2 }),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
username: 'bob',
|
||||
counts: expect.objectContaining({ statuses: 1, fileProgress: 1, bookmarks: 1, annotations: 1, shelves: 1 }),
|
||||
}),
|
||||
]),
|
||||
);
|
||||
|
||||
const metricByKey = buildMetricMap(finished.report.metrics as MetricRow[]);
|
||||
expect(metricByKey.get('shared_overlays:book_metadata')).toMatchObject({
|
||||
@@ -478,8 +533,11 @@ describe('Migration Booklore API to DB (e2e)', { timeout: 240_000 }, () => {
|
||||
title: 'Imported Hash Title',
|
||||
description: 'Hash description',
|
||||
subtitle: null,
|
||||
publishedYear: null,
|
||||
pageCount: null,
|
||||
rating: null,
|
||||
googleBooksId: null,
|
||||
durationSeconds: null,
|
||||
});
|
||||
expect(metadataByBookId.get(scenario.books.audio.bookId)).toMatchObject({
|
||||
title: 'Audio Path Match',
|
||||
|
||||
Reference in New Issue
Block a user