feat(core-testing): RecordingLogger + ./instrumentation subpath
This commit is contained in:
@@ -7,6 +7,7 @@
|
|||||||
".": "./src/index.ts",
|
".": "./src/index.ts",
|
||||||
"./factory": "./src/factory/index.ts",
|
"./factory": "./src/factory/index.ts",
|
||||||
"./contract": "./src/contract/index.ts",
|
"./contract": "./src/contract/index.ts",
|
||||||
|
"./instrumentation": "./src/instrumentation/index.ts",
|
||||||
"./react": "./src/react/index.ts",
|
"./react": "./src/react/index.ts",
|
||||||
"./payload": "./src/payload/index.ts",
|
"./payload": "./src/payload/index.ts",
|
||||||
"./payload/stub-config": "./src/payload/stub-config.ts",
|
"./payload/stub-config": "./src/payload/stub-config.ts",
|
||||||
@@ -20,6 +21,7 @@
|
|||||||
"test": "vitest run"
|
"test": "vitest run"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@repo/core-shared": "workspace:*",
|
||||||
"@testing-library/jest-dom": "^6.5.0",
|
"@testing-library/jest-dom": "^6.5.0",
|
||||||
"@testing-library/react": "^16.0.0",
|
"@testing-library/react": "^16.0.0",
|
||||||
"@testing-library/user-event": "^14.5.0",
|
"@testing-library/user-event": "^14.5.0",
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
export * from "./factory/index.js";
|
export * from "./factory/index.js";
|
||||||
export * from "./contract/index.js";
|
export * from "./contract/index.js";
|
||||||
|
export * from "./instrumentation/index.js";
|
||||||
|
|||||||
2
packages/core-testing/src/instrumentation/index.ts
Normal file
2
packages/core-testing/src/instrumentation/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export { RecordingTracer, type RecordedSpan } from "./recording-tracer";
|
||||||
|
export { RecordingLogger, type RecordedCapture } from "./recording-logger";
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import { describe, it, expect } from "vitest";
|
||||||
|
import { RecordingLogger } from "@/instrumentation/recording-logger";
|
||||||
|
|
||||||
|
describe("RecordingLogger", () => {
|
||||||
|
it("records captureException calls (err + ctx)", () => {
|
||||||
|
const logger = new RecordingLogger();
|
||||||
|
const err = new Error("x");
|
||||||
|
logger.captureException(err, { tags: { feature: "blog" } });
|
||||||
|
expect(logger.captures).toHaveLength(1);
|
||||||
|
expect(logger.captures[0]).toMatchObject({
|
||||||
|
kind: "exception",
|
||||||
|
err,
|
||||||
|
ctx: { tags: { feature: "blog" } },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("records captureMessage calls", () => {
|
||||||
|
const logger = new RecordingLogger();
|
||||||
|
logger.captureMessage("hello", "warning", { extras: { foo: 1 } });
|
||||||
|
expect(logger.captures[0]).toMatchObject({
|
||||||
|
kind: "message",
|
||||||
|
message: "hello",
|
||||||
|
level: "warning",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("records breadcrumbs", () => {
|
||||||
|
const logger = new RecordingLogger();
|
||||||
|
logger.addBreadcrumb({ category: "test", message: "x", level: "info" });
|
||||||
|
expect(logger.breadcrumbs).toHaveLength(1);
|
||||||
|
expect(logger.breadcrumbs[0]!.category).toBe("test");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("records setUser calls", () => {
|
||||||
|
const logger = new RecordingLogger();
|
||||||
|
logger.setUser({ id: "u1" });
|
||||||
|
logger.setUser(null);
|
||||||
|
expect(logger.users).toEqual([{ id: "u1" }, null]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("reset() clears all recordings", () => {
|
||||||
|
const logger = new RecordingLogger();
|
||||||
|
logger.captureException(new Error("x"));
|
||||||
|
logger.addBreadcrumb({ category: "c", message: "m" });
|
||||||
|
logger.setUser({ id: "u" });
|
||||||
|
logger.reset();
|
||||||
|
expect(logger.captures).toHaveLength(0);
|
||||||
|
expect(logger.breadcrumbs).toHaveLength(0);
|
||||||
|
expect(logger.users).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("findCapture returns first capture matching predicate", () => {
|
||||||
|
const logger = new RecordingLogger();
|
||||||
|
logger.captureException(new Error("first"));
|
||||||
|
logger.captureException(new Error("second"));
|
||||||
|
const found = logger.findCapture(
|
||||||
|
(c) => c.kind === "exception" && (c.err as Error).message === "second",
|
||||||
|
);
|
||||||
|
expect(found).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import type {
|
||||||
|
ILogger,
|
||||||
|
Breadcrumb,
|
||||||
|
CaptureContext,
|
||||||
|
} from "@repo/core-shared/instrumentation";
|
||||||
|
|
||||||
|
export type RecordedCapture =
|
||||||
|
| { kind: "exception"; err: unknown; ctx?: CaptureContext }
|
||||||
|
| { kind: "message"; message: string; level?: "info" | "warning" | "error"; ctx?: CaptureContext };
|
||||||
|
|
||||||
|
export class RecordingLogger implements ILogger {
|
||||||
|
captures: RecordedCapture[] = [];
|
||||||
|
breadcrumbs: Breadcrumb[] = [];
|
||||||
|
users: Array<{ id: string } | null> = [];
|
||||||
|
|
||||||
|
captureException(err: unknown, ctx?: CaptureContext): void {
|
||||||
|
this.captures.push({ kind: "exception", err, ctx });
|
||||||
|
}
|
||||||
|
|
||||||
|
captureMessage(
|
||||||
|
message: string,
|
||||||
|
level?: "info" | "warning" | "error",
|
||||||
|
ctx?: CaptureContext,
|
||||||
|
): void {
|
||||||
|
this.captures.push({ kind: "message", message, level, ctx });
|
||||||
|
}
|
||||||
|
|
||||||
|
addBreadcrumb(b: Breadcrumb): void {
|
||||||
|
this.breadcrumbs.push(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
setUser(user: { id: string } | null): void {
|
||||||
|
this.users.push(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
reset(): void {
|
||||||
|
this.captures = [];
|
||||||
|
this.breadcrumbs = [];
|
||||||
|
this.users = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
findCapture(
|
||||||
|
predicate: (c: RecordedCapture) => boolean,
|
||||||
|
): RecordedCapture | undefined {
|
||||||
|
return this.captures.find(predicate);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user