Drives src/routes/sessions.ts and src/lib/work-session.ts to 100% stmts/branch/funcs/lines (branch was 84.12% / 83.33%). - sessions.test.ts: added error/edge branch tests — malformed-JSON start (400), non-numeric id on pause/resume/stop/discard (404), pause/resume of a no-longer-active session (409), discard missing (404) + already-closed (409), stop missing (404), and discard ownership scoping (user B → 404). - work-session.test.ts: mapper unit tests — null insoleType, nullish pausedSeconds → 0, null durationSeconds/notes, full-field shape with activity/user opts, and opts defaulting to undefined. - test/integration/worker-session-lifecycle.test.ts: the anchor lifecycle test (start→pause→resume→stop→history→export). No bugs found — the code was correct. The two remaining uncovered branches were the defensive `row.pausedSeconds ?? 0` guards in resume/stop; pausedSeconds is NOT NULL DEFAULT 0 in the schema, so the null branch is unreachable via any route. Annotated both with `/* c8 ignore next -- reason */` (comment only, no behavior change).
108 lines
3.3 KiB
TypeScript
108 lines
3.3 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { toWorkSession } from '../src/lib/work-session';
|
|
import type { workSessions } from '../src/db/schema';
|
|
|
|
type WorkSessionRow = typeof workSessions.$inferSelect;
|
|
|
|
function baseRow(overrides: Partial<WorkSessionRow> = {}): WorkSessionRow {
|
|
return {
|
|
id: 1,
|
|
userId: 'user-1',
|
|
activityId: 1,
|
|
insoleType: 'Kurk',
|
|
pairCount: 2,
|
|
startTime: new Date('2026-06-17T08:00:00.000Z'),
|
|
endTime: null,
|
|
durationSeconds: null,
|
|
pausedSeconds: 0,
|
|
pausedAt: null,
|
|
status: 'active',
|
|
source: 'app',
|
|
notes: null,
|
|
createdAt: new Date('2026-06-17T08:00:00.000Z'),
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
describe('toWorkSession paused fields', () => {
|
|
it('maps pausedSeconds and a null pausedAt', () => {
|
|
const result = toWorkSession(baseRow({ pausedSeconds: 120, pausedAt: null }));
|
|
expect(result.paused_seconds).toBe(120);
|
|
expect(result.paused_at).toBeNull();
|
|
});
|
|
|
|
it('maps a pausedAt Date to its ISO string', () => {
|
|
const pausedAt = new Date('2026-06-17T08:05:00.000Z');
|
|
const result = toWorkSession(baseRow({ pausedSeconds: 0, pausedAt }));
|
|
expect(result.paused_at).toBe(pausedAt.toISOString());
|
|
});
|
|
});
|
|
|
|
describe('toWorkSession field mapping', () => {
|
|
it('maps a null insoleType to null', () => {
|
|
const result = toWorkSession(baseRow({ insoleType: null }));
|
|
expect(result.insole_type).toBeNull();
|
|
});
|
|
|
|
it('coalesces a nullish pausedSeconds to 0 (defensive against a null column value)', () => {
|
|
const result = toWorkSession(baseRow({ pausedSeconds: null as unknown as number }));
|
|
expect(result.paused_seconds).toBe(0);
|
|
});
|
|
|
|
it('coalesces a nullish durationSeconds to null and a nullish notes to null', () => {
|
|
const result = toWorkSession(baseRow({ durationSeconds: null, notes: null }));
|
|
expect(result.duration_seconds).toBeNull();
|
|
expect(result.notes).toBeNull();
|
|
});
|
|
|
|
it('shapes every field, threading activity/user opts through', () => {
|
|
const start = new Date('2026-06-17T08:00:00.000Z');
|
|
const end = new Date('2026-06-17T09:30:00.000Z');
|
|
const created = new Date('2026-06-17T07:59:00.000Z');
|
|
const result = toWorkSession(
|
|
baseRow({
|
|
id: 42,
|
|
userId: 'user-42',
|
|
activityId: 7,
|
|
insoleType: 'Berk',
|
|
pairCount: 4,
|
|
startTime: start,
|
|
endTime: end,
|
|
durationSeconds: 5000,
|
|
pausedSeconds: 400,
|
|
status: 'completed',
|
|
source: 'manual',
|
|
notes: 'handmatig',
|
|
createdAt: created,
|
|
}),
|
|
{ activityName: 'Frezen', userName: 'Jan', userEmail: 'jan@example.com' },
|
|
);
|
|
expect(result).toEqual({
|
|
id: 42,
|
|
user_id: 'user-42',
|
|
activity_id: 7,
|
|
activity_name: 'Frezen',
|
|
user_name: 'Jan',
|
|
user_email: 'jan@example.com',
|
|
insole_type: 'Berk',
|
|
pair_count: 4,
|
|
start_time: start.toISOString(),
|
|
end_time: end.toISOString(),
|
|
duration_seconds: 5000,
|
|
paused_seconds: 400,
|
|
paused_at: null,
|
|
status: 'completed',
|
|
source: 'manual',
|
|
notes: 'handmatig',
|
|
created_at: created.toISOString(),
|
|
});
|
|
});
|
|
|
|
it('defaults activity/user opts to undefined when omitted', () => {
|
|
const result = toWorkSession(baseRow());
|
|
expect(result.activity_name).toBeUndefined();
|
|
expect(result.user_name).toBeUndefined();
|
|
expect(result.user_email).toBeUndefined();
|
|
});
|
|
});
|