mirror of
https://github.com/bookorbit/bookorbit.git
synced 2026-11-03 00:11:37 -06:00
fix(server): merge imported read statuses safely
Preserve target-only statuses and update conflicts only when the source carries newer evidence. Treat timestamp-free source rows as insert-only so they cannot overwrite curated target state. Fixes #1104
This commit is contained in:
@@ -135,6 +135,31 @@ describe('Migration Audiobookshelf live and backup (e2e)', { timeout: 300_000 },
|
||||
const backupExport = await adapter.exportData(backupConfig as AudiobookshelfConnectionConfig);
|
||||
expect(canonicalizeExport(backupExport)).toEqual(canonicalizeExport(liveExport));
|
||||
|
||||
await ctx.db.insert(schema.userBookStatus).values([
|
||||
{
|
||||
userId: scenario.targetUsers.theo.id,
|
||||
bookId: scenario.targetBooks.glass.bookId,
|
||||
status: 'read',
|
||||
source: 'manual',
|
||||
finishedAt: new Date('2024-01-01T00:00:00.000Z'),
|
||||
updatedAt: new Date('2024-01-01T00:00:00.000Z'),
|
||||
},
|
||||
{
|
||||
userId: scenario.targetUsers.maya.id,
|
||||
bookId: scenario.targetBooks.glass.bookId,
|
||||
status: 'want_to_read',
|
||||
source: 'manual',
|
||||
updatedAt: new Date('2099-01-01T00:00:00.000Z'),
|
||||
},
|
||||
{
|
||||
userId: scenario.targetUsers.theo.id,
|
||||
bookId: scenario.targetBooks.map.bookId,
|
||||
status: 'want_to_read',
|
||||
source: 'manual',
|
||||
updatedAt: new Date('2020-01-01T00:00:00.000Z'),
|
||||
},
|
||||
]);
|
||||
|
||||
const started = await apiJson<{ id: number; state: string }>(ctx, {
|
||||
method: 'POST',
|
||||
url: '/api/v1/migration/runs/live',
|
||||
@@ -149,6 +174,30 @@ describe('Migration Audiobookshelf live and backup (e2e)', { timeout: 300_000 },
|
||||
expect(finished.report.totals.failed).toBe(0);
|
||||
expect(finished.report.totals.unresolved).toBeGreaterThanOrEqual(1);
|
||||
|
||||
const mergedStatuses = await ctx.db
|
||||
.select()
|
||||
.from(schema.userBookStatus)
|
||||
.where(inArray(schema.userBookStatus.userId, [scenario.targetUsers.maya.id, scenario.targetUsers.theo.id]));
|
||||
expect(mergedStatuses).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
userId: scenario.targetUsers.theo.id,
|
||||
bookId: scenario.targetBooks.glass.bookId,
|
||||
status: 'read',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
userId: scenario.targetUsers.maya.id,
|
||||
bookId: scenario.targetBooks.glass.bookId,
|
||||
status: 'want_to_read',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
userId: scenario.targetUsers.theo.id,
|
||||
bookId: scenario.targetBooks.map.bookId,
|
||||
status: 'read',
|
||||
}),
|
||||
]),
|
||||
);
|
||||
|
||||
await assertImportedState();
|
||||
const countsAfterFirstRun = await loadStateCounts();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user