this repo has no description
at master 263 lines 5.9 kB view raw
1// Copyright 2025 CUE Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package json 16 17import ( 18 "slices" 19 "testing" 20 21 "github.com/go-quicktest/qt" 22 23 "cuelang.org/go/cue" 24) 25 26func TestPointerFromTokens(t *testing.T) { 27 tests := []struct { 28 name string 29 tokens []string 30 want string 31 }{ 32 { 33 name: "empty", 34 tokens: []string{}, 35 want: "", 36 }, 37 { 38 name: "single_simple_token", 39 tokens: []string{"foo"}, 40 want: "/foo", 41 }, 42 { 43 name: "multiple_simple_tokens", 44 tokens: []string{"foo", "bar", "baz"}, 45 want: "/foo/bar/baz", 46 }, 47 { 48 name: "tokens_with_slash", 49 tokens: []string{"foo/bar", "baz"}, 50 want: "/foo~1bar/baz", 51 }, 52 { 53 name: "tokens_with_tilde", 54 tokens: []string{"foo~bar", "baz"}, 55 want: "/foo~0bar/baz", 56 }, 57 { 58 name: "tokens_with_both_slash_and_tilde", 59 tokens: []string{"foo~/bar", "baz~test/more"}, 60 want: "/foo~0~1bar/baz~0test~1more", 61 }, 62 { 63 name: "empty_token", 64 tokens: []string{"foo", "", "bar"}, 65 want: "/foo//bar", 66 }, 67 { 68 name: "numeric_tokens", 69 tokens: []string{"0", "123", "foo"}, 70 want: "/0/123/foo", 71 }, 72 { 73 name: "special_chars", 74 tokens: []string{"foo bar", "test\tmore", "with\nnewline"}, 75 want: "/foo bar/test\tmore/with\nnewline", 76 }, 77 } 78 79 for _, tt := range tests { 80 t.Run(tt.name, func(t *testing.T) { 81 got := PointerFromTokens(slices.Values(tt.tokens)) 82 qt.Check(t, qt.Equals(string(got), tt.want)) 83 }) 84 } 85} 86 87func TestPointerTokens(t *testing.T) { 88 tests := []struct { 89 name string 90 pointer string 91 want []string 92 }{ 93 { 94 name: "empty", 95 pointer: "", 96 want: nil, 97 }, 98 { 99 name: "root", 100 pointer: "/", 101 want: []string{""}, 102 }, 103 { 104 name: "single_token", 105 pointer: "/foo", 106 want: []string{"foo"}, 107 }, 108 { 109 name: "multiple_tokens", 110 pointer: "/foo/bar/baz", 111 want: []string{"foo", "bar", "baz"}, 112 }, 113 { 114 name: "escaped_slash", 115 pointer: "/foo~1bar/baz", 116 want: []string{"foo/bar", "baz"}, 117 }, 118 { 119 name: "escaped_tilde", 120 pointer: "/foo~0bar/baz", 121 want: []string{"foo~bar", "baz"}, 122 }, 123 { 124 name: "both_escapes", 125 pointer: "/foo~0~1bar/baz~0test~1more", 126 want: []string{"foo~/bar", "baz~test/more"}, 127 }, 128 { 129 name: "empty_tokens", 130 pointer: "/foo//bar", 131 want: []string{"foo", "", "bar"}, 132 }, 133 { 134 name: "numeric_tokens", 135 pointer: "/0/123/foo", 136 want: []string{"0", "123", "foo"}, 137 }, 138 { 139 name: "special_chars", 140 pointer: "/foo bar/test\tmore/with\nnewline", 141 want: []string{"foo bar", "test\tmore", "with\nnewline"}, 142 }, 143 { 144 name: "no_leading_slash", 145 pointer: "foo/bar", 146 want: []string{"foo", "bar"}, 147 }, 148 } 149 150 for _, tt := range tests { 151 t.Run(tt.name, func(t *testing.T) { 152 ptr := Pointer(tt.pointer) 153 got := slices.Collect(ptr.Tokens()) 154 qt.Check(t, qt.DeepEquals(got, tt.want)) 155 }) 156 } 157} 158 159func TestPointerRoundTrip(t *testing.T) { 160 tests := []struct { 161 name string 162 tokens []string 163 }{ 164 {"simple", []string{"foo", "bar", "baz"}}, 165 {"with_slashes", []string{"foo/bar", "baz/qux"}}, 166 {"with_tildes", []string{"foo~bar", "baz~qux"}}, 167 {"with_both", []string{"foo~/bar", "baz~qux/more"}}, 168 {"empty_tokens", []string{"foo", "", "bar"}}, 169 {"numeric", []string{"0", "123", "456"}}, 170 {"special_chars", []string{"foo bar", "test\tmore", "with\nnewline"}}, 171 } 172 173 for _, tt := range tests { 174 t.Run(tt.name, func(t *testing.T) { 175 pointer := PointerFromTokens(slices.Values(tt.tokens)) 176 roundTrip := slices.Collect(pointer.Tokens()) 177 qt.Check(t, qt.DeepEquals(roundTrip, tt.tokens)) 178 }) 179 } 180} 181 182func TestPointerFromCUEPath(t *testing.T) { 183 tests := []struct { 184 name string 185 path string 186 want string 187 wantErr bool 188 }{ 189 { 190 name: "empty_path", 191 path: "", 192 want: "", 193 }, 194 { 195 name: "simple_string_field", 196 path: "foo", 197 want: "/foo", 198 }, 199 { 200 name: "nested_string_fields", 201 path: "foo.bar.baz", 202 want: "/foo/bar/baz", 203 }, 204 { 205 name: "string_field_with_index", 206 path: "foo[0]", 207 want: "/foo/0", 208 }, 209 { 210 name: "complex_path", 211 path: "foo.bar[123].baz", 212 want: "/foo/bar/123/baz", 213 }, 214 { 215 name: "string_with_special_chars", 216 path: `"foo/bar"."baz~qux"`, 217 want: "/foo~1bar/baz~0qux", 218 }, 219 { 220 name: "multiple_indices", 221 path: "arr[0][1][2]", 222 want: "/arr/0/1/2", 223 }, 224 } 225 226 for _, tt := range tests { 227 t.Run(tt.name, func(t *testing.T) { 228 path := cue.ParsePath(tt.path) 229 if path.Err() != nil { 230 t.Fatalf("failed to parse path %q: %v", tt.path, path.Err()) 231 } 232 233 got, err := PointerFromCUEPath(path) 234 if tt.wantErr { 235 qt.Check(t, qt.IsNotNil(err)) 236 return 237 } 238 qt.Check(t, qt.IsNil(err)) 239 qt.Check(t, qt.Equals(string(got), tt.want)) 240 }) 241 } 242} 243 244func TestPointerFromCUEPathEdgeCases(t *testing.T) { 245 t.Run("empty_string_field", func(t *testing.T) { 246 path := cue.ParsePath(`""`) 247 qt.Check(t, qt.IsNil(path.Err())) 248 249 got, err := PointerFromCUEPath(path) 250 qt.Check(t, qt.IsNil(err)) 251 qt.Check(t, qt.Equals(string(got), "/")) 252 }) 253 254 t.Run("error_case", func(t *testing.T) { 255 // Test that unsupported selector types return an error 256 path := cue.MakePath(cue.Def("foo")) // Definition selector should error 257 258 got, err := PointerFromCUEPath(path) 259 qt.Check(t, qt.IsNotNil(err)) 260 qt.Check(t, qt.Equals(string(got), "")) 261 qt.Check(t, qt.StringContains(err.Error(), "cannot convert selector")) 262 }) 263}