TestEachKeyMultiPathInvariants closes the EachKey multi-path blind spot deterministically. EachKey's purpose is to resolve MULTIPLE key paths in one traversal; the existing fuzz harnesses test only single-path. This test verifies the multi-path callback contract: each path that matches fires the cal
(t *testing.T)
| 784 | // |
| 785 | // Verifies: SYS-REQ-008 (EachKey multi-path traversal correctness). |
| 786 | func TestEachKeyMultiPathInvariants(t *testing.T) { |
| 787 | data := []byte(`{"a":1,"b":2,"nested":{"x":10,"y":20},"arr":[100,200,300]}`) |
| 788 | paths := [][]string{ |
| 789 | {"a"}, |
| 790 | {"b"}, |
| 791 | {"missing_top"}, |
| 792 | {"nested", "x"}, |
| 793 | {"nested", "missing_nested"}, |
| 794 | {"nested", "y"}, |
| 795 | {"arr", "[1]"}, |
| 796 | } |
| 797 | matched := make(map[int]int) // idx → number of times callback fired |
| 798 | callbackCount := 0 |
| 799 | EachKey(data, func(idx int, value []byte, vt ValueType, err error) { |
| 800 | matched[idx]++ |
| 801 | callbackCount++ |
| 802 | // No-error invariant on matched paths. |
| 803 | if err != nil { |
| 804 | t.Errorf("EachKey callback for path %d returned error %v (data=%q)", |
| 805 | idx, err, data) |
| 806 | } |
| 807 | if len(value) == 0 { |
| 808 | t.Errorf("EachKey callback for path %d returned empty value (data=%q)", |
| 809 | idx, data) |
| 810 | } |
| 811 | }, paths...) |
| 812 | |
| 813 | // Each matching path should fire at most once. (We can't assert "exactly |
| 814 | // once" without knowing which paths matched, but we CAN assert no path |
| 815 | // fires more than once — that would be a state-leakage bug.) |
| 816 | for idx, count := range matched { |
| 817 | if count > 1 { |
| 818 | t.Errorf("EachKey path %d fired %d times (expected at most 1): data=%q paths=%v", |
| 819 | idx, count, data, paths) |
| 820 | } |
| 821 | } |
| 822 | // Sanity: at least the trivially-matching paths must have fired. |
| 823 | if callbackCount == 0 { |
| 824 | t.Errorf("EachKey multi-path: callback never fired despite some paths matching: data=%q paths=%v", |
| 825 | data, paths) |
| 826 | } |
| 827 | } |