/*
[RFC-1288] Unit tests for strict price marker resolution: resolvePriceMarkersStrict, findDerivedPrice suffix lookup, verbatim unresolved markers, and the no-"0 €"-fallback contract of resolvePriceMarkersForSemantic.
- Not a contract source of truth — the module under test owns the authoritative behavior; fixtures are minimal scaffolding only.
- RFC-1311: Compass header sweep — declares the price suite's contract for test-file scaffold coverage.
*/
import { test, expect, describe } from "vitest";
import {
findDerivedPrice,
offeringIdFromRef,
resolvePriceMarkersForSemantic,
resolvePriceMarkersStrict,
type DerivedPriceEntry,
} from "../price-marker-resolver.ts";
// Deliberately non-warpgogol host — the shared resolver must not depend on a
// site-specific offering URI prefix (RFC-1288 AC-1).
const fixturePrices: Record = {
"https://example.org/id/offerings/digital-foundation": [
{
chargeRef: "monthlySubscription",
targetCurrency: "USD",
amount: { value: "80", currency: "USD" },
trace: {
source: { amount: "70", currency: "EUR" },
rate: { value: "1.1", pair: "EUR/USD" },
},
},
],
};
describe("resolvePriceMarkersStrict", () => {
test("AC-1: resolves a marker keyed by a non-warpgogol host", () => {
const result = resolvePriceMarkersStrict(
"Ab {price:digital-foundation:monthlySubscription} pro Monat",
"de",
fixturePrices,
);
expect(result.text).toBe("Ab 70 € pro Monat");
expect(result.unresolved).toEqual([]);
});
test("AC-2/AC-8: unknown chargeRef stays verbatim and reports unknown-charge", () => {
const marker = "{price:digital-foundation:unknownCharge}";
const result = resolvePriceMarkersStrict(`Preis: ${marker}`, "de", fixturePrices);
expect(result.text).toBe(`Preis: ${marker}`);
expect(result.unresolved).toHaveLength(1);
expect(result.unresolved[0]!.reason).toBe("unknown-charge");
expect(result.unresolved[0]!.marker).toBe(marker);
});
test("unknown offering stays verbatim and reports unknown-offering", () => {
const marker = "{price:no-such-offering:monthly}";
const result = resolvePriceMarkersStrict(marker, "de", fixturePrices);
expect(result.text).toBe(marker);
expect(result.unresolved[0]!.reason).toBe("unknown-offering");
});
test("null derivedPrices reports no-derived-prices", () => {
const marker = "{price:digital-foundation:monthlySubscription}";
const result = resolvePriceMarkersStrict(marker, "de", null);
expect(result.text).toBe(marker);
expect(result.unresolved[0]!.reason).toBe("no-derived-prices");
});
test("two keys ending in the same offering id report ambiguous-offering", () => {
const ambiguous: Record = {
"https://a.example/id/offerings/digital-foundation":
fixturePrices["https://example.org/id/offerings/digital-foundation"]!,
"https://b.example/id/offerings/digital-foundation":
fixturePrices["https://example.org/id/offerings/digital-foundation"]!,
};
const marker = "{price:digital-foundation:monthlySubscription}";
const result = resolvePriceMarkersStrict(marker, "de", ambiguous);
expect(result.text).toBe(marker);
expect(result.unresolved[0]!.reason).toBe("ambiguous-offering");
});
test("missing trace.source.amount reports no-source-amount even when amount.value exists", () => {
const noSource: Record = {
"https://example.org/id/offerings/digital-foundation": [
{
chargeRef: "monthlySubscription",
targetCurrency: "USD",
amount: { value: "80", currency: "USD" },
trace: {
source: undefined as unknown as { amount: string; currency: string },
rate: { value: "1.1", pair: "EUR/USD" },
},
},
],
};
const marker = "{price:digital-foundation:monthlySubscription}";
const result = resolvePriceMarkersStrict(marker, "de", noSource);
expect(result.text).toBe(marker);
expect(result.unresolved[0]!.reason).toBe("no-source-amount");
});
test("empty/whitespace trace.source.amount reports no-source-amount (not 0 €)", () => {
for (const degenerate of ["", " "]) {
const prices: Record = {
"https://example.org/id/offerings/hosting": [
{
chargeRef: "monthly",
targetCurrency: "USD",
amount: { value: "15", currency: "USD" },
trace: {
source: { amount: degenerate, currency: "EUR" },
rate: { value: "1.1", pair: "EUR/USD" },
},
},
],
};
const marker = "{price:hosting:monthly}";
const result = resolvePriceMarkersStrict(marker, "de", prices);
expect(result.text).toBe(marker);
expect(result.unresolved[0]!.reason).toBe("no-source-amount");
}
});
test("{amount:2500} resolves to 2.500 € for de and never fails", () => {
const result = resolvePriceMarkersStrict("Budget {amount:2500}", "de", null);
expect(result.text).toBe("Budget 2.500 €");
expect(result.unresolved).toEqual([]);
});
test("mixed text resolves known markers and reports the rest", () => {
const result = resolvePriceMarkersStrict(
"{price:digital-foundation:monthlySubscription} vs {price:other:x} und {amount:50}",
"de",
fixturePrices,
);
expect(result.text).toBe("70 € vs {price:other:x} und 50 €");
expect(result.unresolved).toHaveLength(1);
expect(result.unresolved[0]!.reason).toBe("unknown-offering");
});
});
describe("resolvePriceMarkersForSemantic (RFC-1288 delegation)", () => {
test("AC-3: unknown offering returns the marker verbatim, not 0 €", () => {
const result = resolvePriceMarkersForSemantic(
"{price:unknown-offering:activation}",
"de",
fixturePrices,
);
expect(result).toBe("{price:unknown-offering:activation}");
expect(result).not.toContain("0");
});
test("known marker resolves identically to the strict resolver", () => {
const text = "Отримайте {price:digital-foundation:monthlySubscription} щомісяця";
expect(resolvePriceMarkersForSemantic(text, "de", fixturePrices)).toBe(
resolvePriceMarkersStrict(text, "de", fixturePrices).text,
);
});
});
describe("findDerivedPrice / offeringIdFromRef", () => {
test("returns the canonical offering ref and all charge matches", () => {
const found = findDerivedPrice(fixturePrices, "digital-foundation", "monthlySubscription");
expect("entry" in found).toBe(true);
if ("entry" in found) {
expect(found.offeringRef).toBe("https://example.org/id/offerings/digital-foundation");
expect(found.matching).toHaveLength(1);
}
});
test("offeringIdFromRef strips the canonical infix and passes bare ids through", () => {
expect(offeringIdFromRef("https://x.test/id/offerings/abc")).toBe("abc");
expect(offeringIdFromRef("abc")).toBe("abc");
});
});