github.com/mitranim/sqlb@v0.7.2/t_ord_test.go (about) 1 package sqlb 2 3 import ( 4 "encoding/json" 5 r "reflect" 6 "testing" 7 ) 8 9 func Test_Ords_Expr(t *testing.T) { 10 testExpr(t, rei(``), Ords(nil)) 11 testExpr(t, rei(``), Ords{}) 12 testExpr(t, rei(``), Ords{nil, nil, nil}) 13 testExpr(t, rei(`order by one`), Ords{nil, Str(`one`), nil}) 14 testExpr(t, rei(`order by $1, $2`, 10, 20), Ords{rei(`$1`, 10), nil, rei(`$2`, 20)}) 15 16 testExprs( 17 t, 18 rei(`one order by two`, 10, 20), 19 rei(`one`, 10), 20 Ords{rei(`two`, 20)}, 21 ) 22 } 23 24 func Test_Ords_Len(t *testing.T) { 25 eq(t, 0, Ords(nil).Len()) 26 eq(t, 0, Ords{}.Len()) 27 eq(t, 0, Ords{nil}.Len()) 28 eq(t, 1, Ords{nil, Str(``), nil}.Len()) 29 eq(t, 2, Ords{nil, Str(``), nil, Str(``), nil}.Len()) 30 } 31 32 func Test_Ords_IsEmpty(t *testing.T) { 33 eq(t, true, Ords(nil).IsEmpty()) 34 eq(t, true, Ords{}.IsEmpty()) 35 eq(t, true, Ords{nil}.IsEmpty()) 36 eq(t, false, Ords{nil, Str(``), nil}.IsEmpty()) 37 eq(t, false, Ords{nil, Str(``), nil, Str(``), nil}.IsEmpty()) 38 } 39 40 func Test_Ords_RowNumberOver(t *testing.T) { 41 eq(t, RowNumberOver{}, Ords(nil).RowNumberOver()) 42 eq(t, RowNumberOver{}, Ords{}.RowNumberOver()) 43 eq(t, RowNumberOver{}, Ords{nil}.RowNumberOver()) 44 eq(t, RowNumberOver{Ords{nil, Str(``)}}, Ords{nil, Str(``)}.RowNumberOver()) 45 } 46 47 func Test_Ords_Or(t *testing.T) { 48 test := func(exp, tar, args Ords) { 49 t.Helper() 50 tar.Or(args...) 51 eq(t, exp, tar) 52 } 53 54 test(Ords(nil), Ords(nil), Ords(nil)) 55 test(Ords{}, Ords{}, Ords{}) 56 test(Ords{}, Ords{nil}, Ords{}) 57 test(Ords{Str(``)}, Ords{nil}, Ords{Str(``)}) 58 test(Ords{Str(``)}, Ords{nil}, Ords{nil, Str(``), nil}) 59 test(Ords{Str(`one`), Str(`two`)}, Ords{}, Ords{nil, Str(`one`), nil, Str(`two`)}) 60 test(Ords{Str(`one`), Str(`two`)}, Ords{nil}, Ords{nil, Str(`one`), nil, Str(`two`)}) 61 test(Ords{Str(`one`)}, Ords{Str(`one`)}, Ords{Str(`two`)}) 62 } 63 64 func Test_Ordering_Expr(t *testing.T) { 65 testExpr(t, rei(``), Ordering{}) 66 testExpr(t, rei(``), Ordering{Dir: DirDesc, Nulls: NullsLast, Using: Str(`<`)}) 67 68 testExpr(t, rei(`one`), Ordering{Expr: Str(`one`)}) 69 testExpr(t, rei(`one`, 10), Ordering{Expr: rei(`one`, 10)}) 70 testExpr(t, rei(`one asc`, 10), Ordering{Expr: rei(`one`, 10), Dir: DirAsc}) 71 testExpr(t, rei(`one desc`, 10), Ordering{Expr: rei(`one`, 10), Dir: DirDesc}) 72 testExpr(t, rei(`one nulls first`, 10), Ordering{Expr: rei(`one`, 10), Nulls: NullsFirst}) 73 testExpr(t, rei(`one nulls last`, 10), Ordering{Expr: rei(`one`, 10), Nulls: NullsLast}) 74 testExpr(t, rei(`one asc nulls first`, 10), Ordering{Expr: rei(`one`, 10), Dir: DirAsc, Nulls: NullsFirst}) 75 testExpr(t, rei(`one asc nulls last`, 10), Ordering{Expr: rei(`one`, 10), Dir: DirAsc, Nulls: NullsLast}) 76 testExpr(t, rei(`one desc nulls first`, 10), Ordering{Expr: rei(`one`, 10), Dir: DirDesc, Nulls: NullsFirst}) 77 testExpr(t, rei(`one desc nulls last`, 10), Ordering{Expr: rei(`one`, 10), Dir: DirDesc, Nulls: NullsLast}) 78 79 testExpr(t, rei(`one using <`), Ordering{Expr: Str(`one`), Using: Str(`<`)}) 80 testExpr(t, rei(`one using <`, 10), Ordering{Expr: rei(`one`, 10), Using: Str(`<`)}) 81 testExpr(t, rei(`one asc using <`, 10), Ordering{Expr: rei(`one`, 10), Dir: DirAsc, Using: Str(`<`)}) 82 testExpr(t, rei(`one desc using <`, 10), Ordering{Expr: rei(`one`, 10), Dir: DirDesc, Using: Str(`<`)}) 83 testExpr(t, rei(`one nulls first using <`, 10), Ordering{Expr: rei(`one`, 10), Nulls: NullsFirst, Using: Str(`<`)}) 84 testExpr(t, rei(`one nulls last using <`, 10), Ordering{Expr: rei(`one`, 10), Nulls: NullsLast, Using: Str(`<`)}) 85 testExpr(t, rei(`one asc nulls first using <`, 10), Ordering{Expr: rei(`one`, 10), Dir: DirAsc, Nulls: NullsFirst, Using: Str(`<`)}) 86 testExpr(t, rei(`one asc nulls last using <`, 10), Ordering{Expr: rei(`one`, 10), Dir: DirAsc, Nulls: NullsLast, Using: Str(`<`)}) 87 testExpr(t, rei(`one desc nulls first using <`, 10), Ordering{Expr: rei(`one`, 10), Dir: DirDesc, Nulls: NullsFirst, Using: Str(`<`)}) 88 testExpr(t, rei(`one desc nulls last using <`, 10), Ordering{Expr: rei(`one`, 10), Dir: DirDesc, Nulls: NullsLast, Using: Str(`<`)}) 89 } 90 91 func Test_Ord_Expr(t *testing.T) { 92 testExpr(t, rei(``), Ord{}) 93 testExpr(t, rei(``), Ord{Dir: DirDesc, Nulls: NullsLast}) 94 95 testExpr(t, rei(`"one"`), Ord{Path: Path{`one`}}) 96 testExpr(t, rei(`"one"`), Ord{Path: Path{`one`}}) 97 testExpr(t, rei(`"one" asc`), Ord{Path: Path{`one`}, Dir: DirAsc}) 98 testExpr(t, rei(`"one" desc`), Ord{Path: Path{`one`}, Dir: DirDesc}) 99 testExpr(t, rei(`"one" nulls first`), Ord{Path: Path{`one`}, Nulls: NullsFirst}) 100 testExpr(t, rei(`"one" nulls last`), Ord{Path: Path{`one`}, Nulls: NullsLast}) 101 testExpr(t, rei(`"one" asc nulls first`), Ord{Path: Path{`one`}, Dir: DirAsc, Nulls: NullsFirst}) 102 testExpr(t, rei(`"one" asc nulls last`), Ord{Path: Path{`one`}, Dir: DirAsc, Nulls: NullsLast}) 103 testExpr(t, rei(`"one" desc nulls first`), Ord{Path: Path{`one`}, Dir: DirDesc, Nulls: NullsFirst}) 104 testExpr(t, rei(`"one" desc nulls last`), Ord{Path: Path{`one`}, Dir: DirDesc, Nulls: NullsLast}) 105 } 106 107 func Test_Ord_combos(t *testing.T) { 108 testExpr(t, rei(``), OrdAsc{}) 109 testExpr(t, rei(``), OrdDesc{}) 110 testExpr(t, rei(``), OrdNullsFirst{}) 111 testExpr(t, rei(``), OrdNullsLast{}) 112 testExpr(t, rei(``), OrdAscNullsFirst{}) 113 testExpr(t, rei(``), OrdAscNullsLast{}) 114 testExpr(t, rei(``), OrdDescNullsFirst{}) 115 testExpr(t, rei(``), OrdDescNullsLast{}) 116 117 testExpr(t, rei(`("one")."two" asc`), OrdAsc{`one`, `two`}) 118 testExpr(t, rei(`("one")."two" desc`), OrdDesc{`one`, `two`}) 119 testExpr(t, rei(`("one")."two" nulls first`), OrdNullsFirst{`one`, `two`}) 120 testExpr(t, rei(`("one")."two" nulls last`), OrdNullsLast{`one`, `two`}) 121 testExpr(t, rei(`("one")."two" asc nulls first`), OrdAscNullsFirst{`one`, `two`}) 122 testExpr(t, rei(`("one")."two" asc nulls last`), OrdAscNullsLast{`one`, `two`}) 123 testExpr(t, rei(`("one")."two" desc nulls first`), OrdDescNullsFirst{`one`, `two`}) 124 testExpr(t, rei(`("one")."two" desc nulls last`), OrdDescNullsLast{`one`, `two`}) 125 } 126 127 func Test_ParseOpt_OrType(t *testing.T) { 128 test := func(exp r.Type, src, typ any) { 129 t.Helper() 130 131 opt := ParseOpt{Type: r.TypeOf(src)} 132 opt.OrType(typ) 133 134 eq(t, exp, opt.Type) 135 } 136 137 test(nil, nil, nil) 138 139 test(r.TypeOf(Outer{}), nil, Outer{}) 140 test(r.TypeOf(Outer{}), nil, &Outer{}) 141 test(r.TypeOf(Outer{}), nil, (*Outer)(nil)) 142 test(r.TypeOf(Outer{}), nil, []Outer(nil)) 143 test(r.TypeOf(Outer{}), nil, []*Outer(nil)) 144 test(r.TypeOf(Outer{}), nil, (*[]Outer)(nil)) 145 test(r.TypeOf(Outer{}), nil, (*[]*Outer)(nil)) 146 147 test(r.TypeOf(Internal{}), Internal{}, nil) 148 test(r.TypeOf(Internal{}), Internal{}, Outer{}) 149 test(r.TypeOf(Internal{}), Internal{}, &Outer{}) 150 test(r.TypeOf(Internal{}), Internal{}, (*Outer)(nil)) 151 test(r.TypeOf(Internal{}), Internal{}, []Outer(nil)) 152 test(r.TypeOf(Internal{}), Internal{}, []*Outer(nil)) 153 test(r.TypeOf(Internal{}), Internal{}, (*[]Outer)(nil)) 154 test(r.TypeOf(Internal{}), Internal{}, (*[]*Outer)(nil)) 155 } 156 157 // Delegates to `(*ParserOrds).ParseSlice` which is tested separately. 158 func Test_ParserOrds_UnmarshalJSON(t *testing.T) { 159 test := func(exp Ords, src string, typ any) { 160 t.Helper() 161 162 var par ParserOrds 163 par.OrType(typ) 164 165 eq(t, nil, par.UnmarshalJSON([]byte(src))) 166 eq(t, exp, par.Ords) 167 } 168 169 test(Ords(nil), `null`, nil) 170 test(Ords(nil), `null`, Outer{}) 171 test(Ords(nil), `[]`, nil) 172 test(Ords(nil), `[]`, Outer{}) 173 test(Ords(nil), `[""]`, nil) 174 test(Ords(nil), `[""]`, Outer{}) 175 test(Ords{Path{`outer_id`}}, `["outerId"]`, Outer{}) 176 177 test( 178 Ords{OrdAsc{`outer_id`}, OrdDesc{`outer_name`}}, 179 `["outerId asc", "outerName desc"]`, 180 Outer{}, 181 ) 182 } 183 184 func Test_ParserOrds_ParseSlice_invalid(t *testing.T) { 185 test := func(src, msg string, typ any) { 186 t.Helper() 187 188 var par ParserOrds 189 par.OrType(typ) 190 191 panics(t, msg, func() { 192 try(par.ParseSlice([]string{src})) 193 }) 194 } 195 196 test(`one two three`, `is not a valid ordering string`, nil) 197 test(`one asc nulls`, `is not a valid ordering string`, nil) 198 test(`one`, `expected struct, found int`, 10) 199 test(`one`, `expected struct, found string`, `some string`) 200 test(`one`, `error "ErrUnknownField" while converting JSON identifier path to DB path: no DB path corresponding to JSON path "one" in type nil`, nil) 201 test(`one.two`, `error "ErrUnknownField" while converting JSON identifier path to DB path: no DB path corresponding to JSON path "one.two" in type nil`, nil) 202 test(`one`, `error "ErrUnknownField" while converting JSON identifier path to DB path: no DB path corresponding to JSON path "one" in type Outer`, Outer{}) 203 test(`one.two`, `error "ErrUnknownField" while converting JSON identifier path to DB path: no DB path corresponding to JSON path "one.two" in type Outer`, Outer{}) 204 test(`outer_id`, `error "ErrUnknownField" while converting JSON identifier path to DB path: no DB path corresponding to JSON path "outer_id" in type Outer`, Outer{}) 205 test(`OuterId`, `error "ErrUnknownField" while converting JSON identifier path to DB path: no DB path corresponding to JSON path "OuterId" in type Outer`, Outer{}) 206 test(`Id`, `error "ErrUnknownField" while converting JSON identifier path to DB path: no DB path corresponding to JSON path "Id" in type Outer`, Outer{}) 207 test(`onlyJson`, `error "ErrUnknownField" while converting JSON identifier path to DB path: no DB path corresponding to JSON path "onlyJson" in type Outer`, Outer{}) 208 } 209 210 func testOrdsParsing(t testing.TB, exp Ords, src []string, typ any) { 211 t.Helper() 212 213 var par ParserOrds 214 par.OrType(typ) 215 216 eq(t, nil, par.ParseSlice(src)) 217 eq(t, exp, par.Ords) 218 } 219 220 func Test_ParserOrds_ParseSlice_empty(t *testing.T) { 221 testOrdsParsing(t, Ords(nil), nil, nil) 222 testOrdsParsing(t, Ords(nil), []string{}, nil) 223 testOrdsParsing(t, Ords(nil), []string{``}, nil) 224 testOrdsParsing(t, Ords(nil), []string{``, ``}, nil) 225 } 226 227 func Test_ParserOrds_ParseSlice_single(t *testing.T) { 228 test := func(exp Expr, src string, typ any) { 229 t.Helper() 230 testOrdsParsing(t, Ords{exp}, []string{src}, typ) 231 } 232 233 test(Path{`outer_id`}, `outerId`, Outer{}) 234 test(Path{`outer_name`}, `outerName`, Outer{}) 235 test(Path{`embed_id`}, `embedId`, Outer{}) 236 test(Path{`embed_name`}, `embedName`, Outer{}) 237 test(Path{`internal`}, `externalInternal`, External{}) 238 test(Path{`internal`, `id`}, `externalInternal.internalId`, External{}) 239 test(Path{`internal`, `name`}, `externalInternal.internalName`, External{}) 240 test(OrdAsc{`outer_id`}, `outerId asc`, Outer{}) 241 test(OrdDesc{`outer_id`}, `outerId desc`, Outer{}) 242 test(OrdNullsFirst{`outer_id`}, `outerId nulls first`, Outer{}) 243 test(OrdNullsLast{`outer_id`}, `outerId nulls last`, Outer{}) 244 test(OrdAscNullsFirst{`outer_id`}, `outerId asc nulls first`, Outer{}) 245 test(OrdAscNullsLast{`outer_id`}, `outerId asc nulls last`, Outer{}) 246 test(OrdDescNullsFirst{`outer_id`}, `outerId desc nulls first`, Outer{}) 247 test(OrdDescNullsLast{`outer_id`}, `outerId desc nulls last`, Outer{}) 248 test(OrdDescNullsLast{`outer_id`}, ` outerId dEsC nUlLs LaSt `, Outer{}) 249 } 250 251 func Test_ParserOrds_ParseSlice_multiple(t *testing.T) { 252 test := func(exp Ords, src []string, typ any) { 253 t.Helper() 254 testOrdsParsing(t, exp, src, typ) 255 } 256 257 test( 258 Ords{Path{`outer_id`}}, 259 []string{``, `outerId`, ``}, 260 Outer{}, 261 ) 262 263 test( 264 Ords{Path{`outer_id`}, Path{`outer_name`}}, 265 []string{`outerId`, ``, `outerName`, ``}, 266 Outer{}, 267 ) 268 269 test( 270 Ords{OrdAscNullsFirst{`outer_id`}, OrdDescNullsLast{`outer_name`}}, 271 []string{``, `outerId asc nulls first`, ``, ``, `outerName desc nulls last`}, 272 Outer{}, 273 ) 274 } 275 276 func Test_ParserOrds_ParseSlice_lax(t *testing.T) { 277 test := func(src []string, typ any) { 278 t.Helper() 279 280 panics(t, `no DB path corresponding to JSON path`, func() { 281 var par ParserOrds 282 par.OrType(typ) 283 try(par.ParseSlice(src)) 284 }) 285 286 var par ParserOrds 287 par.OrType(typ) 288 par.Lax = true 289 290 eq(t, nil, par.ParseSlice(src)) 291 eq(t, 0, len(par.Ords)) 292 } 293 294 test([]string{`outerId`}, nil) 295 test([]string{`outer_id`}, Outer{}) 296 } 297 298 func Test_ParseOpt_Filter(t *testing.T) { 299 type Target struct { 300 Tagged string `json:"jsonTagged" db:"db_tagged" ord:""` 301 Untagged string `json:"jsonUntagged" db:"db_untagged"` 302 } 303 304 t.Run(`without filter`, func(t *testing.T) { 305 par := ParserOrds{ParseOpt: ParseOpt{ 306 Type: r.TypeOf(Target{}), 307 }} 308 309 try(par.ParseSlice([]string{ 310 `jsonTagged asc`, 311 `jsonUntagged desc`, 312 })) 313 314 eq( 315 t, 316 Ords{ 317 OrdAsc{`db_tagged`}, 318 OrdDesc{`db_untagged`}, 319 }, 320 par.Ords, 321 ) 322 }) 323 324 t.Run(`with filter`, func(t *testing.T) { 325 par := ParserOrds{ParseOpt: ParseOpt{ 326 Type: r.TypeOf(Target{}), 327 Filter: TagFilter(`ord`), 328 }} 329 330 panics( 331 t, 332 `no DB path corresponding to JSON path "jsonUntagged" in type Target`, 333 func() { 334 try(par.ParseSlice([]string{`jsonUntagged asc`})) 335 }, 336 ) 337 }) 338 } 339 340 func Test_ParserOrds_default_dir(t *testing.T) { 341 test := func(typ any, src []string, exp Ords) { 342 t.Helper() 343 344 var par ParserOrds 345 par.OrType(typ) 346 347 try(par.ParseSlice(src)) 348 eq(t, exp, par.Ords) 349 } 350 351 test( 352 struct { 353 Name string `json:"name" db:"name" ord.dir:""` 354 }{}, 355 []string{`name`}, 356 Ords{Path{`name`}}, 357 ) 358 359 test( 360 struct { 361 Name string `json:"name" db:"name" ord.dir:"asc"` 362 }{}, 363 []string{`name`}, 364 Ords{OrdAsc{`name`}}, 365 ) 366 367 test( 368 struct { 369 Name string `json:"name" db:"name" ord.dir:"asc"` 370 }{}, 371 []string{`name desc`}, 372 Ords{OrdDesc{`name`}}, 373 ) 374 375 test( 376 struct { 377 Name string `json:"name" db:"name" ord.dir:"desc"` 378 }{}, 379 []string{`name`}, 380 Ords{OrdDesc{`name`}}, 381 ) 382 383 test( 384 struct { 385 Name string `json:"name" db:"name" ord.dir:"desc"` 386 }{}, 387 []string{`name asc`}, 388 Ords{OrdAsc{`name`}}, 389 ) 390 } 391 392 func Test_ParserOrds_default_nulls(t *testing.T) { 393 test := func(typ any, src []string, exp Ords) { 394 t.Helper() 395 396 var par ParserOrds 397 par.OrType(typ) 398 399 try(par.ParseSlice(src)) 400 eq(t, exp, par.Ords) 401 } 402 403 test( 404 struct { 405 Name string `json:"name" db:"name" ord.nulls:""` 406 }{}, 407 []string{`name`}, 408 Ords{Path{`name`}}, 409 ) 410 411 test( 412 struct { 413 Name string `json:"name" db:"name" ord.nulls:"first"` 414 }{}, 415 []string{`name`}, 416 Ords{OrdNullsFirst{`name`}}, 417 ) 418 419 test( 420 struct { 421 Name string `json:"name" db:"name" ord.nulls:"first"` 422 }{}, 423 []string{`name nulls last`}, 424 Ords{OrdNullsLast{`name`}}, 425 ) 426 427 test( 428 struct { 429 Name string `json:"name" db:"name" ord.nulls:"last"` 430 }{}, 431 []string{`name`}, 432 Ords{OrdNullsLast{`name`}}, 433 ) 434 435 test( 436 struct { 437 Name string `json:"name" db:"name" ord.nulls:"last"` 438 }{}, 439 []string{`name nulls first`}, 440 Ords{OrdNullsFirst{`name`}}, 441 ) 442 } 443 444 func TestDir(t *testing.T) { 445 t.Run(`String`, func(t *testing.T) { 446 eq(t, ``, DirNone.String()) 447 eq(t, `asc`, DirAsc.String()) 448 eq(t, `desc`, DirDesc.String()) 449 }) 450 451 t.Run(`Parse`, func(t *testing.T) { 452 test := func(exp Dir, src string) { 453 t.Helper() 454 var tar Dir 455 try(tar.Parse(src)) 456 eq(t, exp, tar) 457 } 458 459 test(DirNone, ``) 460 test(DirAsc, `asc`) 461 test(DirDesc, `desc`) 462 }) 463 464 t.Run(`MarshalJSON`, func(t *testing.T) { 465 test := func(exp string, src Dir) { 466 t.Helper() 467 eq(t, exp, string(try1(json.Marshal(src)))) 468 } 469 470 test(`null`, DirNone) 471 test(`"asc"`, DirAsc) 472 test(`"desc"`, DirDesc) 473 }) 474 475 t.Run(`UnmarshalJSON`, func(t *testing.T) { 476 test := func(exp Dir, src string) { 477 t.Helper() 478 var tar Dir 479 try(json.Unmarshal([]byte(src), &tar)) 480 eq(t, exp, tar) 481 } 482 483 test(DirNone, `null`) 484 test(DirNone, `""`) 485 test(DirAsc, `"asc"`) 486 test(DirDesc, `"desc"`) 487 }) 488 }