mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-23 07:26:30 +00:00
fix(server): plan 3h close-out — new-value-wins COALESCE restores stay/reservation propagation; doc-sync state filter truthiness; request-context and parity ratchets for share, doc-sync and the integrations
This commit is contained in:
@@ -1155,3 +1155,65 @@ describe('PlacesRepository.setSource (DWS7)', () => {
|
||||
expect(row.updated_at).toBe(before.updated_at);
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Plan 3h Task 7 review, M1 coverage — Plan 3h Task 4's `maps.service.ts`
|
||||
// photo-fetch (MAP9), untested at the repository level (the service test
|
||||
// stubs this method).
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('PlacesRepository.setImageUrlIfUnset (MAP9)', () => {
|
||||
it('MAP9-001: fills a NULL image_url, matching the legacy $or guard', async () => {
|
||||
const { user } = createUser(testDb);
|
||||
const trip = createTrip(testDb, user.id);
|
||||
const place = createPlace(testDb, trip.id, { name: 'No photo yet' });
|
||||
testDb.prepare("UPDATE places SET google_place_id = 'ChIJ_shared' WHERE id = ?").run(place.id);
|
||||
|
||||
const n = await places.setImageUrlIfUnset('ChIJ_shared', '/uploads/photo-cache/new.jpg');
|
||||
|
||||
expect(n).toBe(1);
|
||||
const row = testDb.prepare('SELECT image_url FROM places WHERE id = ?').get(place.id) as { image_url: string | null };
|
||||
expect(row.image_url).toBe('/uploads/photo-cache/new.jpg');
|
||||
});
|
||||
|
||||
it('MAP9-002: fills an EMPTY-STRING image_url too (the second half of the $or)', async () => {
|
||||
const { user } = createUser(testDb);
|
||||
const trip = createTrip(testDb, user.id);
|
||||
const place = createPlace(testDb, trip.id, { name: 'Blank photo' });
|
||||
testDb.prepare("UPDATE places SET google_place_id = 'ChIJ_blank', image_url = '' WHERE id = ?").run(place.id);
|
||||
|
||||
const n = await places.setImageUrlIfUnset('ChIJ_blank', '/uploads/photo-cache/filled.jpg');
|
||||
|
||||
expect(n).toBe(1);
|
||||
const row = testDb.prepare('SELECT image_url FROM places WHERE id = ?').get(place.id) as { image_url: string | null };
|
||||
expect(row.image_url).toBe('/uploads/photo-cache/filled.jpg');
|
||||
});
|
||||
|
||||
it('MAP9-003: an already-set image_url is left untouched — never clobbers a custom image', async () => {
|
||||
const { user } = createUser(testDb);
|
||||
const trip = createTrip(testDb, user.id);
|
||||
const place = createPlace(testDb, trip.id, { name: 'Custom photo' });
|
||||
testDb.prepare("UPDATE places SET google_place_id = 'ChIJ_custom', image_url = '/uploads/custom.jpg' WHERE id = ?").run(place.id);
|
||||
|
||||
const n = await places.setImageUrlIfUnset('ChIJ_custom', '/uploads/photo-cache/should-not-land.jpg');
|
||||
|
||||
expect(n).toBe(0);
|
||||
const row = testDb.prepare('SELECT image_url FROM places WHERE id = ?').get(place.id) as { image_url: string | null };
|
||||
expect(row.image_url).toBe('/uploads/custom.jpg');
|
||||
});
|
||||
|
||||
it('MAP9-004: unscoped by trip — every empty-image row sharing the google_place_id across trips is filled in one statement', async () => {
|
||||
const { user } = createUser(testDb);
|
||||
const tripA = createTrip(testDb, user.id);
|
||||
const tripB = createTrip(testDb, user.id);
|
||||
const placeA = createPlace(testDb, tripA.id, { name: 'A' });
|
||||
const placeB = createPlace(testDb, tripB.id, { name: 'B' });
|
||||
testDb.prepare("UPDATE places SET google_place_id = 'ChIJ_shared2' WHERE id IN (?, ?)").run(placeA.id, placeB.id);
|
||||
|
||||
const n = await places.setImageUrlIfUnset('ChIJ_shared2', '/uploads/photo-cache/both.jpg');
|
||||
|
||||
expect(n).toBe(2);
|
||||
expect((testDb.prepare('SELECT image_url FROM places WHERE id = ?').get(placeA.id) as { image_url: string }).image_url).toBe('/uploads/photo-cache/both.jpg');
|
||||
expect((testDb.prepare('SELECT image_url FROM places WHERE id = ?').get(placeB.id) as { image_url: string }).image_url).toBe('/uploads/photo-cache/both.jpg');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -505,3 +505,64 @@ describe('ReservationsRepository — Task 2 reads (listForTrip / findWithJoins /
|
||||
expect(typed.map((r) => r.id).sort((a, b) => a - b)).toEqual([dinner.id, hotel.id].sort((a, b) => a - b));
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Plan 3h Task 7 review, M1 coverage — the AirTrail leaf's writes/reads,
|
||||
// untested at the repository level (the airtrail-link/sync service tests
|
||||
// stub this repository).
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('ReservationsRepository — AirTrail leaf (ATL1/ATL2/ATL4, airports)', () => {
|
||||
it('ATL1 linkAirtrailSingleFlight — stamps every AirTrail column and switches sync on, matching the legacy statement', async () => {
|
||||
const { user } = createUser(testDb);
|
||||
const trip = createTrip(testDb, user.id);
|
||||
const res = createReservation(testDb, trip.id, { title: 'Flight', type: 'flight' });
|
||||
|
||||
await reservationsRepo.linkAirtrailSingleFlight(res.id, 'AT-42', user.id, 'hash-1', '2026-09-24T10:00:00Z');
|
||||
|
||||
const row = testDb.prepare(
|
||||
"SELECT external_source, external_id, external_owner_user_id, sync_enabled, external_hash, external_synced_at FROM reservations WHERE id = ?",
|
||||
).get(res.id);
|
||||
expect(row).toEqual({
|
||||
external_source: 'airtrail', external_id: 'AT-42', external_owner_user_id: user.id,
|
||||
sync_enabled: 1, external_hash: 'hash-1', external_synced_at: '2026-09-24T10:00:00Z',
|
||||
});
|
||||
});
|
||||
|
||||
it('ATL2 setAirtrailSyncDisabled — turns sync_enabled off, matching the legacy statement, and leaves every other column alone', async () => {
|
||||
const { user } = createUser(testDb);
|
||||
const trip = createTrip(testDb, user.id);
|
||||
const res = createReservation(testDb, trip.id, { title: 'Flight', type: 'flight' });
|
||||
await reservationsRepo.linkAirtrailSingleFlight(res.id, 'AT-42', user.id, 'hash-1', '2026-09-24T10:00:00Z');
|
||||
|
||||
await reservationsRepo.setAirtrailSyncDisabled(res.id);
|
||||
|
||||
const row = testDb.prepare('SELECT sync_enabled, external_id FROM reservations WHERE id = ?').get(res.id);
|
||||
expect(row).toEqual({ sync_enabled: 0, external_id: 'AT-42' });
|
||||
});
|
||||
|
||||
it('ATL4 findAirtrailLinked — matches the legacy statement, scoped to external_source = \'airtrail\' (a plain flight misses)', async () => {
|
||||
const { user } = createUser(testDb);
|
||||
const trip = createTrip(testDb, user.id);
|
||||
const linked = createReservation(testDb, trip.id, { title: 'Flight', type: 'flight' });
|
||||
await reservationsRepo.linkAirtrailSingleFlight(linked.id, 'AT-7', user.id, 'hash-2', '2026-09-24T11:00:00Z');
|
||||
const plain = createReservation(testDb, trip.id, { title: 'Dinner', type: 'restaurant' });
|
||||
|
||||
const legacy = testDb.prepare(
|
||||
"SELECT id, trip_id, external_id, external_owner_user_id, sync_enabled FROM reservations WHERE id = ? AND external_source = 'airtrail'",
|
||||
).get(linked.id);
|
||||
expect(await reservationsRepo.findAirtrailLinked(linked.id)).toEqual(legacy);
|
||||
expect(await reservationsRepo.findAirtrailLinked(plain.id)).toBeUndefined();
|
||||
});
|
||||
|
||||
it('markNeedsReview (airports) — sets needs_review, matching the legacy statement', async () => {
|
||||
const { user } = createUser(testDb);
|
||||
const trip = createTrip(testDb, user.id);
|
||||
const res = createReservation(testDb, trip.id, { title: 'Flight', type: 'flight' });
|
||||
expect((testDb.prepare('SELECT needs_review FROM reservations WHERE id = ?').get(res.id) as { needs_review: number }).needs_review).toBe(0);
|
||||
|
||||
await reservationsRepo.markNeedsReview(res.id);
|
||||
|
||||
expect((testDb.prepare('SELECT needs_review FROM reservations WHERE id = ?').get(res.id) as { needs_review: number }).needs_review).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user