test(budget): settled_at through the HTTP pipe

The e2e settlement round trip now sends settled_at on create and update
and reads it back from the column, including a PUT that clears it to
NULL. The service test covers the clear branch for null and the empty
string the date picker sends.
This commit is contained in:
Maurice
2026-09-12 13:15:38 +02:00
parent 953720c874
commit 259a8c6b4d
2 changed files with 45 additions and 6 deletions
+25 -6
View File
@@ -228,22 +228,41 @@ describe('Budget e2e (real auth guard + temp SQLite, real budget SQL)', () => {
}
});
it('200 on settlement update with permission, persisting the new amount', async () => {
it('200 on settlement update with permission, persisting the new amount and day', async () => {
const created = await request(server)
.post(`/api/trips/${tripId}/budget/settlements`)
.set('Cookie', sessionCookie(1))
.send({ from_user_id: 2, to_user_id: 1, amount: 10 });
.send({ from_user_id: 2, to_user_id: 1, amount: 10, settled_at: '2026-01-05' });
expect(created.status).toBe(201);
expect(created.body.settlement.settled_at).toBe('2026-01-05');
const res = await request(server)
.put(`/api/trips/${tripId}/budget/settlements/${created.body.settlement.id}`)
.set('Cookie', sessionCookie(1))
.send({ from_user_id: 2, to_user_id: 1, amount: 15, settled_at: '2026-01-09' });
expect(res.status).toBe(200);
expect(res.body.settlement).toMatchObject({ id: created.body.settlement.id, from_user_id: 2, to_user_id: 1, amount: 15, settled_at: '2026-01-09' });
const row = db.prepare('SELECT amount, settled_at FROM budget_settlements WHERE id = ?').get(created.body.settlement.id);
expect(row).toEqual({ amount: 15, settled_at: '2026-01-09' });
});
it('200 on settlement update that clears the day, leaving NULL in the column', async () => {
const created = await request(server)
.post(`/api/trips/${tripId}/budget/settlements`)
.set('Cookie', sessionCookie(1))
.send({ from_user_id: 2, to_user_id: 1, amount: 10, settled_at: '2026-01-05' });
expect(created.status).toBe(201);
const res = await request(server)
.put(`/api/trips/${tripId}/budget/settlements/${created.body.settlement.id}`)
.set('Cookie', sessionCookie(1))
.send({ from_user_id: 2, to_user_id: 1, amount: 15 });
.send({ from_user_id: 2, to_user_id: 1, amount: 10, settled_at: null });
expect(res.status).toBe(200);
expect(res.body.settlement).toMatchObject({ id: created.body.settlement.id, from_user_id: 2, to_user_id: 1, amount: 15 });
expect(res.body.settlement.settled_at).toBeNull();
const row = db.prepare('SELECT amount FROM budget_settlements WHERE id = ?').get(created.body.settlement.id);
expect(row).toEqual({ amount: 15 });
const row = db.prepare('SELECT settled_at FROM budget_settlements WHERE id = ?').get(created.body.settlement.id);
expect(row).toEqual({ settled_at: null });
});
it('404 on settlement update when it does not exist', async () => {
@@ -790,6 +790,26 @@ describe('post-fold quirk fixes', () => {
expect(untouched!.settled_at).toBe('2026-01-09');
});
it('BUDGET-SVC-DB-043: clearing settled_at stores NULL, whether it arrives as null or an empty string', () => {
const { user: alice } = createUser(testDb, { username: 'alice' });
const { user: bob } = createUser(testDb, { username: 'bob' });
const trip = createTrip(testDb, alice.id);
const parties = { from_user_id: alice.id, to_user_id: bob.id, amount: 20 };
// The date picker's clear button sends '' and the contract allows null; both
// mean "no day of its own", never a stored empty string.
const blank = budget.insertSettlement(trip.id, { ...parties, settled_at: '' }, alice.id);
expect(blank!.settled_at).toBeNull();
const dated = budget.insertSettlement(trip.id, { ...parties, settled_at: '2026-01-05' }, alice.id);
expect(budget.applySettlementUpdate(dated!.id, trip.id, { ...parties, settled_at: null })!.settled_at).toBeNull();
budget.applySettlementUpdate(dated!.id, trip.id, { ...parties, settled_at: '2026-01-05' });
expect(budget.applySettlementUpdate(dated!.id, trip.id, { ...parties, settled_at: '' })!.settled_at).toBeNull();
const row = testDb.prepare('SELECT settled_at FROM budget_settlements WHERE id = ?').get(dated!.id) as { settled_at: string | null };
expect(row.settled_at).toBeNull();
});
// ── Notes vs. itemized receipts (#1658) ────────────────────────────────────
it('BUDGET-SVC-DB-021: a note and a receipt are stored in their own columns', () => {