import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { TRPCError } from "@trpc/server"; import { injectable } from "inversify"; import { mediaContainer } from "@/di/container"; import { MediaModule } from "@/di/module"; import { MEDIA_SYMBOLS } from "@/di/symbols"; import { getMediaUseCase } from "@/application/use-cases/get-media.use-case"; import { listMediaUseCase } from "@/application/use-cases/list-media.use-case"; import { deleteMediaUseCase } from "@/application/use-cases/delete-media.use-case"; import { getMediaController } from "@/interface-adapters/controllers/get-media.controller"; import { listMediaController } from "@/interface-adapters/controllers/list-media.controller"; import { deleteMediaController } from "@/interface-adapters/controllers/delete-media.controller"; import { mediaRouter } from "@/integrations/api/router"; describe("mediaRouter", () => { beforeEach(() => { mediaContainer.unbindAll(); mediaContainer.load(MediaModule); }); afterEach(() => { mediaContainer.unbindAll(); }); it("exposes getMedia, listMedia, deleteMedia procedures", () => { const names = Object.keys(mediaRouter._def.procedures); expect(names).toContain("getMedia"); expect(names).toContain("listMedia"); expect(names).toContain("deleteMedia"); }); it("listMedia returns empty array by default", async () => { const caller = mediaRouter.createCaller({}); const result = await caller.listMedia({}); expect(result).toEqual([]); }); }); describe("mediaRouter error mapping", () => { beforeEach(() => { mediaContainer.unbindAll(); mediaContainer.load(MediaModule); }); afterEach(() => { mediaContainer.unbindAll(); }); it("translates MediaNotFoundError → NOT_FOUND when id is missing", async () => { const caller = mediaRouter.createCaller({}); try { await caller.getMedia({ id: "nonexistent-id" }); throw new Error("expected throw"); } catch (e) { expect(e).toBeInstanceOf(TRPCError); expect((e as TRPCError).code).toBe("NOT_FOUND"); } }); it("translates zod parse failure → BAD_REQUEST on getMedia with empty id", async () => { const caller = mediaRouter.createCaller({}); try { await caller.getMedia({} as unknown as { id: string }); throw new Error("expected throw"); } catch (e) { expect(e).toBeInstanceOf(TRPCError); expect((e as TRPCError).code).toBe("BAD_REQUEST"); } }); it("translates MediaNotFoundError → NOT_FOUND on deleteMedia with missing id", async () => { const caller = mediaRouter.createCaller({}); try { await caller.deleteMedia({ id: "nonexistent-id" }); throw new Error("expected throw"); } catch (e) { expect(e).toBeInstanceOf(TRPCError); expect((e as TRPCError).code).toBe("NOT_FOUND"); } }); it("translates MediaNotFoundError → NOT_FOUND via NullMediaRepository", async () => { @injectable() class NullMediaRepository { async getMedia() { return undefined as never; } async listMedia() { return [] as never; } async deleteMedia() { return; } } mediaContainer.unbindAll(); mediaContainer.bind(MEDIA_SYMBOLS.IMediaRepository).to(NullMediaRepository); mediaContainer .bind(MEDIA_SYMBOLS.IGetMediaUseCase) .toDynamicValue((ctx) => getMediaUseCase(ctx.container.get(MEDIA_SYMBOLS.IMediaRepository)), ); mediaContainer .bind(MEDIA_SYMBOLS.IListMediaUseCase) .toDynamicValue((ctx) => listMediaUseCase(ctx.container.get(MEDIA_SYMBOLS.IMediaRepository)), ); mediaContainer .bind(MEDIA_SYMBOLS.IDeleteMediaUseCase) .toDynamicValue((ctx) => deleteMediaUseCase(ctx.container.get(MEDIA_SYMBOLS.IMediaRepository)), ); mediaContainer .bind(MEDIA_SYMBOLS.IGetMediaController) .toDynamicValue((ctx) => getMediaController(ctx.container.get(MEDIA_SYMBOLS.IGetMediaUseCase)), ); mediaContainer .bind(MEDIA_SYMBOLS.IListMediaController) .toDynamicValue((ctx) => listMediaController(ctx.container.get(MEDIA_SYMBOLS.IListMediaUseCase)), ); mediaContainer .bind(MEDIA_SYMBOLS.IDeleteMediaController) .toDynamicValue((ctx) => deleteMediaController( ctx.container.get(MEDIA_SYMBOLS.IDeleteMediaUseCase), ), ); const caller = mediaRouter.createCaller({}); try { await caller.getMedia({ id: "any-id" }); throw new Error("expected throw"); } catch (e) { expect(e).toBeInstanceOf(TRPCError); expect((e as TRPCError).code).toBe("NOT_FOUND"); } }); });