From 7bcf3801985ef3a4510c39ef0f013e18de7e8fcb Mon Sep 17 00:00:00 2001 From: Nicolas Meienberger Date: Mon, 16 Feb 2026 23:20:33 +0100 Subject: [PATCH] test: fix impossible test scenarios --- .../__tests__/backups.execution.test.ts | 26 ++++++++++++++++--- .../backups/__tests__/backups.queries.test.ts | 2 ++ .../backups/__tests__/backups.service.test.ts | 4 +-- app/test/helpers/backup.ts | 21 +++++++++++---- app/test/setup.ts | 1 + 5 files changed, 43 insertions(+), 11 deletions(-) diff --git a/app/server/modules/backups/__tests__/backups.execution.test.ts b/app/server/modules/backups/__tests__/backups.execution.test.ts index cc585238..5d01d1e3 100644 --- a/app/server/modules/backups/__tests__/backups.execution.test.ts +++ b/app/server/modules/backups/__tests__/backups.execution.test.ts @@ -12,6 +12,7 @@ import * as context from "~/server/core/request-context"; import * as spawnModule from "~/server/utils/spawn"; import { restic } from "~/server/utils/restic"; import { NotFoundError, BadRequestError } from "http-errors-enhanced"; +import { scheduleQueries } from "../backups.queries"; const resticBackupMock = mock(() => Promise.resolve({ exitCode: 0, summary: generateBackupOutput(), error: "" })); const resticForgetMock = mock(() => Promise.resolve({ success: true, data: null })); @@ -55,12 +56,21 @@ describe("backup execution - validation failures", () => { test("should fail backup when volume does not exist", async () => { // arrange + const volume = await createTestVolume(); const repository = await createTestRepository(); const schedule = await createTestBackupSchedule({ - volumeId: 99999, + volumeId: volume.id, repositoryId: repository.id, }); + const hydratedSchedule = await scheduleQueries.findById(schedule.id, TEST_ORG_ID); + expect(hydratedSchedule).toBeDefined(); + const scheduleWithoutVolume = { + ...hydratedSchedule!, + volume: null, + } as unknown as NonNullable>>; + spyOn(scheduleQueries, "findById").mockResolvedValueOnce(scheduleWithoutVolume); + // act const result = await backupsExecutionService.validateBackupExecution(schedule.id); @@ -76,11 +86,20 @@ describe("backup execution - validation failures", () => { test("should fail backup when repository does not exist", async () => { // arrange const volume = await createTestVolume(); + const repository = await createTestRepository(); const schedule = await createTestBackupSchedule({ volumeId: volume.id, - repositoryId: "non-existent-repo", + repositoryId: repository.id, }); + const hydratedSchedule = await scheduleQueries.findById(schedule.id, TEST_ORG_ID); + expect(hydratedSchedule).toBeDefined(); + const scheduleWithoutRepository = { + ...hydratedSchedule!, + repository: null, + } as unknown as NonNullable>>; + spyOn(scheduleQueries, "findById").mockResolvedValueOnce(scheduleWithoutRepository); + // act const result = await backupsExecutionService.validateBackupExecution(schedule.id); @@ -216,14 +235,13 @@ describe("retention policy - runForget", () => { test("should throw NotFoundError when repository does not exist", async () => { // arrange const schedule = await createTestBackupSchedule({ - repositoryId: "non-existent-repo", retentionPolicy: { keepHourly: 24, }, }); // act & assert - expect(backupsExecutionService.runForget(schedule.id)).rejects.toThrow("Repository not found"); + expect(backupsExecutionService.runForget(schedule.id, "non-existent-repo")).rejects.toThrow("Repository not found"); }); }); diff --git a/app/server/modules/backups/__tests__/backups.queries.test.ts b/app/server/modules/backups/__tests__/backups.queries.test.ts index 2427cc3a..2a8d6464 100644 --- a/app/server/modules/backups/__tests__/backups.queries.test.ts +++ b/app/server/modules/backups/__tests__/backups.queries.test.ts @@ -3,6 +3,7 @@ import { scheduleQueries } from "../backups.queries"; import { createTestBackupSchedule } from "~/test/helpers/backup"; import { createTestVolume } from "~/test/helpers/volume"; import { createTestRepository } from "~/test/helpers/repository"; +import { createTestOrganization } from "~/test/helpers/organization"; import { TEST_ORG_ID } from "~/test/helpers/organization"; import { faker } from "@faker-js/faker"; @@ -140,6 +141,7 @@ describe("scheduleQueries.findExecutable", () => { test("should not return schedules from other organizations", async () => { // arrange const otherOrgId = faker.string.uuid(); + await createTestOrganization({ id: otherOrgId }); const schedule = await createTestBackupSchedule({ volumeId: volume.id, repositoryId: repository.id, diff --git a/app/server/modules/backups/__tests__/backups.service.test.ts b/app/server/modules/backups/__tests__/backups.service.test.ts index 2dbf05cf..3dbbbde7 100644 --- a/app/server/modules/backups/__tests__/backups.service.test.ts +++ b/app/server/modules/backups/__tests__/backups.service.test.ts @@ -236,7 +236,7 @@ describe("listSchedules", () => { }); describe("cleanupOrphanedSchedules", () => { - test("should delete orphaned schedules and their mirror assignments", async () => { + test("should return zero when cascades already removed orphaned schedules", async () => { const healthyVolume = await createTestVolume(); const healthyRepository = await createTestRepository(); const healthySchedule = await createTestBackupSchedule({ @@ -262,7 +262,7 @@ describe("cleanupOrphanedSchedules", () => { const cleanupResult = await backupsService.cleanupOrphanedSchedules(); - expect(cleanupResult.deletedSchedules).toBeGreaterThanOrEqual(1); + expect(cleanupResult.deletedSchedules).toBe(0); const deletedSchedule = await db.query.backupSchedulesTable.findFirst({ where: { id: orphanSchedule.id }, diff --git a/app/test/helpers/backup.ts b/app/test/helpers/backup.ts index 59256086..5e257262 100644 --- a/app/test/helpers/backup.ts +++ b/app/test/helpers/backup.ts @@ -1,18 +1,29 @@ import { db } from "~/server/db/db"; import { faker } from "@faker-js/faker"; import { backupSchedulesTable, type BackupScheduleInsert } from "~/server/db/schema"; -import { ensureTestOrganization, TEST_ORG_ID } from "./organization"; +import { createTestOrganization, ensureTestOrganization, TEST_ORG_ID } from "./organization"; +import { createTestVolume } from "./volume"; +import { createTestRepository } from "./repository"; export const createTestBackupSchedule = async (overrides: Partial = {}) => { - await ensureTestOrganization(); + const organizationId = overrides.organizationId ?? TEST_ORG_ID; + + if (organizationId === TEST_ORG_ID) { + await ensureTestOrganization(); + } else { + await createTestOrganization({ id: organizationId }); + } + + const volumeId = overrides.volumeId ?? (await createTestVolume({ organizationId })).id; + const repositoryId = overrides.repositoryId ?? (await createTestRepository({ organizationId })).id; const backup: BackupScheduleInsert = { name: faker.system.fileName(), cronExpression: "0 0 * * *", - repositoryId: "repo_123", - volumeId: 1, + repositoryId, + volumeId, shortId: faker.string.uuid(), - organizationId: TEST_ORG_ID, + organizationId, ...overrides, }; diff --git a/app/test/setup.ts b/app/test/setup.ts index 9760a5db..85ec2b11 100644 --- a/app/test/setup.ts +++ b/app/test/setup.ts @@ -25,4 +25,5 @@ void mock.module("~/server/utils/crypto", () => ({ beforeAll(async () => { const migrationsFolder = path.join(cwd(), "app", "drizzle"); migrate(db, { migrationsFolder }); + db.run("PRAGMA foreign_keys = ON;"); });