# frozen_string_literal: true # Zero-dependency offline tests for the pure ledger reducer. Run: ruby components/backend/test/test_ledger.rb require_relative "../lib/helpdesk/ledger" $pass = 0; $fail = 0 def ok(d); $pass += 1; puts " ok #{d}"; end def bad(d, got = nil); $fail += 1; puts " FAIL #{d}#{got.nil? ? '' : " (got: #{got.inspect})"}"; end def eq(a, b, d); a == b ? ok(d) : bad(d, a); end def truthy(x, d); x ? ok(d) : bad(d, x); end L = Helpdesk::Ledger IDS = (1..9999).to_a def mint; -> { "led#{IDS.shift}" }; end # deterministic tiny vectors so cosine is predictable; entry_vec resolves from the delta that created it. def d(entry_id:, decision:, q: "q", vec: nil, quote: "…") { "entry_id" => entry_id, "question" => q, "decision" => decision, "quote" => quote, "question_vec" => vec } end # resolver: entries created in these tests store their raw vec under vec_ref["_test_vec"] for lookup def resolver; ->(e) { e.dig("vec_ref", "_test_vec") }; end puts "packing round-trips" v = [0.1, -0.25, 1.0, 0.0] eq(L.unpack_vec(L.pack_vec(v)).map { |x| x.round(4) }, v, "pack/unpack Float32 round-trips (to 4dp)") truthy(L.pack_vec(v).is_a?(String) && !L.pack_vec(v).include?("\n"), "packed vec is a single-line string") puts "cosine" eq(L.cosine([1,0], [1,0]), 1.0, "identical -> 1.0") eq(L.cosine([1,0], [0,1]), 0.0, "orthogonal -> 0.0") eq(L.cosine([1,0], [1,2,3]), 0.0, "dim mismatch -> 0.0 (never raises)") puts "canonical entry_id keying" r = L.apply(ledger: [], deltas: [d(entry_id: nil, decision: "no", q: "reset?")], event_id: 1, started_at: "2026-07-01T09:00:00Z", applied_event_ids: [], entry_vec: resolver, next_id: mint) eq(r[:ledger].size, 1, "first delta creates one entry") eq(r[:ledger][0]["decision"], "no", "decision recorded") eq(r[:ledger][0]["last_event_id"], 1, "last_event_id set") truthy(r[:changed], "changed=true on new entry") led = r[:ledger]; eid = led[0]["id"] r2 = L.apply(ledger: led, deltas: [d(entry_id: eid, decision: "no", q: "reset again?")], event_id: 2, started_at: "2026-07-02T09:00:00Z", applied_event_ids: [1], entry_vec: resolver, next_id: mint) eq(r2[:ledger].size, 1, "same entry_id + same decision -> refresh, no new entry") eq(r2[:ledger][0]["last_event_id"], 2, "refresh updates last_event_id") puts "never flip a past no: later reversal supersedes + flags, old kept" r3 = L.apply(ledger: r2[:ledger], deltas: [d(entry_id: eid, decision: "yes")], event_id: 3, started_at: "2026-07-03T09:00:00Z", applied_event_ids: [1, 2], entry_vec: resolver, next_id: mint) eq(r3[:ledger].size, 2, "reversal APPENDS (old retained), never mutates in place") old = r3[:ledger].find { |e| e["id"] == eid } neu = r3[:ledger].find { |e| e["id"] != eid } eq(old["decision"], "no", "old 'no' entry unchanged") eq(old["superseded_by"], neu["id"], "old entry superseded_by the new one") truthy(old["contradiction"] && neu["contradiction"], "both flagged contradiction") eq(neu["decision"], "yes", "new entry carries the new decision") puts "out-of-order (older) reversal does NOT supersede the current decision" r4 = L.apply(ledger: r2[:ledger], deltas: [d(entry_id: eid, decision: "yes")], event_id: 4, started_at: "2026-06-01T09:00:00Z", applied_event_ids: [1, 2], entry_vec: resolver, next_id: mint) older = r4[:ledger].find { |e| e["id"] == eid } eq(older["decision"], "no", "current 'no' stays current when the reversing event is OLDER") truthy(older["superseded_by"].nil?, "older reversal does not supersede") truthy(r4[:ledger].any? { |e| e["contradiction"] }, "older reversal still recorded + flagged for audit") puts "idempotency per event_id" r5 = L.apply(ledger: r2[:ledger], deltas: [d(entry_id: eid, decision: "yes")], event_id: 2, started_at: "2026-07-02T09:00:00Z", applied_event_ids: [1, 2], entry_vec: resolver, next_id: mint) eq(r5[:ledger].size, 1, "re-applying an already-applied event_id is a no-op") truthy(!r5[:changed], "no-op apply reports changed=false") puts "cosine backstop: null entry_id, near-match same decision -> refresh" base = L.apply(ledger: [], deltas: [d(entry_id: nil, decision: "no", vec: [1.0, 0.0])], event_id: 1, started_at: "2026-07-01T09:00:00Z", applied_event_ids: [], entry_vec: nil, next_id: mint)[:ledger] base[0]["vec_ref"] = { "_test_vec" => [1.0, 0.0] } # stash the vec so the resolver can find it r6 = L.apply(ledger: base, deltas: [d(entry_id: nil, decision: "no", vec: [0.98, 0.02])], event_id: 2, started_at: "2026-07-02T09:00:00Z", applied_event_ids: [1], entry_vec: resolver, next_id: mint) eq(r6[:ledger].size, 1, "near-match (cosine>=0.86) same decision -> refresh, no new entry") puts "cosine backstop: near-match DIFFERENT decision -> contradiction" r7 = L.apply(ledger: base, deltas: [d(entry_id: nil, decision: "yes", vec: [0.98, 0.02])], event_id: 3, started_at: "2026-07-03T09:00:00Z", applied_event_ids: [1], entry_vec: resolver, next_id: mint) eq(r7[:ledger].size, 2, "near-match different decision -> append") truthy(r7[:ledger].all? { |e| e["contradiction"] }, "both flagged contradiction") puts "cosine below threshold -> new entry; nil vec -> new + flagged" r8 = L.apply(ledger: base, deltas: [d(entry_id: nil, decision: "no", vec: [0.0, 1.0])], event_id: 4, started_at: "2026-07-04T09:00:00Z", applied_event_ids: [1], entry_vec: resolver, next_id: mint) eq(r8[:ledger].size, 2, "far vector -> distinct new entry") r9 = L.apply(ledger: base, deltas: [d(entry_id: nil, decision: "no", vec: nil)], event_id: 5, started_at: "2026-07-05T09:00:00Z", applied_event_ids: [1], entry_vec: resolver, next_id: mint) neu9 = r9[:ledger].find { |e| e["last_event_id"] == 5 } truthy(neu9 && neu9["contradiction"], "embed-failed (nil vec) new entry flagged for review, never silently merged") puts "unknown/foreign entry_id falls back to the null path (never nil-derefs)" r10 = L.apply(ledger: base, deltas: [d(entry_id: "does-not-exist", decision: "no", vec: [0.0, 1.0])], event_id: 6, started_at: "2026-07-06T09:00:00Z", applied_event_ids: [1], entry_vec: resolver, next_id: mint) eq(r10[:ledger].size, 2, "unknown entry_id -> treated as null -> new entry (no crash)") puts "FIX 6: foreign entry_id + nil vec -> new entry flagged contradiction" r11 = L.apply(ledger: base, deltas: [d(entry_id: "does-not-exist", decision: "no", vec: nil)], event_id: 7, started_at: "2026-07-07T09:00:00Z", applied_event_ids: [1], entry_vec: resolver, next_id: mint) neu11 = r11[:ledger].find { |e| e["last_event_id"] == 7 } truthy(neu11 && neu11["contradiction"], "foreign entry_id + nil question_vec -> new entry flagged contradiction") puts "\n#{$pass} passed, #{$fail} failed" exit($fail.zero? ? 0 : 1)