Initial Commit
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
* Spec-conformance pins for SegmenterFormatter — Zod rejection paths,
|
||||
* Intl.RangeError mapping, OTel emission, and cache reuse semantics.
|
||||
*/
|
||||
|
||||
import { I18N_CODES, I18nError } from "@suaveplan/i18n-shared";
|
||||
import { describe, expect, expectTypeOf, it } from "@suaveplan/testing/runner";
|
||||
import {
|
||||
clearSegmenterCache,
|
||||
SegmenterFormatter,
|
||||
segmentText,
|
||||
} from "./formatter/formatter.js";
|
||||
import type {
|
||||
SegmenterFormatterOptions,
|
||||
SegmenterGranularity,
|
||||
} from "./formatter.types.js";
|
||||
import {
|
||||
getSegmenterMetricsState,
|
||||
resetSegmenterMetricsState,
|
||||
} from "./observability/observability.js";
|
||||
|
||||
describe("SegmenterFormatter spec-conformance", () => {
|
||||
it("rejects non-object options shape (no path → field='options')", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
try {
|
||||
// @ts-expect-error wrong type for safeParse
|
||||
segmentText("a", "en-GB", "not-an-object");
|
||||
expect.fail("expected throw");
|
||||
} catch (err) {
|
||||
expect(err).toBeInstanceOf(I18nError);
|
||||
expect((err as I18nError).code).toBe(
|
||||
I18N_CODES.INVALID_FORMATTER_OPTIONS
|
||||
);
|
||||
expect((err as I18nError).context).toMatchObject({
|
||||
field: "options",
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects unknown granularity with INVALID_FORMATTER_OPTIONS", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
try {
|
||||
new SegmenterFormatter({ granularity: "magic" as never });
|
||||
expect.fail("expected throw");
|
||||
} catch (err) {
|
||||
expect(err).toBeInstanceOf(I18nError);
|
||||
expect((err as I18nError).code).toBe(
|
||||
I18N_CODES.INVALID_FORMATTER_OPTIONS
|
||||
);
|
||||
expect((err as I18nError).context).toMatchObject({
|
||||
field: "granularity",
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it("maps Intl RangeError to UNSUPPORTED_LOCALE", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
clearSegmenterCache();
|
||||
try {
|
||||
segmentText("a", "en US");
|
||||
expect.fail("expected throw");
|
||||
} catch (err) {
|
||||
expect(err).toBeInstanceOf(I18nError);
|
||||
expect((err as I18nError).code).toBe(I18N_CODES.UNSUPPORTED_LOCALE);
|
||||
}
|
||||
});
|
||||
|
||||
it("reuses cached Intl.Segmenter on identical key", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
clearSegmenterCache();
|
||||
resetSegmenterMetricsState();
|
||||
segmentText("a", "en-GB");
|
||||
segmentText("a", "en-GB");
|
||||
const state = getSegmenterMetricsState();
|
||||
expect(state.cacheHitSamples).toHaveLength(1);
|
||||
expect(state.cacheHitSamples[0]?.attributes.formatter).toBe(
|
||||
"segmenter"
|
||||
);
|
||||
});
|
||||
|
||||
it("does not reuse cache across distinct granularities", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
clearSegmenterCache();
|
||||
resetSegmenterMetricsState();
|
||||
segmentText("a b", "en-GB", { granularity: "grapheme" });
|
||||
segmentText("a b", "en-GB", { granularity: "word" });
|
||||
const state = getSegmenterMetricsState();
|
||||
expect(state.cacheHitSamples).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("records a duration_ms sample per call with locale + granularity", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
clearSegmenterCache();
|
||||
resetSegmenterMetricsState();
|
||||
segmentText("hello", "en-GB", { granularity: "word" });
|
||||
const state = getSegmenterMetricsState();
|
||||
expect(state.durationSamples).toHaveLength(1);
|
||||
expect(state.durationSamples[0]?.attributes).toMatchObject({
|
||||
locale: "en-GB",
|
||||
granularity: "word",
|
||||
});
|
||||
});
|
||||
|
||||
it("defaults granularity to 'grapheme' when omitted", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
clearSegmenterCache();
|
||||
resetSegmenterMetricsState();
|
||||
segmentText("ab", "en-GB");
|
||||
const state = getSegmenterMetricsState();
|
||||
expect(state.durationSamples[0]?.attributes.granularity).toBe(
|
||||
"grapheme"
|
||||
);
|
||||
});
|
||||
|
||||
it("granularity field of options matches SegmenterGranularity", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
expectTypeOf<SegmenterFormatterOptions["granularity"]>().toEqualTypeOf<
|
||||
SegmenterGranularity | undefined
|
||||
>();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user