From d4bc045a28fd65bfc07ad5ea2fa1e2ef2c105087 Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Thu, 7 May 2026 19:09:50 +0200 Subject: [PATCH] =?UTF-8?q?test(features):=20R50=20=E2=80=94=20repo=20cont?= =?UTF-8?q?ract=20suites=20assert=20span=20shape=20per=20method?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- .../users-repository.contract.ts | 39 ++++++++++- .../users.repository.mock.test.ts | 7 +- .../repositories/users.repository.test.ts | 18 ++++-- .../articles-repository.contract.ts | 64 ++++++++++++++++++- .../articles.repository.mock.test.ts | 7 +- .../repositories/articles.repository.test.ts | 17 +++-- .../pages-repository.contract.ts | 28 +++++++- .../site-settings-repository.contract.ts | 16 ++++- .../pages.repository.mock.test.ts | 7 +- .../repositories/pages.repository.test.ts | 17 +++-- .../site-settings.repository.mock.test.ts | 7 +- .../site-settings.repository.test.ts | 17 +++-- .../media-repository.contract.ts | 39 ++++++++++- .../media.repository.mock.test.ts | 7 +- .../repositories/media.repository.test.ts | 17 +++-- .../header-repository.contract.ts | 16 ++++- .../header.repository.mock.test.ts | 7 +- .../repositories/header.repository.test.ts | 17 +++-- 18 files changed, 293 insertions(+), 54 deletions(-) diff --git a/packages/auth/src/__contracts__/users-repository.contract.ts b/packages/auth/src/__contracts__/users-repository.contract.ts index 8c9181f..b0850cb 100644 --- a/packages/auth/src/__contracts__/users-repository.contract.ts +++ b/packages/auth/src/__contracts__/users-repository.contract.ts @@ -1,4 +1,4 @@ -import { it, expect, beforeEach } from "vitest"; +import { it, expect, beforeEach, describe } from "vitest"; import { defineContractSuite } from "@repo/core-testing/contract"; import type { IUsersRepository } from "../application/repositories/users.repository.interface.js"; import { userFactory } from "../__factories__/user.factory.js"; @@ -6,7 +6,7 @@ import { userFactory } from "../__factories__/user.factory.js"; export const usersRepositoryContract = defineContractSuite( "IUsersRepository", - ({ buildSubject }) => { + ({ buildSubject, getTracer }) => { let repo: IUsersRepository; beforeEach(async () => { @@ -52,5 +52,40 @@ export const usersRepositoryContract = expect(created.id).toBe(seed.id); expect(created.username).toBe("carol"); }); + + describe("span emission (R50)", () => { + it("getUser emits users.getUser span with id attribute", async () => { + if (!getTracer) return; + const tracer = getTracer(); + tracer.reset(); + await repo.getUser("nonexistent"); + const span = tracer.findSpan("users.getUser"); + expect(span).toBeDefined(); + expect(span!.op).toBe("repository"); + expect(span!.attributes.id).toBe("nonexistent"); + }); + + it("getUserByUsername emits users.getUserByUsername span", async () => { + if (!getTracer) return; + const tracer = getTracer(); + tracer.reset(); + await repo.getUserByUsername("alice"); + const span = tracer.findSpan("users.getUserByUsername"); + expect(span).toBeDefined(); + expect(span!.op).toBe("repository"); + }); + + it("createUser emits users.createUser span with id attribute", async () => { + if (!getTracer) return; + const tracer = getTracer(); + tracer.reset(); + const seed = userFactory.build({ username: "span-test-user" }); + await repo.createUser(seed); + const span = tracer.findSpan("users.createUser"); + expect(span).toBeDefined(); + expect(span!.op).toBe("repository"); + expect(span!.attributes.id).toBe(seed.id); + }); + }); }, ); diff --git a/packages/auth/src/infrastructure/repositories/users.repository.mock.test.ts b/packages/auth/src/infrastructure/repositories/users.repository.mock.test.ts index 8fb0e5a..0690594 100644 --- a/packages/auth/src/infrastructure/repositories/users.repository.mock.test.ts +++ b/packages/auth/src/infrastructure/repositories/users.repository.mock.test.ts @@ -1,8 +1,13 @@ import { describe } from "vitest"; +import { RecordingTracer } from "@repo/core-testing/instrumentation"; import { MockUsersRepository } from "@/infrastructure/repositories/users.repository.mock"; import { usersRepositoryContract } from "@/__contracts__/users-repository.contract"; describe("MockUsersRepository", () => { + const tracer = new RecordingTracer(); // Start with empty store so contract tests run from a clean slate. - usersRepositoryContract.run(() => new MockUsersRepository([])); + usersRepositoryContract.run( + () => new MockUsersRepository([], tracer), + { tracer: () => tracer }, + ); }); diff --git a/packages/auth/src/infrastructure/repositories/users.repository.test.ts b/packages/auth/src/infrastructure/repositories/users.repository.test.ts index eb880f7..ab93647 100644 --- a/packages/auth/src/infrastructure/repositories/users.repository.test.ts +++ b/packages/auth/src/infrastructure/repositories/users.repository.test.ts @@ -1,4 +1,5 @@ import { describe, vi, beforeEach } from "vitest"; +import { RecordingTracer } from "@repo/core-testing/instrumentation"; import { UsersRepository } from "@/infrastructure/repositories/users.repository"; import { usersRepositoryContract } from "@/__contracts__/users-repository.contract"; import { stubPayloadConfig } from "@repo/core-testing/payload/stub-config"; @@ -48,15 +49,20 @@ function buildPayloadStub() { describe("UsersRepository", () => { describe("contract", () => { + const tracer = new RecordingTracer(); + beforeEach(() => { vi.clearAllMocks(); }); - usersRepositoryContract.run(async () => { - const stub = buildPayloadStub(); - const { getPayload } = await import("payload"); - (getPayload as ReturnType).mockResolvedValue(stub); - return new UsersRepository(stubPayloadConfig); - }); + usersRepositoryContract.run( + async () => { + const stub = buildPayloadStub(); + const { getPayload } = await import("payload"); + (getPayload as ReturnType).mockResolvedValue(stub); + return new UsersRepository(stubPayloadConfig, tracer); + }, + { tracer: () => tracer }, + ); }); }); diff --git a/packages/blog/src/__contracts__/articles-repository.contract.ts b/packages/blog/src/__contracts__/articles-repository.contract.ts index f258852..e00f207 100644 --- a/packages/blog/src/__contracts__/articles-repository.contract.ts +++ b/packages/blog/src/__contracts__/articles-repository.contract.ts @@ -1,4 +1,4 @@ -import { it, expect, beforeEach } from "vitest"; +import { it, expect, beforeEach, describe } from "vitest"; import { defineContractSuite } from "@repo/core-testing/contract"; import type { IArticlesRepository } from "../application/repositories/articles.repository.interface.js"; import { articleFactory } from "../__factories__/article.factory.js"; @@ -6,7 +6,7 @@ import { articleFactory } from "../__factories__/article.factory.js"; export const articlesRepositoryContract = defineContractSuite( "IArticlesRepository", - ({ buildSubject }) => { + ({ buildSubject, getTracer }) => { let repo: IArticlesRepository; beforeEach(async () => { @@ -116,5 +116,65 @@ export const articlesRepositoryContract = }); expect(result).toBeUndefined(); }); + + describe("span emission (R50)", () => { + it("getArticles emits articles.getArticles span with op=repository", async () => { + if (!getTracer) return; + const tracer = getTracer(); + tracer.reset(); + await repo.getArticles({ limit: 5 }); + const span = tracer.findSpan("articles.getArticles"); + expect(span).toBeDefined(); + expect(span!.op).toBe("repository"); + expect(span!.attributes.limit).toBe(5); + }); + + it("getArticle emits articles.getArticle span with id attribute", async () => { + if (!getTracer) return; + const tracer = getTracer(); + tracer.reset(); + await repo.getArticle("nonexistent"); + const span = tracer.findSpan("articles.getArticle"); + expect(span).toBeDefined(); + expect(span!.op).toBe("repository"); + expect(span!.attributes.id).toBe("nonexistent"); + }); + + it("getArticleBySlug emits articles.getArticleBySlug span with slug attribute", async () => { + if (!getTracer) return; + const tracer = getTracer(); + tracer.reset(); + await repo.getArticleBySlug("nonexistent"); + const span = tracer.findSpan("articles.getArticleBySlug"); + expect(span).toBeDefined(); + expect(span!.op).toBe("repository"); + expect(span!.attributes.slug).toBe("nonexistent"); + }); + + it("createArticle emits articles.createArticle span", async () => { + if (!getTracer) return; + const tracer = getTracer(); + tracer.reset(); + const seed = articleFactory.build(); + await repo.createArticle(seed); + const span = tracer.findSpan("articles.createArticle"); + expect(span).toBeDefined(); + expect(span!.op).toBe("repository"); + expect(span!.attributes.slug).toBe(seed.slug); + }); + + it("updateArticle emits articles.updateArticle span", async () => { + if (!getTracer) return; + const tracer = getTracer(); + tracer.reset(); + const seed = articleFactory.build(); + const created = await repo.createArticle(seed); + await repo.updateArticle(created.id, { title: "Updated" }); + const span = tracer.findSpan("articles.updateArticle"); + expect(span).toBeDefined(); + expect(span!.op).toBe("repository"); + expect(span!.attributes.id).toBe(created.id); + }); + }); }, ); diff --git a/packages/blog/src/infrastructure/repositories/articles.repository.mock.test.ts b/packages/blog/src/infrastructure/repositories/articles.repository.mock.test.ts index b556871..311a234 100644 --- a/packages/blog/src/infrastructure/repositories/articles.repository.mock.test.ts +++ b/packages/blog/src/infrastructure/repositories/articles.repository.mock.test.ts @@ -1,7 +1,12 @@ import { describe } from "vitest"; +import { RecordingTracer } from "@repo/core-testing/instrumentation"; import { MockArticlesRepository } from "@/infrastructure/repositories/articles.repository.mock"; import { articlesRepositoryContract } from "@/__contracts__/articles-repository.contract"; describe("MockArticlesRepository", () => { - articlesRepositoryContract.run(() => new MockArticlesRepository()); + const tracer = new RecordingTracer(); + articlesRepositoryContract.run( + () => new MockArticlesRepository(tracer), + { tracer: () => tracer }, + ); }); diff --git a/packages/blog/src/infrastructure/repositories/articles.repository.test.ts b/packages/blog/src/infrastructure/repositories/articles.repository.test.ts index adf375e..b57b55b 100644 --- a/packages/blog/src/infrastructure/repositories/articles.repository.test.ts +++ b/packages/blog/src/infrastructure/repositories/articles.repository.test.ts @@ -1,4 +1,5 @@ import { describe, expect, it, vi, beforeEach } from "vitest"; +import { RecordingTracer } from "@repo/core-testing/instrumentation"; import { ArticlesRepository } from "@/infrastructure/repositories/articles.repository"; import { articlesRepositoryContract } from "@/__contracts__/articles-repository.contract"; import { stubPayloadConfig } from "@repo/core-testing/payload/stub-config"; @@ -102,12 +103,16 @@ vi.mock("payload", () => ({ describe("ArticlesRepository", () => { describe("contract", () => { - articlesRepositoryContract.run(async () => { - const stub = buildPayloadStub(); - const { getPayload } = await import("payload"); - (getPayload as ReturnType).mockResolvedValue(stub); - return new ArticlesRepository(stubPayloadConfig); - }); + const tracer = new RecordingTracer(); + articlesRepositoryContract.run( + async () => { + const stub = buildPayloadStub(); + const { getPayload } = await import("payload"); + (getPayload as ReturnType).mockResolvedValue(stub); + return new ArticlesRepository(stubPayloadConfig, tracer); + }, + { tracer: () => tracer }, + ); }); // ------------------------------------------------------------------------- diff --git a/packages/marketing-pages/src/__contracts__/pages-repository.contract.ts b/packages/marketing-pages/src/__contracts__/pages-repository.contract.ts index c0c9e99..416d4bc 100644 --- a/packages/marketing-pages/src/__contracts__/pages-repository.contract.ts +++ b/packages/marketing-pages/src/__contracts__/pages-repository.contract.ts @@ -1,4 +1,4 @@ -import { it, expect, beforeEach } from "vitest"; +import { it, expect, beforeEach, describe } from "vitest"; import { defineContractSuite } from "@repo/core-testing/contract"; import type { IPagesRepository } from "../application/repositories/pages.repository.interface.js"; import type { Page } from "../entities/models/page.js"; @@ -47,7 +47,7 @@ export const CONTRACT_PAGES_SEED: Page[] = [ export const pagesRepositoryContract = defineContractSuite( "IPagesRepository", - ({ buildSubject }) => { + ({ buildSubject, getTracer }) => { let repo: IPagesRepository; beforeEach(async () => { @@ -95,5 +95,29 @@ export const pagesRepositoryContract = expect(page.status).toBe("draft"); } }); + + describe("span emission (R50)", () => { + it("getPageBySlug emits pages.getPageBySlug span with slug attribute", async () => { + if (!getTracer) return; + const tracer = getTracer(); + tracer.reset(); + await repo.getPageBySlug("about"); + const span = tracer.findSpan("pages.getPageBySlug"); + expect(span).toBeDefined(); + expect(span!.op).toBe("repository"); + expect(span!.attributes.slug).toBe("about"); + }); + + it("getPages emits pages.getPages span with op=repository", async () => { + if (!getTracer) return; + const tracer = getTracer(); + tracer.reset(); + await repo.getPages({ status: "published" }); + const span = tracer.findSpan("pages.getPages"); + expect(span).toBeDefined(); + expect(span!.op).toBe("repository"); + expect(span!.attributes.status).toBe("published"); + }); + }); }, ); diff --git a/packages/marketing-pages/src/__contracts__/site-settings-repository.contract.ts b/packages/marketing-pages/src/__contracts__/site-settings-repository.contract.ts index c0dcd2c..f371d6a 100644 --- a/packages/marketing-pages/src/__contracts__/site-settings-repository.contract.ts +++ b/packages/marketing-pages/src/__contracts__/site-settings-repository.contract.ts @@ -1,4 +1,4 @@ -import { it, expect, beforeEach } from "vitest"; +import { it, expect, beforeEach, describe } from "vitest"; import { defineContractSuite } from "@repo/core-testing/contract"; import type { ISiteSettingsRepository } from "../application/repositories/site-settings.repository.interface.js"; @@ -11,7 +11,7 @@ import type { ISiteSettingsRepository } from "../application/repositories/site-s export const siteSettingsRepositoryContract = defineContractSuite( "ISiteSettingsRepository", - ({ buildSubject }) => { + ({ buildSubject, getTracer }) => { let repo: ISiteSettingsRepository; beforeEach(async () => { @@ -34,5 +34,17 @@ export const siteSettingsRepositoryContract = typeof settings.siteDescription === "string", ).toBe(true); }); + + describe("span emission (R50)", () => { + it("getSiteSettings emits site-settings.getSiteSettings span with op=repository", async () => { + if (!getTracer) return; + const tracer = getTracer(); + tracer.reset(); + await repo.getSiteSettings(); + const span = tracer.findSpan("site-settings.getSiteSettings"); + expect(span).toBeDefined(); + expect(span!.op).toBe("repository"); + }); + }); }, ); diff --git a/packages/marketing-pages/src/infrastructure/repositories/pages.repository.mock.test.ts b/packages/marketing-pages/src/infrastructure/repositories/pages.repository.mock.test.ts index 032e0e3..c1c7b75 100644 --- a/packages/marketing-pages/src/infrastructure/repositories/pages.repository.mock.test.ts +++ b/packages/marketing-pages/src/infrastructure/repositories/pages.repository.mock.test.ts @@ -1,4 +1,5 @@ import { describe } from "vitest"; +import { RecordingTracer } from "@repo/core-testing/instrumentation"; import { MockPagesRepository } from "@/infrastructure/repositories/pages.repository.mock"; import { pagesRepositoryContract, @@ -6,7 +7,11 @@ import { } from "@/__contracts__/pages-repository.contract"; describe("MockPagesRepository", () => { + const tracer = new RecordingTracer(); // Pre-seed with the contract fixtures so the contract assertions find what // they expect (an "about" published page and a "draft-page" draft page). - pagesRepositoryContract.run(() => new MockPagesRepository(CONTRACT_PAGES_SEED)); + pagesRepositoryContract.run( + () => new MockPagesRepository(CONTRACT_PAGES_SEED, tracer), + { tracer: () => tracer }, + ); }); diff --git a/packages/marketing-pages/src/infrastructure/repositories/pages.repository.test.ts b/packages/marketing-pages/src/infrastructure/repositories/pages.repository.test.ts index 175a10d..5381ee1 100644 --- a/packages/marketing-pages/src/infrastructure/repositories/pages.repository.test.ts +++ b/packages/marketing-pages/src/infrastructure/repositories/pages.repository.test.ts @@ -1,4 +1,5 @@ import { describe, vi } from "vitest"; +import { RecordingTracer } from "@repo/core-testing/instrumentation"; import { PagesRepository } from "@/infrastructure/repositories/pages.repository"; import { pagesRepositoryContract, @@ -73,11 +74,15 @@ vi.mock("payload", () => ({ describe("PagesRepository", () => { describe("contract", () => { - pagesRepositoryContract.run(async () => { - const stub = buildPayloadPagesStub(CONTRACT_PAGES_SEED); - const { getPayload } = await import("payload"); - (getPayload as ReturnType).mockResolvedValue(stub); - return new PagesRepository(stubPayloadConfig); - }); + const tracer = new RecordingTracer(); + pagesRepositoryContract.run( + async () => { + const stub = buildPayloadPagesStub(CONTRACT_PAGES_SEED); + const { getPayload } = await import("payload"); + (getPayload as ReturnType).mockResolvedValue(stub); + return new PagesRepository(stubPayloadConfig, tracer); + }, + { tracer: () => tracer }, + ); }); }); diff --git a/packages/marketing-pages/src/infrastructure/repositories/site-settings.repository.mock.test.ts b/packages/marketing-pages/src/infrastructure/repositories/site-settings.repository.mock.test.ts index b0a2c47..7e9e6a2 100644 --- a/packages/marketing-pages/src/infrastructure/repositories/site-settings.repository.mock.test.ts +++ b/packages/marketing-pages/src/infrastructure/repositories/site-settings.repository.mock.test.ts @@ -1,7 +1,12 @@ import { describe } from "vitest"; +import { RecordingTracer } from "@repo/core-testing/instrumentation"; import { MockSiteSettingsRepository } from "@/infrastructure/repositories/site-settings.repository.mock"; import { siteSettingsRepositoryContract } from "@/__contracts__/site-settings-repository.contract"; describe("MockSiteSettingsRepository", () => { - siteSettingsRepositoryContract.run(() => new MockSiteSettingsRepository()); + const tracer = new RecordingTracer(); + siteSettingsRepositoryContract.run( + () => new MockSiteSettingsRepository(tracer), + { tracer: () => tracer }, + ); }); diff --git a/packages/marketing-pages/src/infrastructure/repositories/site-settings.repository.test.ts b/packages/marketing-pages/src/infrastructure/repositories/site-settings.repository.test.ts index 88bb35f..7897bdb 100644 --- a/packages/marketing-pages/src/infrastructure/repositories/site-settings.repository.test.ts +++ b/packages/marketing-pages/src/infrastructure/repositories/site-settings.repository.test.ts @@ -1,4 +1,5 @@ import { describe, vi } from "vitest"; +import { RecordingTracer } from "@repo/core-testing/instrumentation"; import { SiteSettingsRepository } from "@/infrastructure/repositories/site-settings.repository"; import { siteSettingsRepositoryContract } from "@/__contracts__/site-settings-repository.contract"; import { stubPayloadConfig } from "@repo/core-testing/payload/stub-config"; @@ -30,11 +31,15 @@ vi.mock("payload", () => ({ describe("SiteSettingsRepository", () => { describe("contract", () => { - siteSettingsRepositoryContract.run(async () => { - const stub = buildSiteSettingsStub(); - const { getPayload } = await import("payload"); - (getPayload as ReturnType).mockResolvedValue(stub); - return new SiteSettingsRepository(stubPayloadConfig); - }); + const tracer = new RecordingTracer(); + siteSettingsRepositoryContract.run( + async () => { + const stub = buildSiteSettingsStub(); + const { getPayload } = await import("payload"); + (getPayload as ReturnType).mockResolvedValue(stub); + return new SiteSettingsRepository(stubPayloadConfig, tracer); + }, + { tracer: () => tracer }, + ); }); }); diff --git a/packages/media/src/__contracts__/media-repository.contract.ts b/packages/media/src/__contracts__/media-repository.contract.ts index c270f20..60642a6 100644 --- a/packages/media/src/__contracts__/media-repository.contract.ts +++ b/packages/media/src/__contracts__/media-repository.contract.ts @@ -1,4 +1,4 @@ -import { it, expect, beforeEach } from "vitest"; +import { it, expect, beforeEach, describe } from "vitest"; import { defineContractSuite } from "@repo/core-testing/contract"; import type { IMediaRepository } from "../application/repositories/media.repository.interface.js"; import { mediaFactory } from "../__factories__/media.factory.js"; @@ -6,7 +6,7 @@ import { mediaFactory } from "../__factories__/media.factory.js"; export const mediaRepositoryContract = defineContractSuite( "IMediaRepository", - ({ buildSubject }) => { + ({ buildSubject, getTracer }) => { let repo: IMediaRepository; beforeEach(async () => { @@ -57,5 +57,40 @@ export const mediaRepositoryContract = const result = await repo.getMedia(""); expect(result).toBeUndefined(); }); + + describe("span emission (R50)", () => { + it("getMedia emits media.getMedia span with id attribute", async () => { + if (!getTracer) return; + const tracer = getTracer(); + tracer.reset(); + await repo.getMedia("nonexistent"); + const span = tracer.findSpan("media.getMedia"); + expect(span).toBeDefined(); + expect(span!.op).toBe("repository"); + expect(span!.attributes.id).toBe("nonexistent"); + }); + + it("listMedia emits media.listMedia span with op=repository", async () => { + if (!getTracer) return; + const tracer = getTracer(); + tracer.reset(); + await repo.listMedia({ limit: 10 }); + const span = tracer.findSpan("media.listMedia"); + expect(span).toBeDefined(); + expect(span!.op).toBe("repository"); + expect(span!.attributes.limit).toBe(10); + }); + + it("deleteMedia emits media.deleteMedia span with id attribute", async () => { + if (!getTracer) return; + const tracer = getTracer(); + tracer.reset(); + await repo.deleteMedia("nonexistent"); + const span = tracer.findSpan("media.deleteMedia"); + expect(span).toBeDefined(); + expect(span!.op).toBe("repository"); + expect(span!.attributes.id).toBe("nonexistent"); + }); + }); }, ); diff --git a/packages/media/src/infrastructure/repositories/media.repository.mock.test.ts b/packages/media/src/infrastructure/repositories/media.repository.mock.test.ts index a1684d4..c789a66 100644 --- a/packages/media/src/infrastructure/repositories/media.repository.mock.test.ts +++ b/packages/media/src/infrastructure/repositories/media.repository.mock.test.ts @@ -1,7 +1,12 @@ import { describe } from "vitest"; +import { RecordingTracer } from "@repo/core-testing/instrumentation"; import { MockMediaRepository } from "@/infrastructure/repositories/media.repository.mock"; import { mediaRepositoryContract } from "@/__contracts__/media-repository.contract"; describe("MockMediaRepository", () => { - mediaRepositoryContract.run(() => new MockMediaRepository()); + const tracer = new RecordingTracer(); + mediaRepositoryContract.run( + () => new MockMediaRepository(tracer), + { tracer: () => tracer }, + ); }); diff --git a/packages/media/src/infrastructure/repositories/media.repository.test.ts b/packages/media/src/infrastructure/repositories/media.repository.test.ts index b8d59e8..8a3a25a 100644 --- a/packages/media/src/infrastructure/repositories/media.repository.test.ts +++ b/packages/media/src/infrastructure/repositories/media.repository.test.ts @@ -1,4 +1,5 @@ import { describe, expect, it, vi } from "vitest"; +import { RecordingTracer } from "@repo/core-testing/instrumentation"; import { MediaRepository } from "@/infrastructure/repositories/media.repository"; import { mediaRepositoryContract } from "@/__contracts__/media-repository.contract"; import { stubPayloadConfig } from "@repo/core-testing/payload/stub-config"; @@ -58,12 +59,16 @@ vi.mock("payload", () => ({ describe("MediaRepository", () => { describe("contract", () => { - mediaRepositoryContract.run(async () => { - const stub = buildPayloadStub(); - const { getPayload } = await import("payload"); - (getPayload as ReturnType).mockResolvedValue(stub); - return new MediaRepository(stubPayloadConfig); - }); + const tracer = new RecordingTracer(); + mediaRepositoryContract.run( + async () => { + const stub = buildPayloadStub(); + const { getPayload } = await import("payload"); + (getPayload as ReturnType).mockResolvedValue(stub); + return new MediaRepository(stubPayloadConfig, tracer); + }, + { tracer: () => tracer }, + ); }); // ------------------------------------------------------------------------- diff --git a/packages/navigation/src/__contracts__/header-repository.contract.ts b/packages/navigation/src/__contracts__/header-repository.contract.ts index 08b13b0..fecdae4 100644 --- a/packages/navigation/src/__contracts__/header-repository.contract.ts +++ b/packages/navigation/src/__contracts__/header-repository.contract.ts @@ -1,4 +1,4 @@ -import { it, expect, beforeEach } from "vitest"; +import { it, expect, beforeEach, describe } from "vitest"; import { defineContractSuite } from "@repo/core-testing/contract"; import type { IHeaderRepository } from "../application/repositories/header.repository.interface.js"; import type { Header } from "../entities/models/header.js"; @@ -25,7 +25,7 @@ export const CONTRACT_HEADER_SEED: Header = { export const headerRepositoryContract = defineContractSuite( "IHeaderRepository", - ({ buildSubject }) => { + ({ buildSubject, getTracer }) => { let repo: IHeaderRepository; beforeEach(async () => { @@ -71,5 +71,17 @@ export const headerRepositoryContract = header.logoId === undefined || typeof header.logoId === "string", ).toBe(true); }); + + describe("span emission (R50)", () => { + it("getHeader emits header.getHeader span with op=repository", async () => { + if (!getTracer) return; + const tracer = getTracer(); + tracer.reset(); + await repo.getHeader(); + const span = tracer.findSpan("header.getHeader"); + expect(span).toBeDefined(); + expect(span!.op).toBe("repository"); + }); + }); }, ); diff --git a/packages/navigation/src/infrastructure/repositories/header.repository.mock.test.ts b/packages/navigation/src/infrastructure/repositories/header.repository.mock.test.ts index e7e62e9..68ed794 100644 --- a/packages/navigation/src/infrastructure/repositories/header.repository.mock.test.ts +++ b/packages/navigation/src/infrastructure/repositories/header.repository.mock.test.ts @@ -1,7 +1,12 @@ import { describe } from "vitest"; +import { RecordingTracer } from "@repo/core-testing/instrumentation"; import { MockHeaderRepository } from "@/infrastructure/repositories/header.repository.mock"; import { headerRepositoryContract, CONTRACT_HEADER_SEED } from "@/__contracts__/header-repository.contract"; describe("MockHeaderRepository", () => { - headerRepositoryContract.run(() => new MockHeaderRepository(CONTRACT_HEADER_SEED)); + const tracer = new RecordingTracer(); + headerRepositoryContract.run( + () => new MockHeaderRepository(CONTRACT_HEADER_SEED, tracer), + { tracer: () => tracer }, + ); }); diff --git a/packages/navigation/src/infrastructure/repositories/header.repository.test.ts b/packages/navigation/src/infrastructure/repositories/header.repository.test.ts index 954dd60..fedc96b 100644 --- a/packages/navigation/src/infrastructure/repositories/header.repository.test.ts +++ b/packages/navigation/src/infrastructure/repositories/header.repository.test.ts @@ -1,4 +1,5 @@ import { describe, it, expect, vi } from "vitest"; +import { RecordingTracer } from "@repo/core-testing/instrumentation"; import { HeaderRepository } from "@/infrastructure/repositories/header.repository"; import { headerRepositoryContract, CONTRACT_HEADER_SEED } from "@/__contracts__/header-repository.contract"; import { stubPayloadConfig } from "@repo/core-testing/payload/stub-config"; @@ -25,12 +26,16 @@ vi.mock("payload", () => ({ describe("HeaderRepository", () => { describe("contract", () => { - headerRepositoryContract.run(async () => { - const stub = buildHeaderStub(); - const { getPayload } = await import("payload"); - (getPayload as ReturnType).mockResolvedValue(stub); - return new HeaderRepository(stubPayloadConfig); - }); + const tracer = new RecordingTracer(); + headerRepositoryContract.run( + async () => { + const stub = buildHeaderStub(); + const { getPayload } = await import("payload"); + (getPayload as ReturnType).mockResolvedValue(stub); + return new HeaderRepository(stubPayloadConfig, tracer); + }, + { tracer: () => tracer }, + ); }); // ---------------------------------------------------------------------------