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.
This commit is contained in:
Maurice
2026-10-08 18:37:16 +02:00
parent df10dc6ec2
commit e90ed065e5
8 changed files with 41 additions and 33 deletions
+16 -12
View File
@@ -34,14 +34,17 @@ import { createTestMikroOrmModule, createTestOrm, type TestOrm } from '../helper
import { db } from '../../src/db/database';
import { dbNow } from '../../src/db/types/db-timestamp.type';
import { PackingBags } from '../../src/db/entities/PackingBags.entity';
import { PackingCategoryAssignees } from '../../src/db/entities/PackingCategoryAssignees.entity';
import { PackingItemContributors } from '../../src/db/entities/PackingItemContributors.entity';
import { PackingItems } from '../../src/db/entities/PackingItems.entity';
import { PackingTemplateCategories } from '../../src/db/entities/PackingTemplateCategories.entity';
import { PackingTemplateItems } from '../../src/db/entities/PackingTemplateItems.entity';
import { PackingTemplates } from '../../src/db/entities/PackingTemplates.entity';
import { TripMembers } from '../../src/db/entities/TripMembers.entity';
import { Trips } from '../../src/db/entities/Trips.entity';
import { makeAdmin, makeUser } from '../helpers/factories/users';
import { makeTrip } from '../helpers/factories/trips';
import { countRows, findRow, findRows, insertRow } from '../helpers/factories/rows';
import { countRows, deleteRows, findRow, findRows, insertRow } from '../helpers/factories/rows';
let orm: TestOrm;
@@ -86,17 +89,18 @@ describe('Packing e2e (real auth guard + real SQL over migrated temp SQLite)', (
});
beforeEach(async () => {
db.exec('DELETE FROM packing_category_assignees');
db.exec('DELETE FROM packing_template_items');
db.exec('DELETE FROM packing_template_categories');
db.exec('DELETE FROM packing_templates');
db.exec('DELETE FROM packing_item_recipients');
db.exec('DELETE FROM packing_item_contributors');
db.exec('DELETE FROM packing_bag_members');
db.exec('DELETE FROM packing_items');
db.exec('DELETE FROM packing_bags');
db.exec('DELETE FROM trip_members');
db.exec('DELETE FROM trips');
await deleteRows(orm, PackingCategoryAssignees);
await deleteRows(orm, PackingTemplateItems);
await deleteRows(orm, PackingTemplateCategories);
await deleteRows(orm, PackingTemplates);
await deleteRows(orm, PackingItemContributors);
// packing_item_recipients and packing_bag_members have no entity of their
// own; both cascade on delete from their item and bag, so the two deletes
// below clear them too (foreign keys are on in the snapshot database).
await deleteRows(orm, PackingItems);
await deleteRows(orm, PackingBags);
await deleteRows(orm, TripMembers);
await deleteRows(orm, Trips);
tripId = await insertTrip('Trip');
checkPermission.mockReturnValue(true);
});
+7 -5
View File
@@ -48,9 +48,11 @@ import { TestUnitOfWorkModule } from '../helpers/test-uow';
import { createTestMikroOrmModule, createTestOrm, type TestOrm } from '../helpers/test-orm';
import { makeUser } from '../helpers/factories/users';
import { makeTrip } from '../helpers/factories/trips';
import { findRow, insertRow } from '../helpers/factories/rows';
import { deleteRows, findRow, insertRow } from '../helpers/factories/rows';
import { Places } from '../../src/db/entities/Places.entity';
import { ShareTokens } from '../../src/db/entities/ShareTokens.entity';
import { TripMembers } from '../../src/db/entities/TripMembers.entity';
import { Trips } from '../../src/db/entities/Trips.entity';
let orm: TestOrm;
@@ -83,10 +85,10 @@ describe('Share-link e2e (real auth guard + real SQL over temp SQLite)', () => {
});
beforeEach(async () => {
db.exec('DELETE FROM share_tokens');
db.exec('DELETE FROM places');
db.exec('DELETE FROM trip_members');
db.exec('DELETE FROM trips');
await deleteRows(orm, ShareTokens);
await deleteRows(orm, Places);
await deleteRows(orm, TripMembers);
await deleteRows(orm, Trips);
tripId = (await makeTrip(orm, 1, { title: 'Trip' })).id;
checkPermission.mockReturnValue(true);
serveKey.mockReset();
+4 -4
View File
@@ -45,7 +45,7 @@ import { db } from '../../src/db/database';
import { AppSettings } from '../../src/db/entities/AppSettings.entity';
import { AuditLog } from '../../src/db/entities/AuditLog.entity';
import { makeAdmin, makeUser } from '../helpers/factories/users';
import { countRows, findRow } from '../helpers/factories/rows';
import { countRows, deleteRows, findRow } from '../helpers/factories/rows';
let orm: TestOrm;
@@ -86,9 +86,9 @@ describe('Storage admin e2e (real auth + admin guard + managed guard + migrated
// STORE2E-004 that expects to see fresh defaults must call GET (which
// renders the registry's live snapshot) rather than assume the deleted rows
// reset it — the registry only re-reads app_settings on `reload()`/init.
beforeEach(() => {
db.exec("DELETE FROM app_settings WHERE key LIKE 'storage.%'");
db.exec('DELETE FROM audit_log');
beforeEach(async () => {
await deleteRows(orm, AppSettings, { key: { $like: 'storage.%' } });
await deleteRows(orm, AuditLog);
});
afterAll(async () => {
@@ -3,7 +3,7 @@ import { createSnapshotTestDb } from '../../../helpers/db-mock';
import { resetTestDb } from '../../../helpers/test-db';
import { createTestOrm, type TestOrm } from '../../../helpers/test-orm';
import { createUser } from '../../../helpers/factories';
import { countRows, findRow, insertRow, upsertRow } from '../../../helpers/factories/rows';
import { countRows, deleteRows, findRow, insertRow, updateRows, upsertRow } from '../../../helpers/factories/rows';
import { DawarichVisitSuggestions } from '../../../../src/db/entities/DawarichVisitSuggestions.entity';
import { DawarichConnections } from '../../../../src/db/entities/DawarichConnections.entity';
import type { DawarichConnectionsRepository } from '../../../../src/db/repositories/DawarichConnections.repository';
@@ -16,10 +16,10 @@ beforeAll(async () => {
t = await createTestOrm(testDb);
connections = t.repo(DawarichConnections);
});
beforeEach(() => {
beforeEach(async () => {
resetTestDb(testDb);
t.clear();
testDb.exec('DELETE FROM dawarich_connections');
await deleteRows(t, DawarichConnections);
});
afterAll(async () => {
await t.close();
@@ -147,7 +147,7 @@ describe('DawarichConnectionsRepository', () => {
it('CONNREPO-032: updated_at is bumped unconditionally, insert or merge alike', async () => {
const { user } = createUser(testDb);
await connections.upsertConnection(user.id, { url: 'https://d.example', allowInsecureTls: false, syncEnabled: true });
testDb.exec(`UPDATE dawarich_connections SET updated_at = '2000-01-01 00:00:00'`);
await updateRows(t, DawarichConnections, {}, { updated_at: '2000-01-01 00:00:00' });
await connections.upsertConnection(user.id, { url: 'https://d.example', allowInsecureTls: false, syncEnabled: false });
@@ -27,7 +27,7 @@ import { createUser, createTrip, addTripMember } from '../../helpers/factories';
import { invalidatePermissionsCache } from '../../../src/nest/permissions/permissions-cache';
import { createMcpHarness, parseToolResult, type McpHarness } from '../../helpers/mcp-harness';
import { createTestOrm, type TestOrm } from '../../helpers/test-orm';
import { countRows, findRow, insertRow, updateRows } from '../../helpers/factories/rows';
import { countRows, deleteRows, findRow, insertRow, updateRows } from '../../helpers/factories/rows';
import { AppSettings } from '../../../src/db/entities/AppSettings.entity';
import { AuditLog } from '../../../src/db/entities/AuditLog.entity';
import { TripInviteTokens } from '../../../src/db/entities/TripInviteTokens.entity';
@@ -77,7 +77,7 @@ beforeEach(async () => {
// trip_invite_tokens is not in the shared helper's RESET_TABLES, and trip ids
// restart after a DELETE-based reset, so a leftover row would attach itself
// to the next test's trip.
testDb.exec('DELETE FROM trip_invite_tokens');
await deleteRows(orm, TripInviteTokens);
// The permission levels are cached in module state that resetTestDb knows
// nothing about, so the one test that lowers share_manage would otherwise
// keep the lowered level alive for every test after it.
@@ -252,8 +252,8 @@ beforeEach(async () => {
// RESET_TABLES in tests/helpers/test-db.ts predates this domain and does not
// list its two tables; foreign keys are off during the reset, so rows would
// otherwise outlive their user and leak into the next case.
testDb.exec('DELETE FROM dawarich_visit_suggestions');
testDb.exec('DELETE FROM dawarich_connections');
await deleteRows(t, DawarichVisitSuggestions);
await deleteRows(t, DawarichConnections);
t.clear();
vi.clearAllMocks();
probeCapabilities.mockResolvedValue(CAPABILITIES);
@@ -31,7 +31,7 @@ describe('GoogleQuotaService', () => {
});
beforeEach(async () => {
db.exec('DELETE FROM google_api_usage');
await deleteRows(await sharedTestOrm(db), GoogleApiUsage);
await deleteRows(await sharedTestOrm(db), AppSettings, { key: GOOGLE_DAILY_LIMIT_SETTING });
// A fresh service per case, so the once-a-day warning state starts clean.
quota = new GoogleQuotaService(appSettings, usage);
@@ -44,7 +44,7 @@ import type { PlacesRepository } from '../../../../src/db/repositories/Places.re
import { CollectionPlaces } from '../../../../src/db/entities/CollectionPlaces.entity';
import type { CollectionPlacesRepository } from '../../../../src/db/repositories/CollectionPlaces.repository';
import { makeStorageFixture, type StorageFixture } from '../../../helpers/storage-fixture';
import { findRow, insertRow, updateRows } from '../../../helpers/factories/rows';
import { deleteRows, findRow, insertRow, updateRows } from '../../../helpers/factories/rows';
import { makeUser } from '../../../helpers/factories/users';
import { makeTrip } from '../../../helpers/factories/trips';
import { makePlace } from '../../../helpers/factories/places';
@@ -122,8 +122,10 @@ describe.each([
cache = new PlacePhotoCacheService(fx.storage, metaRepo, placesRepo, collectionPlacesRepo);
});
beforeEach(() => {
testDb.exec('DELETE FROM places; DELETE FROM collection_places; DELETE FROM google_place_photo_meta;');
beforeEach(async () => {
await deleteRows(t, Places);
await deleteRows(t, CollectionPlaces);
await deleteRows(t, GooglePlacePhotoMeta);
t.clear();
for (const f of fs.readdirSync(fx.root)) {
if (f === '.tmp') continue;