mirror of
https://github.com/bookorbit/bookorbit.git
synced 2026-11-03 00:11:37 -06:00
fix(reading): file late Kobo sessions into the read they finished
When a Kobo syncs after finishing a book, its finished state closes the reading attempt before the offline sessions arrive, and those sessions opened a second attempt, marking the book as reread or read twice. Activity dated within the finished read now joins it, activity from before it began joins nothing, and an undated start never widens the range. Update the reading-attempts E2E Kobo test to use a real device that holds the book. Fixes #1224 Fixes #1182
This commit is contained in:
@@ -44,6 +44,7 @@ export function makeFakeRepo() {
|
||||
findLatest: vi.fn((_tx: object, userId: number, bookId: number) =>
|
||||
Promise.resolve([...rows].reverse().find((row) => row.userId === userId && row.bookId === bookId && row.deletedAt === null)),
|
||||
),
|
||||
attachBackfilledSessions: vi.fn(() => Promise.resolve()),
|
||||
hasCompleted: vi.fn((_tx: object, userId: number, bookId: number) =>
|
||||
Promise.resolve(rows.some((row) => row.userId === userId && row.bookId === bookId && row.outcome === 'completed' && row.deletedAt === null)),
|
||||
),
|
||||
|
||||
@@ -346,6 +346,34 @@ describe('Reading attempts main-flow simulation (docker e2e)', { timeout: TIMEOU
|
||||
const book = await createBook();
|
||||
await patchStatus(book.bookId, { status: 'read', startedAt: '2024-01-01', finishedAt: '2024-01-10' });
|
||||
const kobo = ctx.app.get(KoboReadingStateService);
|
||||
const device = await ctx.app.inject({
|
||||
method: 'POST',
|
||||
url: '/api/v1/kobo/devices',
|
||||
headers: auth(ctx.adminToken),
|
||||
payload: { name: 'Attempt simulation Kobo' },
|
||||
});
|
||||
expect(device.statusCode).toBe(201);
|
||||
const deviceId = (device.json() as { id: number }).id;
|
||||
// The device holds this copy, so the positions it reports are its reader's, not an unknown copy's.
|
||||
await ctx.db.insert(schema.koboDeviceDeliveries).values({
|
||||
userId: adminUserId,
|
||||
deviceId,
|
||||
bookId: book.bookId,
|
||||
sourceFileId: book.fileId,
|
||||
metadata: {
|
||||
artifactId: 'simulated',
|
||||
sourceRevision: 'simulated',
|
||||
requestedVariant: null,
|
||||
actualVariant: 'original',
|
||||
format: 'epub',
|
||||
sizeBytes: 1024,
|
||||
sha256: 'simulated',
|
||||
fileHash: 'simulated',
|
||||
outcome: 'original',
|
||||
},
|
||||
options: { hyphenate: false, converterVersion: null },
|
||||
state: 'transfer_observed',
|
||||
});
|
||||
await kobo.upsertState(
|
||||
adminUserId,
|
||||
book.bookId,
|
||||
@@ -357,7 +385,7 @@ describe('Reading attempts main-flow simulation (docker e2e)', { timeout: TIMEOU
|
||||
1,
|
||||
98,
|
||||
false,
|
||||
1,
|
||||
deviceId,
|
||||
);
|
||||
await kobo.upsertState(
|
||||
adminUserId,
|
||||
@@ -370,7 +398,7 @@ describe('Reading attempts main-flow simulation (docker e2e)', { timeout: TIMEOU
|
||||
1,
|
||||
98,
|
||||
false,
|
||||
1,
|
||||
deviceId,
|
||||
);
|
||||
const history = await listAttempts(book.bookId);
|
||||
expect(history.total).toBe(2);
|
||||
|
||||
Reference in New Issue
Block a user