TestCleanOneOfMessage tests that oneOf error messages are simplified to plain English
(t *testing.T)
| 11 | |
| 12 | // TestCleanOneOfMessage tests that oneOf error messages are simplified to plain English |
| 13 | func TestCleanOneOfMessage(t *testing.T) { |
| 14 | tests := []struct { |
| 15 | name string |
| 16 | input string |
| 17 | wantNot []string // substrings that must NOT appear in output |
| 18 | wantAny []string // at least one of these must appear in output |
| 19 | }{ |
| 20 | { |
| 21 | name: "engine typo removes got-string-want-object branch", |
| 22 | input: "at '/engine': 'oneOf' failed, none matched\n" + |
| 23 | "- at '/engine': value must be one of 'claude', 'codex', 'copilot', 'gemini'\n" + |
| 24 | "- at '/engine': got string, want object", |
| 25 | wantNot: []string{"oneOf", "got string, want object"}, |
| 26 | wantAny: []string{"value must be one of 'claude', 'codex', 'copilot', 'gemini'"}, |
| 27 | }, |
| 28 | { |
| 29 | name: "permissions typo removes got-object-want-string branch", |
| 30 | input: "at '/permissions': 'oneOf' failed, none matched\n" + |
| 31 | "- at '/permissions': got object, want string\n" + |
| 32 | "- at '/permissions/deployments': value must be one of 'read', 'write', 'none'", |
| 33 | wantNot: []string{"oneOf", "got object, want string"}, |
| 34 | wantAny: []string{"value must be one of 'read', 'write', 'none'"}, |
| 35 | }, |
| 36 | { |
| 37 | name: "non-oneOf message is returned unchanged", |
| 38 | input: "value must be one of 'a', 'b', 'c'", |
| 39 | wantNot: []string{"oneOf"}, |
| 40 | wantAny: []string{"value must be one of 'a', 'b', 'c'"}, |
| 41 | }, |
| 42 | { |
| 43 | name: "nested path context preserved for sub-field errors", |
| 44 | input: "at '/permissions': 'oneOf' failed, none matched\n" + |
| 45 | "- at '/permissions': got object, want string\n" + |
| 46 | "- at '/permissions/deployments': value must be one of 'read', 'write', 'none'", |
| 47 | wantNot: []string{}, |
| 48 | wantAny: []string{"deployments"}, |
| 49 | }, |
| 50 | { |
| 51 | name: "all type conflicts synthesizes plain-English message", |
| 52 | input: "at '/x': 'oneOf' failed, none matched\n" + |
| 53 | "- at '/x': got string, want object\n" + |
| 54 | "- at '/x': got string, want array", |
| 55 | // When all sub-errors are type conflicts, synthesize a plain-English message |
| 56 | wantNot: []string{"oneOf", "got string, want object"}, |
| 57 | wantAny: []string{"expected object or array, got string"}, |
| 58 | }, |
| 59 | { |
| 60 | name: "engine type conflict produces actionable message", |
| 61 | input: "at '/engine': 'oneOf' failed, none matched\n" + |
| 62 | "- at '/engine': got number, want string\n" + |
| 63 | "- at '/engine': got number, want object", |
| 64 | wantNot: []string{"oneOf", "got number, want string"}, |
| 65 | wantAny: []string{"expected string or object, got number", "Valid engine names", "copilot"}, |
| 66 | }, |
| 67 | } |
| 68 | |
| 69 | for _, tt := range tests { |
| 70 | t.Run(tt.name, func(t *testing.T) { |
nothing calls this directly
no test coverage detected