mirror of
https://github.com/Horizontal42/summarum.git
synced 2026-11-11 01:02:01 +03:00
add registry tests
This commit is contained in:
parent
6716f1b6da
commit
dcac3ae425
1 changed files with 162 additions and 0 deletions
162
src/engine/registry.test.ts
Normal file
162
src/engine/registry.test.ts
Normal file
|
|
@ -0,0 +1,162 @@
|
||||||
|
import { describe, it, expect, beforeEach } from "vitest";
|
||||||
|
import { buildRegistry, Registry } from "./registry";
|
||||||
|
import { lexLine } from "./lexer";
|
||||||
|
import { Decimal } from "./types";
|
||||||
|
|
||||||
|
describe("registry", () => {
|
||||||
|
let reg: Registry;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
reg = buildRegistry();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("buildRegistry", () => {
|
||||||
|
it("initializes a valid Registry instance with core vocabulary", () => {
|
||||||
|
// Check core words
|
||||||
|
const plusPayload = reg.match(lexLine("plus"), ["plus"], 0);
|
||||||
|
expect(plusPayload).not.toBeNull();
|
||||||
|
expect(plusPayload?.payload.t).toBe("op");
|
||||||
|
if (plusPayload?.payload.t === "op") expect(plusPayload.payload.op).toBe("plus");
|
||||||
|
|
||||||
|
const piPayload = reg.match(lexLine("pi"), ["pi"], 0);
|
||||||
|
expect(piPayload).not.toBeNull();
|
||||||
|
expect(piPayload?.payload.t).toBe("const");
|
||||||
|
if (piPayload?.payload.t === "const") expect(piPayload.payload.name).toBe("pi");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("registers built-in functions", () => {
|
||||||
|
expect(reg.isFunc("sin")).toBe(true);
|
||||||
|
expect(reg.isFunc("sqrt")).toBe(true);
|
||||||
|
expect(reg.isFunc("random")).toBe(true);
|
||||||
|
expect(reg.isFunc("unknownFunc")).toBe(false);
|
||||||
|
|
||||||
|
const sinPayload = reg.match(lexLine("sin"), ["sin"], 0);
|
||||||
|
expect(sinPayload).not.toBeNull();
|
||||||
|
expect(sinPayload?.payload.t).toBe("func");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("registers common units", () => {
|
||||||
|
// "meter"
|
||||||
|
const meterPayload = reg.match(lexLine("meter"), ["meter"], 0);
|
||||||
|
expect(meterPayload).not.toBeNull();
|
||||||
|
expect(meterPayload?.payload.t).toBe("unit");
|
||||||
|
|
||||||
|
// "kg" (strict/lenient)
|
||||||
|
const kgPayload = reg.match(lexLine("kg"), ["kg"], 0);
|
||||||
|
expect(kgPayload).not.toBeNull();
|
||||||
|
expect(kgPayload?.payload.t).toBe("unit");
|
||||||
|
|
||||||
|
// "USD" (strict/lenient currency)
|
||||||
|
const usdPayload = reg.match(lexLine("USD"), ["usd"], 0);
|
||||||
|
expect(usdPayload).not.toBeNull();
|
||||||
|
expect(usdPayload?.payload.t).toBe("currency");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("match", () => {
|
||||||
|
it("matches single-word phrases", () => {
|
||||||
|
const match = reg.match(lexLine("usd"), ["usd"], 0);
|
||||||
|
expect(match).not.toBeNull();
|
||||||
|
expect(match?.length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("matches multi-word phrases", () => {
|
||||||
|
const match = reg.match(lexLine("square meter"), ["square", "meter"], 0);
|
||||||
|
expect(match).not.toBeNull();
|
||||||
|
expect(match?.length).toBe(2);
|
||||||
|
expect(match?.payload.t).toBe("unit");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("respects case-sensitivity", () => {
|
||||||
|
// "m" (milli) vs "M" (mega)
|
||||||
|
const mMatch = reg.match(lexLine("mm"), ["mm"], 0);
|
||||||
|
const mPayload = mMatch?.payload;
|
||||||
|
expect(mPayload?.t).toBe("unit");
|
||||||
|
|
||||||
|
const MMatch = reg.match(lexLine("Mm"), ["mm"], 0);
|
||||||
|
const MPayload = MMatch?.payload;
|
||||||
|
expect(MPayload?.t).toBe("unit");
|
||||||
|
|
||||||
|
if (mPayload?.t === "unit" && MPayload?.t === "unit") {
|
||||||
|
expect(mPayload.unit.id).not.toBe(MPayload.unit.id);
|
||||||
|
expect(mPayload.unit.id).toBe("milli:meter");
|
||||||
|
expect(MPayload.unit.id).toBe("mega:meter");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("respects mustTouch flag", () => {
|
||||||
|
// "log2" vs "log 2"
|
||||||
|
const lex2 = lexLine("log2");
|
||||||
|
const log2Match = reg.match(lex2, lex2.map(l => l.raw), 0);
|
||||||
|
expect(log2Match).not.toBeNull();
|
||||||
|
expect(log2Match?.payload.t).toBe("func");
|
||||||
|
if (log2Match?.payload.t === "func") expect(log2Match.payload.name).toBe("log2");
|
||||||
|
|
||||||
|
const lexSpace2 = lexLine("log 2");
|
||||||
|
const logSpace2Match = reg.match(lexSpace2, lexSpace2.map(l => l.raw), 0);
|
||||||
|
// "log 2" should match "log", length 1, not "log2"
|
||||||
|
expect(logSpace2Match).not.toBeNull();
|
||||||
|
expect(logSpace2Match?.length).toBe(1);
|
||||||
|
if (logSpace2Match?.payload.t === "func") expect(logSpace2Match.payload.name).toBe("log");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns null for no match", () => {
|
||||||
|
const match = reg.match(lexLine("unknownword"), ["unknownword"], 0);
|
||||||
|
expect(match).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("currency and rates", () => {
|
||||||
|
it("sets and updates conversion rates", () => {
|
||||||
|
reg.setRates({ "EUR": 0.9, "GBP": "0.8" });
|
||||||
|
expect(reg.rates.get("EUR")?.toNumber()).toBe(0.9);
|
||||||
|
expect(reg.rates.get("GBP")?.toNumber()).toBe(0.8);
|
||||||
|
// USD is implicitly set to 1
|
||||||
|
expect(reg.rates.get("USD")?.toNumber()).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("ignores negative rates", () => {
|
||||||
|
reg.setRates({ "EUR": 0.9, "INVALID": -1 });
|
||||||
|
expect(reg.rates.get("EUR")?.toNumber()).toBe(0.9);
|
||||||
|
expect(reg.rates.has("INVALID")).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("creates currency unit from rate", () => {
|
||||||
|
const unit = reg.makeCurrencyUnitFromRate("EUR", new Decimal(0.9));
|
||||||
|
expect(unit.id).toBe("EUR");
|
||||||
|
expect(unit.dimension).toBe("currency");
|
||||||
|
// 1 / 0.9 = 1.111...
|
||||||
|
expect(unit.ratio.toNumber()).toBeCloseTo(1.1111, 4);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("makes currency unit if rate exists", () => {
|
||||||
|
reg.setRates({ "EUR": 0.9 });
|
||||||
|
const unit = reg.makeCurrencyUnit("EUR");
|
||||||
|
expect(unit).not.toBeNull();
|
||||||
|
expect(unit?.id).toBe("EUR");
|
||||||
|
|
||||||
|
const noUnit = reg.makeCurrencyUnit("JPY");
|
||||||
|
expect(noUnit).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("completions", () => {
|
||||||
|
it("populates completions with expected categories", () => {
|
||||||
|
expect(reg.completions.length).toBeGreaterThan(0);
|
||||||
|
|
||||||
|
const types = new Set(reg.completions.map(c => c.type));
|
||||||
|
expect(types.has("unit")).toBe(true);
|
||||||
|
expect(types.has("currency")).toBe(true);
|
||||||
|
expect(types.has("function")).toBe(true);
|
||||||
|
expect(types.has("keyword")).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("contains specific known completions", () => {
|
||||||
|
const labels = reg.completions.map(c => c.label);
|
||||||
|
expect(labels).toContain("USD");
|
||||||
|
expect(labels).toContain("meter");
|
||||||
|
expect(labels).toContain("sin");
|
||||||
|
expect(labels).toContain("sum");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Add table
Reference in a new issue