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  }