import math import re import unittest from pathlib import Path ROOT = Path(__file__).resolve().parents[1] CALCULATORS_TS = ROOT / "hdyc-svelte" / "src" / "lib" / "data" / "calculators.ts" def _js_fmt(n: float) -> str: """Mimics the fmt() function in engine.ts""" if not math.isfinite(n): return str(n) if n == 0: return "0" if abs(n) < 1e-6: return f"{n:.6e}".replace("e-0", "e-").replace("e+0", "e+") # engine.ts uses parseFloat(n.toFixed(6)).toString() rounded = round(n, 6) if rounded == 0: # Handle -0.0 return "0" if rounded == int(rounded): return str(int(rounded)) return str(rounded) def _js_fmt_precise(n: float) -> str: """Mimics formatExampleValue in QuickConversionExample.svelte""" if n is None or math.isnan(n): return "—" if not math.isfinite(n): return str(n) if n == 0: return "0" rounded = round(n, 6) if rounded != 0: if rounded == int(rounded): return str(int(rounded)) return str(rounded) # Precise version for very small numbers precise = f"{n:.12f}".rstrip('0').rstrip('.') return precise if precise else "0" class TestCalculatorsConsistency(unittest.TestCase): def test_standard_calculators_consistency(self): text = CALCULATORS_TS.read_text(encoding="utf-8") # Extract the calculators array content match = re.search(r"export const calculators: CalculatorDef\[\] = \[(.*?)\];", text, re.S) self.assertTrue(match, "Could not find calculators array in calculators.ts") body = match.group(1) # Split by '{"slug":' to avoid splitting on nested braces raw_entries = body.split('{"slug":') errors = [] for raw_entry in raw_entries: if not raw_entry.strip(): continue entry = '{"slug":' + raw_entry slug_match = re.search(r'"slug": "(.*?)"', entry) if not slug_match: continue slug = slug_match.group(1) type_match = re.search(r'"type": "(.*?)"', entry) if not type_match or type_match.group(1) != "standard": continue # Use non-greedy search for factor/offset and handle potential whitespace factor_match = re.search(r'"factor":\s*([0-9.eE+-]+)', entry) offset_match = re.search(r'"offset":\s*([0-9.eE+-]+)', entry) factor = float(factor_match.group(1)) if factor_match else 1.0 offset = float(offset_match.group(1)) if offset_match else 0.0 # 1. Formula Hint vs Chart Row 1 Consistency # Logic: solve(config, 1, "1") -> val2 = fmt(1 * factor + offset) row_one_output = _js_fmt(1.0 * factor + offset) # 2. How to convert Examples Consistency (Inverse) if factor != 0: reverse_val = (1.0 - offset) / factor formatted_reverse = _js_fmt_precise(reverse_val) # Specific check for Réaumur to Kelvin (User request) if slug == "reaumur-to-kelvin": if formatted_reverse != "-217.72": errors.append(f"[{slug}] Reverse example mismatch: expected -217.72, got {formatted_reverse}") if row_one_output != "274.4": errors.append(f"[{slug}] Chart row 1 mismatch: expected 274.4, got {row_one_output}") # Specific check for Feet per minute to Knots (Previous bug fix) if slug == "feet-per-minute-to-knots": if row_one_output != "0.009875": errors.append(f"[{slug}] Chart row 1 mismatch: expected 0.009875, got {row_one_output}") if formatted_reverse != "101.268504": errors.append(f"[{slug}] Reverse example mismatch: expected 101.268504, got {formatted_reverse}") if errors: self.fail("\n" + "\n".join(errors)) if __name__ == "__main__": unittest.main()