TestOracleSetPr286Regression is the explicit regression case for PR #286. The bug: Set([1,2], 9, "[5]") silently produced [9] (data loss). Correct behavior: either Set returns KeyPathNotFoundError (out-of-range index) WITHOUT modifying the input, or Set succeeds and Get("[5]") returns 9. Top-level
(t *testing.T)
| 619 | // reqproof:proptest parser.Set |
| 620 | // Verifies: SYS-REQ-009 [property] |
| 621 | func TestOracleSetPr286Regression(t *testing.T) { |
| 622 | cases := []struct { |
| 623 | name string |
| 624 | doc string |
| 625 | val string |
| 626 | path []string |
| 627 | fixed bool // true if this case is asserted strictly |
| 628 | knownBug string // non-empty if this case documents an open bug |
| 629 | }{ |
| 630 | {"pr286-top-level-oob", `[1,2]`, `9`, []string{"[5]"}, true, ""}, |
| 631 | {"pr286-top-level-far", `[1,2,3]`, `9`, []string{"[99]"}, true, ""}, |
| 632 | {"pr286-top-level-len", `[1,2,3]`, `9`, []string{"[3]"}, true, ""}, |
| 633 | {"pr286-empty-array-index", `[]`, `9`, []string{"[0]"}, true, ""}, |
| 634 | // STILL-OPEN BUG: Set with nested OOB array index silently destroys |
| 635 | // the parent array. See divergence D7 in the file header. |
| 636 | {"pr286-nested-oob-OPEN-BUG", `{"a":[1,2]}`, `9`, []string{"a", "[99]"}, |
| 637 | false, "Set({\"a\":[1,2]}, 9, \"a\", \"[99]\") returns {\"a\":[9]} (data loss)"}, |
| 638 | } |
| 639 | for _, tc := range cases { |
| 640 | t.Run(tc.name, func(t *testing.T) { |
| 641 | doc := []byte(tc.doc) |
| 642 | var out []byte |
| 643 | var err error |
| 644 | func() { |
| 645 | defer func() { |
| 646 | if rec := recover(); rec != nil { |
| 647 | t.Fatalf("Set panicked: in=%q path=%v val=%q: %v", doc, tc.path, tc.val, rec) |
| 648 | } |
| 649 | }() |
| 650 | assertInputUnchanged(t, doc, func() { |
| 651 | out, err = Set(doc, []byte(tc.val), tc.path...) |
| 652 | }) |
| 653 | }() |
| 654 | |
| 655 | if err != nil { |
| 656 | // Set rejected — contract satisfied. Verify original is intact. |
| 657 | if !json.Valid(doc) { |
| 658 | t.Fatalf("Set rejection corrupted original: doc=%q", doc) |
| 659 | } |
| 660 | if out != nil && !bytes.Equal(out, doc) { |
| 661 | t.Fatalf("Set rejected but mutated bytes: in=%q out=%q", doc, out) |
| 662 | } |
| 663 | return |
| 664 | } |
| 665 | // Set succeeded — output MUST be valid JSON. |
| 666 | if !json.Valid(out) { |
| 667 | t.Fatalf("Set produced invalid JSON: in=%q path=%v val=%q out=%q", |
| 668 | doc, tc.path, tc.val, out) |
| 669 | } |
| 670 | |
| 671 | // THE PR #286 INVARIANT: Get on the same path MUST return the |
| 672 | // set value. If it doesn't, Set silently lost data. |
| 673 | got, _, _, gerr := Get(out, tc.path...) |
| 674 | if gerr != nil { |
| 675 | if tc.knownBug != "" { |
| 676 | t.Logf("KNOWN BUG (open): %s. Path=%v, in=%q, out=%q. "+ |
| 677 | "When this bug is fixed, replace this t.Logf with t.Fatalf.", |
| 678 | tc.knownBug, tc.path, doc, out) |
nothing calls this directly
no test coverage detected