// Feature-level test: exercises the full slice // use case factory chain → mock repo // Tests the full dependency chain via direct injection (no container rebinding). import { describe, expect, it } from "vitest"; import { MockMediaRepository } from "../src/infrastructure/repositories/media.repository.mock"; import { getMediaUseCase } from "../src/application/use-cases/get-media.use-case"; import { listMediaUseCase } from "../src/application/use-cases/list-media.use-case"; import { deleteMediaUseCase } from "../src/application/use-cases/delete-media.use-case"; import { getMediaController } from "../src/interface-adapters/controllers/get-media.controller"; import { listMediaController } from "../src/interface-adapters/controllers/list-media.controller"; import { deleteMediaController } from "../src/interface-adapters/controllers/delete-media.controller"; import { MediaNotFoundError } from "../src/entities/errors/media"; import { mediaFactory } from "../src/__factories__/media.factory"; describe("media feature: end-to-end via direct injection", () => { function buildChain() { const repo = new MockMediaRepository(); const getUC = getMediaUseCase(repo); const listUC = listMediaUseCase(repo); const deleteUC = deleteMediaUseCase(repo); const getCtrl = getMediaController(getUC); const listCtrl = listMediaController(listUC); const deleteCtrl = deleteMediaController(deleteUC); return { repo, getCtrl, listCtrl, deleteCtrl }; } it("listMedia returns empty array initially", async () => { const { listCtrl } = buildChain(); const result = await listCtrl({}); expect(result).toHaveLength(0); }); it("stores media and retrieves it by id", async () => { const { repo, getCtrl } = buildChain(); const seed = mediaFactory.build({ id: "feat-1" }); await repo._store(seed); const result = await getCtrl({ id: "feat-1" }); expect(result.id).toBe("feat-1"); expect(result.alt).toBe(seed.alt); }); it("listMedia returns all stored media", async () => { const { repo, listCtrl } = buildChain(); await repo._store(mediaFactory.build()); await repo._store(mediaFactory.build()); await repo._store(mediaFactory.build()); const result = await listCtrl({}); expect(result).toHaveLength(3); }); it("deleteMedia removes media so it can no longer be retrieved", async () => { const { repo, getCtrl, deleteCtrl } = buildChain(); const seed = mediaFactory.build({ id: "feat-del" }); await repo._store(seed); await deleteCtrl({ id: "feat-del" }); await expect(getCtrl({ id: "feat-del" })).rejects.toThrow(MediaNotFoundError); }); it("getMediaController throws MediaNotFoundError for missing id", async () => { const { getCtrl } = buildChain(); await expect(getCtrl({ id: "does-not-exist" })).rejects.toBeInstanceOf(MediaNotFoundError); }); it("deleteMediaController throws MediaNotFoundError for missing id", async () => { const { deleteCtrl } = buildChain(); await expect(deleteCtrl({ id: "does-not-exist" })).rejects.toBeInstanceOf(MediaNotFoundError); }); it("listMedia with limit respects pagination", async () => { const { repo, listCtrl } = buildChain(); for (let i = 0; i < 5; i++) { await repo._store(mediaFactory.build()); } const result = await listCtrl({ limit: 3 }); expect(result).toHaveLength(3); }); });