Files
HiveGrid-Wireframes/03 Quellen und Referenzen/hivegrid-cloud/apps/api/src/sentry.test.ts
T

128 lines
4.2 KiB
TypeScript

import { afterEach, describe, expect, it } from "vitest";
import {
__getApiSentryConfigForTests,
__resetApiSentryForTests,
apiErrorReporter,
initApiSentry,
parseSampleRate,
readApiSentryConfig,
} from "./sentry.js";
afterEach(() => {
__resetApiSentryForTests();
});
describe("parseSampleRate", () => {
it("returns the fallback for undefined/null/empty values", () => {
expect(parseSampleRate(undefined, 0.5)).toBe(0.5);
expect(parseSampleRate("", 0.25)).toBe(0.25);
});
it("returns the fallback for non-numeric input", () => {
expect(parseSampleRate("abc", 0.3)).toBe(0.3);
});
it("clamps to [0, 1]", () => {
expect(parseSampleRate("-0.5", 0)).toBe(0);
expect(parseSampleRate("1.5", 0)).toBe(1);
});
it("returns parsed numbers in range", () => {
expect(parseSampleRate("0.42", 0)).toBeCloseTo(0.42);
});
});
describe("readApiSentryConfig", () => {
it("defaults to dev sample rate 1.0 when NODE_ENV is not production", () => {
const config = readApiSentryConfig({ NODE_ENV: "development" });
expect(config.tracesSampleRate).toBe(1);
expect(config.profilesSampleRate).toBe(0);
});
it("defaults to prod sample rate 0.1 when NODE_ENV=production", () => {
const config = readApiSentryConfig({ NODE_ENV: "production" });
expect(config.tracesSampleRate).toBe(0.1);
});
it("respects SENTRY_TRACES_SAMPLE_RATE override", () => {
const config = readApiSentryConfig({ NODE_ENV: "production", SENTRY_TRACES_SAMPLE_RATE: "0.5" });
expect(config.tracesSampleRate).toBe(0.5);
});
it("falls back environment to NODE_ENV when SENTRY_ENVIRONMENT is unset", () => {
expect(readApiSentryConfig({ NODE_ENV: "staging" }).environment).toBe("staging");
});
it("prefers SENTRY_ENVIRONMENT over NODE_ENV", () => {
expect(readApiSentryConfig({ NODE_ENV: "production", SENTRY_ENVIRONMENT: "canary" }).environment).toBe("canary");
});
it("treats empty DSN as undefined", () => {
expect(readApiSentryConfig({ SENTRY_DSN_API: "" }).dsn).toBeUndefined();
});
it("forwards the release tag", () => {
expect(readApiSentryConfig({ SENTRY_RELEASE: "abc123" }).release).toBe("abc123");
});
it("falls back to the immutable image revision when SENTRY_RELEASE is empty", () => {
expect(readApiSentryConfig({ SENTRY_RELEASE: "", APP_REVISION: "sha-api" }).release).toBe("sha-api");
});
});
describe("initApiSentry", () => {
it("returns a no-op reporter when DSN is missing", () => {
const reporter = initApiSentry({
dsn: undefined,
environment: "test",
release: undefined,
tracesSampleRate: 0,
profilesSampleRate: 0,
});
expect(typeof reporter.report).toBe("function");
// A no-op reporter must not throw on any event:
expect(() => reporter.report({ component: "api", operation: "test" })).not.toThrow();
expect(__getApiSentryConfigForTests()?.dsn).toBeUndefined();
});
it("returns a sentry-backed reporter when DSN is set, with proper sample rates", () => {
const reporter = initApiSentry({
dsn: "https://example@sentry.invalid/1",
environment: "test",
release: "rev-x",
tracesSampleRate: 0.25,
profilesSampleRate: 0.05,
});
expect(typeof reporter.report).toBe("function");
expect(__getApiSentryConfigForTests()?.tracesSampleRate).toBe(0.25);
expect(__getApiSentryConfigForTests()?.profilesSampleRate).toBe(0.05);
expect(__getApiSentryConfigForTests()?.release).toBe("rev-x");
});
it("is idempotent - subsequent calls reuse the cached reporter", () => {
const first = initApiSentry({
dsn: "https://example@sentry.invalid/1",
environment: "test",
release: undefined,
tracesSampleRate: 0,
profilesSampleRate: 0,
});
const second = initApiSentry({
dsn: "https://different@sentry.invalid/2",
environment: "test",
release: undefined,
tracesSampleRate: 0,
profilesSampleRate: 0,
});
expect(second).toBe(first);
});
});
describe("apiErrorReporter facade", () => {
it("lazily initialises with default config when not initialised", () => {
expect(typeof apiErrorReporter.report).toBe("function");
expect(() => apiErrorReporter.report({ component: "api", operation: "test" })).not.toThrow();
});
});