refactor(features): rename mock/payload/interface files per Lazar pattern

Convention now: <name>.repository.{ts,mock.ts,interface.ts}.
Renames .mock prefix to .mock suffix; drops .payload prefix from real
impls (canonical name = real impl); dot-separates the .repository
qualifier in interface filenames. Class names follow suit:
PayloadXRepository → XRepository; Mock* unchanged.

Refactor log: §1, §3
Spec: §9.1

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-05 23:50:01 +02:00
parent a4c4ca6b6e
commit aa325f91cc
71 changed files with 193 additions and 148 deletions

View File

@@ -0,0 +1,163 @@
import { describe, expect, it, vi, beforeEach } from "vitest";
import { ArticlesRepository } from "@/infrastructure/repositories/articles.repository";
import { articlesRepositoryContract } from "@/__contracts__/articles-repository.contract";
import { stubPayloadConfig } from "@repo/core-testing/payload/stub-config";
// ---------------------------------------------------------------------------
// In-memory Payload stub used by both the contract suite and impl-specific tests
// ---------------------------------------------------------------------------
function buildPayloadStub() {
const store = new Map<string, Record<string, unknown>>();
return {
create: vi.fn(
async ({
data,
}: {
collection: string;
data: Record<string, unknown>;
overrideAccess?: boolean;
}) => {
// Payload assigns an id; here we require the data to carry one
// (the repository passes `id` via the mapped domain object implicitly
// through the Article — we expose the mapped doc back from createArticle).
// The stub returns the data as-is so the mapDoc function can work.
const doc = { id: `stub-${store.size + 1}`, ...data };
store.set(String(doc.id), doc);
return doc;
},
),
find: vi.fn(
async ({
where,
limit,
}: {
collection: string;
where?: {
slug?: { equals: string };
status?: { equals: string };
author?: { equals: string };
};
limit?: number;
page?: number;
overrideAccess?: boolean;
}) => {
let docs = Array.from(store.values());
if (where?.slug) {
docs = docs.filter((d) => d.slug === where.slug?.equals);
}
if (where?.status) {
docs = docs.filter((d) => d.status === where.status?.equals);
}
if (where?.author) {
docs = docs.filter((d) => d.author === where.author?.equals);
}
if (limit !== undefined) {
docs = docs.slice(0, limit);
}
return { docs };
},
),
findByID: vi.fn(
async ({ id }: { collection: string; id: string; overrideAccess?: boolean }) => {
const doc = store.get(String(id));
if (!doc) throw new Error(`Not found: ${id}`);
return doc;
},
),
update: vi.fn(
async ({
id,
data,
}: {
collection: string;
id: string;
data: Record<string, unknown>;
overrideAccess?: boolean;
}) => {
const existing = store.get(String(id));
if (!existing) throw new Error(`Not found: ${id}`);
const updated = { ...existing, ...data };
store.set(String(id), updated);
return updated;
},
),
};
}
vi.mock("payload", () => ({
getPayload: vi.fn(),
}));
// ---------------------------------------------------------------------------
// Contract suite
// ---------------------------------------------------------------------------
describe("ArticlesRepository", () => {
describe("contract", () => {
articlesRepositoryContract.run(async () => {
const stub = buildPayloadStub();
const { getPayload } = await import("payload");
(getPayload as ReturnType<typeof vi.fn>).mockResolvedValue(stub);
return new ArticlesRepository(stubPayloadConfig);
});
});
// -------------------------------------------------------------------------
// Impl-specific tests: Payload doc → domain mapping
// -------------------------------------------------------------------------
describe("Payload doc → domain mapping", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("maps a Payload doc to a domain Article on getArticleBySlug", async () => {
const { getPayload } = await import("payload");
const findMock = vi.fn().mockResolvedValue({
docs: [
{
id: "p-123",
title: "Hello",
slug: "hello",
content: { type: "doc", children: [] },
status: "published",
author: "u1",
createdAt: "2026-05-04T12:00:00.000Z",
updatedAt: "2026-05-04T12:00:00.000Z",
},
],
});
(getPayload as ReturnType<typeof vi.fn>).mockResolvedValue({
find: findMock,
});
const repo = new ArticlesRepository(stubPayloadConfig);
const result = await repo.getArticleBySlug("hello");
expect(findMock).toHaveBeenCalledWith({
collection: "articles",
where: { slug: { equals: "hello" } },
limit: 1,
overrideAccess: true,
});
expect(result?.id).toBe("p-123");
expect(result?.slug).toBe("hello");
expect(result?.status).toBe("published");
expect(result?.authorId).toBe("u1");
expect(result?.createdAt).toBeInstanceOf(Date);
});
it("returns undefined when slug is not found", async () => {
const { getPayload } = await import("payload");
(getPayload as ReturnType<typeof vi.fn>).mockResolvedValue({
find: vi.fn().mockResolvedValue({ docs: [] }),
});
const repo = new ArticlesRepository(stubPayloadConfig);
const result = await repo.getArticleBySlug("missing");
expect(result).toBeUndefined();
});
});
});