feat(api): server-authoritative pause/resume + worked-time stop + CSV paused
Add user-scoped POST /api/sessions/:id/pause and /resume endpoints, mirroring the stop handler's ownership/lookup and 401/404/409 guards. Pause sets paused_at (status stays active); resume folds the open span into paused_seconds and clears paused_at. Change stop to fold any open pause span into paused_seconds, then set duration_seconds = max(0, round((end-start)/1000) - paused_seconds) so saved duration is worked time, and clear paused_at. Add a "Paused Duration" column to /api/export (after "Total Duration") using formatDuration(paused_seconds). Products affected: SoleLog backend (apps/api)
This commit is contained in:
@@ -53,7 +53,7 @@ describe('csv export', () => {
|
||||
const text = await res.text();
|
||||
const lines = text.split('\n');
|
||||
expect(lines[0]).toBe(
|
||||
'"ID","Task","Insole Type","No. of Insoles","Date","Total Duration","Start Time","End Time"'
|
||||
'"ID","Task","Insole Type","No. of Insoles","Date","Total Duration","Paused Duration","Start Time","End Time"'
|
||||
);
|
||||
expect(lines).toHaveLength(2);
|
||||
expect(lines[1]).toContain('"Frezen"');
|
||||
@@ -61,6 +61,27 @@ describe('csv export', () => {
|
||||
expect(lines[1]).toContain('"00:01:30"');
|
||||
});
|
||||
|
||||
it('includes a Paused Duration column carrying the formatted paused value', async () => {
|
||||
const app = createApp();
|
||||
const token = await authToken(app, 'export-paused@example.com');
|
||||
const activityId = await seedActivity('Bekleden');
|
||||
const id = await completedSession(app, token, activityId, 'Kurk', 90);
|
||||
|
||||
// Stamp a known paused total on the completed session.
|
||||
await db.update(workSessions).set({ pausedSeconds: 75 }).where(eq(workSessions.id, id));
|
||||
|
||||
const res = await app.request('/api/export', { headers: bearer(token) });
|
||||
expect(res.status).toBe(200);
|
||||
const lines = (await res.text()).split('\n');
|
||||
const header = lines[0].split(',');
|
||||
const totalIdx = header.indexOf('"Total Duration"');
|
||||
const pausedIdx = header.indexOf('"Paused Duration"');
|
||||
expect(pausedIdx).toBe(totalIdx + 1);
|
||||
|
||||
const cells = lines[1].split(',');
|
||||
expect(cells[pausedIdx]).toBe('"00:01:15"');
|
||||
});
|
||||
|
||||
it('excludes active and discarded sessions and scopes to the user', async () => {
|
||||
const app = createApp();
|
||||
const tokenA = await authToken(app, 'export-scopeA@example.com');
|
||||
|
||||
Reference in New Issue
Block a user