mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 13:22:14 +00:00
When a recommendation carries a `referenceId` pointing into the
curated medical-reference bundle, render a small footnote underneath
linking to the guideline. Format: "Source: ESH 2023 — ESH 2023
Guidelines for the Management of Arterial Hypertension" (locale-
routed title), with a tiny external-link icon, opens in a new tab via
target="_blank" rel="noreferrer". Per the v1.4.16 research doc §7
the URL never opens in an in-app webview.
`InsightResult.recommendations` widens to accept either the legacy
plain-string shape OR a structured `{ text, referenceId? }` object so
existing rendering paths keep working while the new citation surface
plugs in through the same component. `getMedicalReferenceById()`
returns undefined for unknown ids — the UI silently drops the
footnote in that case rather than rendering a broken link (defence
in depth on top of the schema validation).
i18n keys `insights.recommendation.source` and
`insights.recommendation.viewSource` land in both EN and DE.
6 component tests pin the no-footnote / EN-locale / DE-locale /
unknown-id-defence / multi-rec paths.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
58 lines
1.7 KiB
TypeScript
58 lines
1.7 KiB
TypeScript
import { describe, expect, it, beforeEach, vi, afterEach } from "vitest";
|
|
import {
|
|
clearLogBuffer,
|
|
readLogBuffer,
|
|
getLogBufferSize,
|
|
} from "../in-memory-buffer";
|
|
import { emitEvent } from "../transports";
|
|
import { resetLoggingConfig } from "../config";
|
|
import type { WideEvent } from "../types";
|
|
|
|
function makeEvent(overrides: Partial<WideEvent> = {}): WideEvent {
|
|
return {
|
|
timestamp: new Date().toISOString(),
|
|
duration_ms: 42,
|
|
request_id: "req-buf-test",
|
|
trace_id: "trace-buf-test",
|
|
level: "info",
|
|
kind: "http",
|
|
service: "healthlog",
|
|
environment: "test",
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
describe("emitEvent → in-memory buffer wiring", () => {
|
|
let stdoutSpy: ReturnType<typeof vi.spyOn>;
|
|
|
|
beforeEach(() => {
|
|
clearLogBuffer();
|
|
resetLoggingConfig();
|
|
// Suppress the JSON line so the test runner stays readable.
|
|
stdoutSpy = vi.spyOn(process.stdout, "write").mockReturnValue(true);
|
|
});
|
|
|
|
afterEach(() => {
|
|
stdoutSpy.mockRestore();
|
|
});
|
|
|
|
it("pushes the emitted event into the in-memory buffer", () => {
|
|
expect(getLogBufferSize()).toBe(0);
|
|
emitEvent(makeEvent({ trace_id: "trace-A" }));
|
|
emitEvent(makeEvent({ trace_id: "trace-B" }));
|
|
expect(getLogBufferSize()).toBe(2);
|
|
const events = readLogBuffer({});
|
|
// Newest-first: trace-B was pushed second.
|
|
expect(events[0].trace_id).toBe("trace-B");
|
|
expect(events[1].trace_id).toBe("trace-A");
|
|
});
|
|
|
|
it("still writes to stdout (transport contract preserved)", () => {
|
|
emitEvent(makeEvent());
|
|
expect(stdoutSpy).toHaveBeenCalledTimes(1);
|
|
const arg = stdoutSpy.mock.calls[0][0];
|
|
expect(typeof arg).toBe("string");
|
|
expect(arg as string).toContain("trace-buf-test");
|
|
});
|
|
});
|