Commit Graph

628 Commits

Author SHA1 Message Date
Maurice cc79f561e5 refactor(auth): guards, auth decorators and ephemeral tokens move into an auth-core kernel
JwtAuthGuard, OptionalJwtAuthGuard, CookieAuthGuard, AdminGuard, the global
and MFA guards, @CurrentUser, @Public, JWT verification and the ephemeral
token service lived in the auth domain, so every controller that checked a
session depended on the whole account module and its imports. They import
no domain themselves; under src/nest/auth-core they are shared kernel like
common/, every importer points there directly, and AtlasModule and
NotificationsModule stop importing AuthModule, which they only did for the
demo gate. lint:boundaries counts auth-core as kernel: 57 fewer cyclic
domain edges, the largest cycle shrinks from 46 to 40 domains.
2026-10-10 20:15:58 +02:00
Maurice 1a9855cf70 refactor(mcp): the demo-mode write block runs once, in the registry's tool gate
Every write tool used to open with its own demo check, 196 copies across 30
tool files, most of them injecting AuthService for nothing else. The
registry now takes a toolGate (built from the container) that runs before
each registered tool handler, and TREK's gate refuses a demo account on
every tool not annotated read-only with the same canned result. The
per-tool checks, the AuthService injections, AuthService.isDemoUser and the
AuthModule imports that only fed them are gone; ESLint refuses a demo check
in a tool file, and a registry-wide test calls every write tool as the
demo account.
2026-10-10 20:15:57 +02:00
Maurice 8350d04a63 refactor(db): the Immich, Synology and AirTrail columns of users get repositories of their own
UserImmichRepository, UserSynologyRepository and UserAirtrailRepository hold the eighteen connection statements UsersRepository carried, on the same users table and with their statements unchanged, provided through a small module as factories over the EntityManager. ImmichService, SynologyService, AirtrailService and JourneyService inject them instead of the account repository. UserRow drops the dead immich_access_token column (the table keeps it), and a shape test pins the account repository's size.
2026-10-10 20:15:57 +02:00
Maurice 3b58694eb9 refactor(settings): instance settings are named through one typed register
app-settings.registry.ts lists every app_settings key the server reads by name with its shape, default, environment override and flags. The admin form's keys, its masked and encrypted handling and the public config's reads derive from it, isOidcConfigured() replaces the three issuer/client-id checks in AuthService, and OidcService resolves its values through the same rule while still requiring the client secret. Every literal getValue() read goes through readAppSetting()/resolveAppSetting(), and ESLint refuses a new one.
2026-10-10 20:15:56 +02:00
Maurice 42d75a2b7e test(auth): pin how the four OIDC checks read the environment and the settings
The login toggles' legacy fallback, the public config, the lockout guard of the settings save and the provider config each decide on their own whether OIDC is configured. These cases hold what each answers, including that only the provider config also wants the client secret, before the checks are shared.
2026-10-10 20:15:56 +02:00
Maurice e316ccf3bb perf(db): the calendar export, the journey reads and the stop reorders read and write in batches
The ICS export reads the stops and notes of all dated days in two statements instead of two per day, the journey's full read and entry list take the source trip titles in one join instead of one read per entry, and reordering a day's stops or sorting it by time writes every position in one CASE update. Results are unchanged: each day keeps its order (the id settles a tie), a missing trip still names null, and an id listed twice keeps its last position.
2026-10-10 20:15:56 +02:00
Maurice 91deedae8b refactor(db): JSON columns are decoded through one declared schema per column
decodeJson/decodeJsonResult/encodeJson read and write a JSON-in-TEXT column against the schema and fallback declared for it in db/json-columns.ts, and log a row that does not decode. reservations.metadata, the OAuth scope and redirect lists, consents, API token scopes, MFA backup codes, journey entry tags and pros/cons and poll options go through it. Readers that used to throw on a broken row (a hotel update, the ICS export, the price mirror, the OAuth token and session reads, a consent, a journey entry, a poll) now take the tolerant fallback the other readers of the same column already used.
2026-10-10 20:15:55 +02:00
Maurice a926990e4a fix(db): timestamp columns are written and stored in one canonical spelling
A DbTimestamp brand, produced only by dbNow(), types the expiry writes of OAuth tokens and password reset links, and collection edits stamp updated_at the same way. parseDbTimestamp() reads the stored text as UTC and replaces the oauth-local parser; utcSuffix lives once beside it. A data migration rewrites the ISO values those writers left behind, and the OAuth session lists keep answering the expiries in ISO.

Two outputs change on purpose. collections.updated_at used to come back in two spellings: a collection never edited answered the column default (2026-01-02 03:04:05), an edited one the ISO text its writer stored (2026-01-02T03:04:05.678Z). It now answers the canonical text in every case, in the PUT and cover responses and, once the migration has run, in every read; the shared schema types it as a plain string and the client does not read it. The ISO expiries of the OAuth session lists are rebuilt from the stored second-precision text, so they end in .000Z instead of carrying the milliseconds of the write.
2026-10-10 20:15:55 +02:00
Maurice 07117949ef fix(mobile): editing a bus, car, taxi or ferry on the phone keeps its price (#2233)
Editing a transport without metadata of its own (bus, car, taxi, ferry
without a transit itinerary) in the phone sheet wiped the expense price
shown on the booking card. The phone built the payload with
emptyMetadataAsNull and sent metadata: null, which the reservations
service reads as "clear the column", so keepMirroredPrice had nothing to
carry price and priceCurrency into. The desktop and both booking forms
send an empty object, which keeps the price.

The option only existed to keep the phone's null, so it is gone and both
views now send the rebuilt metadata object as is.

Red test: FE-MOB-TRFRM-060 in
client/tests/unit/mobile/trip/MTransportFormSheet.test.tsx failed with
"AssertionError: expected null to deeply equal {}".
RESV-SVC-043 in server/tests/unit/nest/reservations.service.test.ts shows
the server side: {} keeps the stored price, null drops it.

Changed assertions (they asserted the null):
- MTransportFormSheet.test.tsx FE-MOB-TRFRM-006: payload metadata null
  is now {}.
- MTransportFormSheet.test.tsx FE-MOB-TRFRM-011: metadata toBeNull is now
  toEqual({}).
- MTransportFormSheet.test.tsx FE-MOB-TRFRM-027: metadata toBeNull is now
  toEqual({}).
- transportEndpoints.test.ts FE-PLANNER-TRANSPORTEP-014: phone metadata
  toBeNull is now toEqual({}) (title updated to match), and the
  emptyMetadataAsNull field is dropped from DESKTOP_SAVE and PHONE_SAVE.
- useTransportForm.test.ts FE-PLANNER-TRANSPORTFORM-007: metadata: null
  is now metadata: {}.
2026-10-09 17:29:22 +02:00
Maurice 8bd831238d test(server): cover the lease failures, sink errors and store passthroughs
The lease heartbeat and settle failures, the log sink and rotation
fallbacks, the runtime env getters, the public API summary edges, the
admin-only permission level and the WebSocket handler rejection had no
spec, which took six domains under their coverage floor.
2026-10-08 21:15:17 +02:00
Maurice da97d7535f test(server): follow the signatures the session and port work changed
The session gate passes a session lookup to verifyJwtAndLoadUser, the
Immich save takes auto_upload, getAppUrl stays synchronous, the demo
session list marks the current session by the id it is handed, and the
lease no longer needs a transaction of its own, so its spec is gone.
2026-10-08 20:22:16 +02:00
Maurice 37af6f4ff9 test(server): seed bookings and places the way the services store them
The converted specs wrote accommodation_id as a plain id, while the
services store the legacy '<id>.0' text, and wrote read-only foreign key
twins instead of their relations. The files e2e schema gains the trip
columns the entity fills, and the idempotency spec seeds a real user.
2026-10-08 20:22:16 +02:00
Maurice 7ae4df1ca2 test(trips): expect a failed day rebuild to roll the new dates back
TRIP-SVC-072 still pinned the old order, where the trip row committed
before the days were rebuilt. Both now run in one transaction, as
TRIP-SVC-018b already asserts.
2026-10-08 19:21:52 +02:00
Maurice be016f180e test(server): seed the session, lease and rollback specs through the factories
The specs written alongside the session, transaction and lifecycle work
still seeded and read with raw statements. They go through the ORM
factories now; the few that test SQL below the ORM carry a test-sql-allow
reason. The raw SQL baseline holds 177 statements in 6 files.
2026-10-08 19:05:04 +02:00
Maurice 8c66964961 test(server): fit the session, transaction and seeding specs together
The session carry-over specs were written against the old close and
reinitialize calls; they now drive the database backup port. The rollback
specs read through the factories and await the helpers that became async
when the seeding moved to the ORM. Two readers that lost their await and a
duplicated import are fixed as well.
2026-10-08 19:05:04 +02:00
Maurice e90ed065e5 test(server): clear and age fixture rows through the ORM in converted specs
These specs left the raw SQL baseline but still emptied tables with
`db.exec('DELETE FROM ...')` and aged a Dawarich connection with a raw
UPDATE, which the ratchet cannot see because it only counts `.prepare(`.
They now go through deleteRows and updateRows like the rest of the file.

packing_item_recipients and packing_bag_members have no entity; both
cascade from their item and bag, so the packing spec relies on that with
foreign keys on in the snapshot database.
2026-10-08 18:47:25 +02:00
Maurice df10dc6ec2 test(server): assert the row exists before checking its stamped columns
findRow returns null for a missing row, and `row?.deleted_at` is then
undefined, which `.not.toBeNull()` accepts. The converted soft delete,
album sync and passkey counter checks would therefore pass if the row were
removed outright. They now assert the row is there first, as the raw
`.get()` reads they replace did by throwing on a missing row.
2026-10-08 18:47:25 +02:00
Maurice f579f03692 test(server): drop the dashes from two reworded spec comments
The permissions and plugin RPC fallback comments were reworded when
their raw SQL went away; this keeps the new wording in line with the
comment style used elsewhere in the converted specs.
2026-10-08 18:47:24 +02:00
Maurice 6a7b55b13b test(server): seed the reservations service spec through the factories
Converts the raw prepare() fixtures and reads in the reservations service
spec to the ORM factories: expense links, stays, stops, endpoints, day
positions and guest flags are written through insertRow and updateRows,
and the narrow SELECT post-conditions read whole rows through findRow and
findRows, compared with toMatchObject so they still assert exactly the
columns the projection named. stopsOn keeps returning only the three
columns its toEqual assertions compare, and the helpers that now await
become async.

The one write left raw carries a test-sql-allow marker: it plants
dangling references inside the synchronous foreign_keys = OFF window,
which an async ORM write would outlive. The unused canAccessTrip and
isOwner entries are dropped from the database mock.
2026-10-08 18:47:21 +02:00
Maurice a0a584e1df test(server): seed the places service spec through the factories
Converts the raw prepare() fixtures and reads in the places service spec
to the ORM factories: tours, waypoints, files, links, expenses, collection
places and ratings are written through insertRow and updateRows, and what
the service left behind is read back through findRow, findRows and
countRows. The track colour cases insert their seeded tracks with
insertRows in place of INSERT ... RETURNING, tracksOf and the file attach
helper become async, and the stale updated_at is computed with dbNow
instead of datetime('now', '-1 hour'). The rating reads keep comparing
only the rating column, as the narrow SELECT did.
2026-10-08 18:47:12 +02:00
Maurice 40c5eb5c54 test(server): seed the calendar service spec through the factories
Converts the raw prepare() fixtures in the calendar service spec to the
ORM factories: reservation times, metadata and endpoints, stays, stops and
place addresses are written through insertRow and updateRows. The stay
helpers become async, so their callers await them. The timezone cache
case seeds its 1010 bulk bookings and their endpoints with two insertRows
batches instead of a raw transaction. The unused canAccessTrip and
isOwner entries are dropped from the database mock.
2026-10-08 18:47:12 +02:00
Maurice 6c3bb58d59 test(server): seed the auth service spec through the factories
Converts the raw prepare() fixtures and reads in the auth service spec to
the ORM factories: instance settings go through setAppSetting and
deleteRows, MFA, OIDC and invite state through updateRows, and the
password version, push device and MCP token checks read back through
readUser and countRows. The push device helpers become async, so every
caller now awaits them. The unused canAccessTrip and isOwner entries are
dropped from the database mock, since nothing under test imports them.
2026-10-08 18:47:12 +02:00
Maurice 732b951439 test(server): seed the document sync service spec through the factories
Converts the raw prepare() fixtures and reads in the document sync service
spec to the ORM factories and readers. The row builders (bindings, sync
items, files, the connection) and the files service double write through
insertRow and updateRows and are async now, the shape the real service has;
the row readers go through findRow and findRows. Seeding that ran inside
Array.from callbacks is a plain loop, so every row is written before the
run it feeds starts.

Three reads stay raw behind allow markers: the helper that takes a time
from SQLite's own clock and the two checks that compare a stored timestamp
with CURRENT_TIMESTAMP and julianday('now'), since the clock is what they
are about.
2026-10-08 18:47:12 +02:00
Maurice e88e28b79c test(server): seed the user cleanup service spec through the factories
Converts the raw prepare() fixtures and reads in the user cleanup service
spec to the ORM factories and readers: plugins and their per-user config,
journeys with their entries, photos, contributors and share links, trip
share tokens, settlements and memberships are written through insertRow and
makeShareToken, and what the deletion leaves behind is checked through
findRow and countRows. The slim-schema case keeps its single raw users
insert behind an allow marker: that database is hand-rolled and carries no
schema an entity could write to. The trek photo the journey case seeds now
takes its created_at from the column default instead of a literal 0 no
assertion reads.
2026-10-08 18:47:11 +02:00
Maurice 56060188a6 test(server): seed the atlas service spec through the factories
Converts the raw prepare() fixtures and reads in the atlas service spec to
the ORM factories and readers: places, booking endpoints, travellers,
visited countries and regions, cached place regions and bucket list rows
go through makePlace, makeReservationEndpoint, insertRow and upsertRow, and
the region cache is read back through findRow.

The two #2527 cases keep their raw move and delete, with an allow marker:
they land inside the repository call the test intercepts, synchronously on
the call's own connection, which is the race they pin. The database mock
loses its raw canAccessTrip and isOwner, which nothing reads any more.
2026-10-08 18:46:52 +02:00
Maurice efce09f3a2 test(server): seed the memories access service spec through the factories
Converts the raw prepare() fixtures and reads in the memories access
service spec to the ORM factories and readers: shared trip photos, journey
photos and contributors, album links and the trip memberships the access
checks walk are written through insertRow. The legacy MA5 statement stays
raw behind an allow marker as the oracle its parity cases compare with, and
the database mock loses the raw canAccessTrip and isOwner nothing reads.
2026-10-08 18:46:52 +02:00
Maurice 7427a47eea test(server): seed the collab service spec through the factories
Converts the raw prepare() fixtures and reads in the collab service spec to
the ORM factories and readers: reactions, note attachments and the stored
note, message and vote rows go through insertRow, findRow and countRows.
The database mock loses its raw canAccessTrip and isOwner, which nothing
reads any more, and the repository parity cases keep the legacy joins they
are held to behind allow markers.
2026-10-08 18:46:51 +02:00
Maurice fecaa82ad2 test(server): seed the unified memories and admin service specs through the factories
Converts the raw prepare() fixtures and reads in the unified memories
service spec and the admin service spec to the ORM factories and readers.

The memories spec drops the raw canAccessTrip and isOwner on its database
mock, which nothing reads any more; its two parity cases keep the legacy
joins they are held to behind allow markers and build the provider list
they bind through the reader. The admin spec's password reset helpers
(password version, token and push device counts, the stored hash) now read
through the ORM, and the audit log parity case keeps its legacy statement
as an allowed oracle.
2026-10-08 18:46:51 +02:00
Maurice 345d459e5a test(server): seed the files and dawarich sync service specs through the factories
Converts the raw prepare() fixtures and reads in the files service spec and
the dawarich sync service spec to the ORM factories and readers.

The dawarich service stub the sync spec hands in now reads and writes its
connection rows through the ORM and is async, the shape the real service
has. Writes that set accepted_hash from the row's own source_hash read the
row first and write the value back, since a native update cannot reference
another column. The files spec drops the raw canAccessTrip and isOwner on
its database mock, which nothing reads any more, and keeps the legacy
FILE_SELECT statements it is held to behind allow markers.
2026-10-08 18:46:50 +02:00
Maurice 635170a14f test(server): seed the share integration and passkey specs through the factories
Converts the raw prepare() fixtures and reads in the share integration
suite and the passkey service spec to the ORM factories and readers.

The passkey credential helper takes a typed set of overrides instead of a
loose record, so a fixture can no longer hand the insert a column the
entity does not map. The two credentials the listing case backdates are
written with their created_at through insertRows, blobs as Buffers.
2026-10-08 18:46:49 +02:00
Maurice 203eb6a216 test(server): seed the collections MCP and in-app notification specs through the factories
Converts the raw prepare() fixtures and reads in the collections MCP spec
and the in-app notification preference spec to the ORM factories and
readers. The collection, member and saved place helpers and the boolean
notification helper keep their signatures and simply resolve to the new
row's id, so each case reads as it did; the four copies of the boolean
notification INSERT collapse into one helper over makeNotification.
2026-10-08 18:46:49 +02:00
Maurice 9104dab183 test(server): seed the photo cache and dawarich specs through the factories
Converts the raw prepare() fixtures and reads in the trek photo cache and
google place photo cache services and the dawarich e2e to the ORM factories
and readers.

The place photo cache suite moves from its hand-rolled three-table fixture
to the migrated snapshot: the references it checks are now real places on a
trip and real saved places in a collection, seeded through makePlace and
makeCollectionPlace, which a table without names or owners could not hold.
The dawarich e2e drops the raw canAccessTrip on its database mock, which
nothing reads any more.
2026-10-08 18:46:49 +02:00
Maurice 2860148df3 test(server): seed the plugin discovery, permission, feed and stay specs through the factories
Converts the raw prepare() fixtures and reads in the plugin discovery spec,
the permissions service spec, the feeds and invite MCP spec and the day
accommodations repository spec to the ORM factories and readers.

The permission cache cases still write their override rows past
savePermissions, now through setAppSetting, so the stale-cache behaviour
they pin is unchanged. The day accommodations spec keeps the legacy
statements it holds the repository to behind allow markers.
2026-10-08 18:46:48 +02:00
Maurice 855c4e3ba3 test(server): seed the collections, storage registry, transit, token and immich specs through the factories
Converts the raw prepare() fixtures and reads in the collections e2e, the
storage registry service spec, the transit MCP spec, the MCP token
repository spec and the immich memories integration suite to the ORM
factories and readers.

The collections e2e drops the raw canAccessTrip on its database mock, which
nothing reads any more. Reads that used to join trip_photos to trek_photos
in SQL resolve the photo row through the readers instead, and the orphan
trip photo of IMMICH-057 is still written with foreign keys switched off,
now through the ORM on the same connection.
2026-10-08 18:46:48 +02:00
Maurice 8b2e7baacd test(server): seed the trip read model, share token and audit log specs through the factories
Converts the raw prepare() fixtures and reads in the trip read model
service spec and the share token and audit log repository specs to the ORM
factories and readers.

The trip read model spec drops the raw canAccessTrip and isOwner it still
put on the database mock; nothing has read them since trip access moved
onto the repositories. The share token spec now inserts through
makeShareToken and reads what a write left behind through findRow, keeping
the SELECT * row and the legacy statements it is held to as allowed
oracles. The audit log spec keeps the raw row and the typeof() storage
class probe behind allow markers, since both are about what SQLite stores.
2026-10-08 18:46:48 +02:00
Maurice 2529255559 test(server): seed the websocket, push, api key and day specs through the factories
Converts the raw prepare() writes and reads in the websocket connection
suite, the web push and instance API key services, the days MCP spec and
the packing visibility and place details cache repository specs to the ORM
factories and readers.

The websocket suite reads the app's own ORM; the password version bump that
was a single relative UPDATE now reads the user and writes the next
version. The packing visibility parity keeps the legacy fragment it is
checked against behind an allow marker.
2026-10-08 18:46:48 +02:00
Maurice 9de9719a02 test(server): seed the share, quota, photo and stats specs through the factories
Converts the raw prepare() fixtures and reads in the share and place shadow
e2e suites, the google quota, travel stats and MCP guard services, the
memories MCP spec, the admin seeder spec and the assignment participants
repository spec to the ORM factories and readers.

The place shadow e2e moves from hand-rolled tables to the migrated snapshot
with its users pinned to the ids their session cookies are signed for. The
share e2e drops the raw canAccessTrip it still put on the database mock:
nothing has read it since trip access moved onto the repositories. The
quota spec computes yesterday and the retention window in UTC, the same
calendar day SQLite's date('now') gives. The participants oracle keeps its
raw statement behind an allow marker.
2026-10-08 18:46:47 +02:00
Maurice e3982a8e2a test(server): seed the repository, MCP and e2e specs through the factories
Converts the raw prepare() fixtures and reads in the next set of specs to
the ORM factories and readers: the password reset, webauthn, dawarich,
budget, to-do and vacay repository specs, the journey, settings, addons and
share MCP specs, the plugin POI and operator egress specs, the feeds,
storage stats and query helper services, and the packing, collections,
day reorder, leg mode, upload context and trip access integration suites.

The notifications and google quota e2e suites now seed their users through
makeUser with pinned ids, and the notifications suite moves from hand-rolled
tables to the migrated snapshot. Legacy statements that serve as a parity
oracle and the raw rows the primary-relation spike holds toObject() against
stay raw, each with an allow marker naming why.
2026-10-08 18:46:47 +02:00
Maurice 894a5ee781 test(server): seed the small leftover specs through the repositories
Converts the raw prepare() reads and writes in the share, airtrail, files,
todo, dev-link, vacay, tours, bucket list, unit-of-work and plugin guard
specs to the ORM factories and readers, so they go through the entities the
app uses instead of hand-written SQLite.

The vacay e2e moves from hand-rolled tables to the migrated snapshot, with
its user pinned to the id its session cookie is signed for. Statements that
are raw by nature keep the allow marker with the reason: the legacy oracle
of the bucket list parity spec, the PRAGMA probe of the trek_photos
migration, the unmapped probe table of the route import rollback, the
behind-the-ORM reads of the entity insert probes, and seedUser, which also
serves the hand-rolled users table of createTempDb. Comments that quoted a
prepare() call are reworded so the ratchet no longer counts them.
2026-10-08 18:46:47 +02:00
Maurice aec925cffb test(server): seed the journey domain spec through the factories
The journey domain suite now writes its photos, entries, contributors,
trip links, plan edits and place changes and reads back what a call left
through the ORM factories. Three small readers keep the shape a
statement's .get()/.all() handed back: one() answers undefined when no
row matches, pickOne()/pickAll() return just the named columns, and a
count stays { n }, so every toBeDefined, toBeUndefined and toEqual keeps
its meaning. The helpers that wrote or read rows (photoOnEntry, photoOn,
insertEntry, orderOf, daysOf, skeletonFor, skeletonsFor, skeletonsOf,
withGeometry) are async and awaited where they are used, and the M5d
parity seed binds its values in the old column order through columnsOf.

Kept raw and marked: the legacy statements of both repository parity
blocks (JG8, JG14, JG17, JG18, JG34 and the LEG set) and the unguarded
delete the JG119 mutation proof measures the guard against.
2026-10-08 18:46:39 +02:00
Maurice 2a51abaeb8 test(server): seed the budget service db spec through the factories
The DB-backed budget service suite now sets trip currencies, hangs
expenses on bookings, writes places, bookings, trip files, file links
and category order, and reads back items, members, payers, settlements
and links through the ORM factories. Its row readers (paidFlag,
personsOf, memberCount, itemRow, placeRow, settlementRow, totalOf,
rateOf, metadataOf, linksOf) and seeders (tripWithBooking, seedAudTrip)
are async now and awaited where they are read; projected comparisons map
the same keys off the found row.

The database mock drops its dead trip access stand-ins. Kept raw and
marked: the legacy BG10 to BG13 and settlement statements the repository
reads are held to, and the currency switch BUDGET-SVC-DB-066 makes from
inside the stubbed rate fetch.
2026-10-08 18:46:39 +02:00
Maurice 9755c51c35 test(server): seed the days service spec through the factories
The days service suite writes bookings, legs, stays, vias, boundaries
and day edits through the ORM factories and reads them back with
findRow/findRows/countRows. The day, booking and stay readers the cases
compare against (dayRows, reservationRow, staySpan, stopsOn, viasOn,
boundaries) are async now and awaited where they are read; a reader of a
row that is gone fails the case as the old cast did.

The database mock drops its getPlaceWithTags, canAccessTrip and isOwner
stand-ins, which the module stopped exporting when those reads moved
onto the repositories.
2026-10-08 18:46:38 +02:00
Maurice b09f48c4d7 test(server): seed the packing service spec through the factories
The packing service suite writes its items, bags and templates and reads
back what a call left through the ORM factories. Template items are read
category by category instead of through a join, and the trip access
stand-ins the database mock still carried go, as the module no longer
exports them.

The repository parity block keeps its legacy statements raw, marked: the
VISIBLE_TO_ACTOR fragment, the assignee join, the correlated item count,
the category assignees and the export read are the oracles the converted
reads are held to.
2026-10-08 18:46:38 +02:00
Maurice 58cdaf828c test(server): seed the days integration, OIDC and share service specs through the factories
The days integration suite, the OIDC service suite and the share service
suite write their fixtures and read back their effects through the ORM
factories: invite tokens through makeInviteToken, user column edits
through updateRows, bookings, stays, packing items and messages through
insertRow on their entities. Two more database mocks drop their dead
canAccessTrip/isOwner stand-ins.

The OIDC identity-linking cases keep their raw SELECT * of the user row,
marked: they assert that no column of the table moved, including any the
entity might not map.
2026-10-08 18:46:38 +02:00
Maurice 5947631db6 test(server): seed the days, tours, assignments and journey share specs through the factories
The days and tours e2e suites, the assignments service suite and the
journey share suite build their fixtures through the ORM factories and
read back through findRow/findRows/countRows. Fixtures that pin ids (the
trip, day and places a session cookie or a route targets) keep them.
Readers that the old suites called synchronously (order, dayOrder,
viaAnchors, insertJourneyPhoto) are async and awaited, Promise.all where
a case compares several at once.

Left raw and marked: the legacy statements the parity cases hold the
repository reads to, the COLLATE NOCASE lookups that prove the token
match is exact, and total_changes(), which counts writes on the
connection itself.
2026-10-08 18:46:37 +02:00
Maurice 2cf853e504 test(server): seed the atlas and notes tools, day removal, doc sync config and plugin runtime specs through the factories
The atlas and notes MCP tool suites, day removal, the doc sync config
service and the plugin runtime conversion suite build their fixtures and
read back what a call left through the ORM factories. Helpers that used
to answer synchronously off the handle (dayRows, range, the reservation
and boundary readers) are async now and every caller awaits them; a
reader that answered undefined for a gone row still does, so the
toBeUndefined/toBeDefined checks keep their meaning.

The one statement left raw rewinds sqlite_sequence, the engine's own
autoincrement table, which no entity maps; it is marked.
2026-10-08 18:46:37 +02:00
Maurice 2b41a55022 test(server): seed the reminder, roster, Dawarich and plugin specs through the factories
Reminder jobs, user profile, trip members, Dawarich suggestions, the doc
sync controller, the plugin registry and the plugin repository parity
suite now write and read their rows through the ORM factories. The doc
sync controller's itemsForTrip stand-in answers through the two
repository reads DocSyncService makes instead of a copy of their SQL, and
one more database mock drops its dead canAccessTrip/isOwner stand-ins.

What stays raw is marked: the legacy statements the parity cases are
held to, and the places stub in the Dawarich suggestions suite, which
runs inside the service's open transaction and has to write on the
connection that transaction holds.
2026-10-08 18:46:26 +02:00
Maurice 49ba73fcae test(server): seed the atlas, settings, budget and notification specs through the factories
Eleven more suites (atlas and settings e2e, the bucket list, shadow pick
and Dawarich suggestion repositories, the budget and collab MCP tools,
notification preferences, VAPID keys, school holidays and tours) write
and read their rows through the ORM factories. Time-relative fixtures
that leaned on SQLite's date('now', ...) build the same text in JS with
dbNow(), and helpers that returned undefined for a missing row keep doing
so where an assertion depends on it. The legacy statements the parity
cases compare against stay raw, marked as oracles.
2026-10-08 18:46:26 +02:00
Maurice e2ce2ad05c test(server): seed the service, MCP and route specs through the ORM factories
Service, MCP, integration and e2e suites with up to a dozen raw
statements now seed and read through the factories. A buildApp() suite
uses the app's own ORM, a partial-module e2e or a unit suite a
createTestOrm() or sharedTestOrm() handle on its database. Reads that
used to come back undefined now come back null, and the assertions say
so; where a projected SELECT was compared whole, the same columns are
picked off the row.

Two reads inside the scheduler's delivery callback stay synchronous raw
statements, marked, so they keep seeing the row at the moment of
delivery. Two more database mocks lose a canAccessTrip/isOwner stand-in
the module no longer exports.
2026-10-08 18:46:25 +02:00
Maurice b2f2aef016 test(server): seed the small specs through the repositories
The suites with one to three raw statements left now seed and read their
rows through the ORM factories instead of hand-written SQLite on the
better-sqlite3 handle. A legacy statement that serves as a parity oracle,
the schema introspection and the bare backup file keep their raw SQL and
say why with a test-sql-allow marker. Two database mocks still answered a
getPlaceWithTags the module no longer exports; that dead branch goes too,
and the budget seam suite stubs the reservations repository directly
instead of a fake prepare().
2026-10-08 18:46:13 +02:00