test(catalog): assert loadError is reported to Sentry (deduped)

Mock @sentry/nestjs and assert that a getCategoryWithParts loadError drives the
redis dedup key + Sentry.captureMessage("…drill-load-error…"), so the silent-
failure reporting can't regress unnoticed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-09 17:43:39 +03:00
parent e8e9771759
commit 9b9986a149

View File

@@ -1,7 +1,12 @@
import { NotFoundException } from "@nestjs/common";
import * as Sentry from "@sentry/nestjs";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { CategoriesService } from "./categories.service";
// The catalog-degradation reporter calls Sentry.captureMessage; stub it so we can
// assert silent UX failures are reported without hitting the real SDK.
vi.mock("@sentry/nestjs", () => ({ captureMessage: vi.fn() }));
function createService(db: any) {
const redis = {
getJson: vi.fn().mockResolvedValue(null),
@@ -293,15 +298,27 @@ describe("CategoriesService", () => {
execute: vi.fn().mockResolvedValue([]),
};
const { service } = createService(db);
const { service, redis } = createService(db);
// Drill comes back empty (transient upstream failure).
vi.spyOn(service, "getChildren").mockResolvedValue([] as any);
(Sentry.captureMessage as ReturnType<typeof vi.fn>).mockClear();
const result = await service.getCategoryWithParts("psa2");
expect(service.getChildren).toHaveBeenCalledWith("psa2");
expect((result as { loadError?: boolean }).loadError).toBe(true);
expect((result as { children?: unknown }).children).toBeUndefined();
// The silent loadError is reported to Sentry (deduped via redis.set) so it
// can't go unnoticed the way serkan's empty catalog did.
expect(redis.set).toHaveBeenCalledWith(
expect.stringContaining("sentry:cat-degraded:drill-load-error:"),
"1",
3600,
);
expect(Sentry.captureMessage).toHaveBeenCalledTimes(1);
expect((Sentry.captureMessage as ReturnType<typeof vi.fn>).mock.calls[0][0]).toContain(
"drill-load-error",
);
});
});