1import { describe, expect, it } from "vitest";
2import {
3 buildRunBundle,
4 formatNumber,
5 generateParamsBlock,
6 getMethod,
7 isPlainStruct,
8 withEdits,
9 LIBRARY_FILES,
10 validateSpec,
11} from "../src/index";
12import { runner, testSpec } from "./helpers";
14describe("source generation and editing", () => {
15 it("formats numbers as exact, readable MATLAB literals", () => {
16 expect(formatNumber(200e9)).toBe("200e9");
17 expect(formatNumber(0.002)).toBe("0.002");
18 expect(formatNumber(44100)).toBe("44100");
19 expect(formatNumber(1e-3)).toBe("0.001");
20 expect(formatNumber(2.5e-7)).toBe("250e-9");
21 expect(formatNumber(7850)).toBe("7850");
22 expect(Number(formatNumber(68.9e9))).toBe(68.9e9);
23 expect(formatNumber(-3)).toBe("-3");
24 });
26 it("round-trips the spec through the params block", async () => {
27 const spec = testSpec();
28 const block = generateParamsBlock(spec);
29 const bundle = {
30 files: [{ name: "p.m", source: block }, ...LIBRARY_FILES],
31 entry: "p.m",
32 resultVariable: "params",
33 };
34 const run = await runner.run({ bundle });
35 expect(run.ok).toBe(true);
36 if (!run.ok || !isPlainStruct(run.value)) return;
37 const plate = run.value.fields.plate;
38 const mat = run.value.fields.material;
39 if (!isPlainStruct(plate) || !isPlainStruct(mat)) throw new Error("params fields are not structs");
40 expect(plate.fields.h).toBe(spec.plate.h);
41 expect(mat.fields.E).toBe(spec.material.E);
42 expect(mat.fields.name).toBe(spec.material.name);
43 });
45 it("runs the edited source, and surfaces numbl errors with file and line", async () => {
46 const spec = testSpec();
47 const bundle = buildRunBundle(spec, getMethod("fd"));
48 const main = bundle.files.find((f) => f.name === "main.m")!.source;
49 const edited = withEdits(bundle, { "main.m": main.replace("result = plate_fd(params);", "result = plate_fd(params)\nresult = undefined_function_xyz(1);") });
50 const run = await runner.run({ bundle: edited });
51 expect(run.ok).toBe(false);
52 if (run.ok) return;
53 expect(run.error).toMatch(/undefined_function_xyz/);
54 expect(run.error).toMatch(/main\.m/);
55 });
57 it("reports a missing result variable rather than an empty screen", async () => {
58 const spec = testSpec();
59 const bundle = buildRunBundle(spec, getMethod("fd"));
60 const edited = withEdits(bundle, { "main.m": "x = 1;" });
61 const run = await runner.run({ bundle: edited });
62 expect(run.ok).toBe(false);
63 if (!run.ok) expect(run.error).toMatch(/result/);
64 });
66 it("validates obviously bad specs", () => {
67 const spec = testSpec();
68 spec.probe.x = 10;
69 spec.fd.cfl = 1.2;
70 const issues = validateSpec(spec);
71 expect(issues.some((i) => i.path === "probe" && i.severity === "error")).toBe(true);
72 expect(issues.some((i) => i.path === "fd.cfl" && i.severity === "warning")).toBe(true);
73 });
74});