filter_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. package vuetifyx_test
  2. import (
  3. "testing"
  4. "time"
  5. . "github.com/qor5/ui/vuetifyx"
  6. "github.com/theplant/testingutils"
  7. )
  8. func mustParseDatetimePickerValue(v string) time.Time {
  9. t, err := time.ParseInLocation("2006-01-02 15:04", v, time.Local)
  10. if err != nil {
  11. panic(err)
  12. }
  13. return t
  14. }
  15. func mustParseDatePickerValue(v string) time.Time {
  16. t, err := time.ParseInLocation("2006-01-02", v, time.Local)
  17. if err != nil {
  18. panic(err)
  19. }
  20. return t
  21. }
  22. var setByQueryCases = []struct {
  23. name string
  24. data FilterData
  25. qs string
  26. expected FilterData
  27. expectedSQLConds string
  28. expectedSQLArgs []interface{}
  29. }{
  30. {
  31. name: "between",
  32. data: FilterData([]*FilterItem{
  33. {
  34. Key: "created",
  35. ItemType: ItemTypeDatetimeRange,
  36. SQLCondition: "created_at %s ?",
  37. },
  38. }),
  39. qs: "created.lt=2019-04-11 00:00&created.gte=2019-04-10 00:00",
  40. expected: FilterData([]*FilterItem{
  41. {
  42. Key: "created",
  43. ItemType: ItemTypeDatetimeRange,
  44. Modifier: ModifierBetween,
  45. Selected: true,
  46. ValueFrom: "2019-04-10 00:00",
  47. ValueTo: "2019-04-11 00:00",
  48. },
  49. }),
  50. expectedSQLConds: "created_at >= ? AND created_at < ?",
  51. expectedSQLArgs: []interface{}{mustParseDatetimePickerValue("2019-04-10 00:00"), mustParseDatetimePickerValue("2019-04-11 00:00")},
  52. },
  53. {
  54. name: "between2",
  55. data: FilterData([]*FilterItem{
  56. {
  57. Key: "created",
  58. Label: "Created",
  59. ItemType: ItemTypeDatetimeRange,
  60. SQLCondition: `extract(epoch from created_at) %s ?`,
  61. },
  62. {
  63. Key: "name",
  64. Label: "Name",
  65. ItemType: ItemTypeString,
  66. SQLCondition: `name %s ?`,
  67. },
  68. }),
  69. qs: "created.lt=2019-08-09 00:00&created.gte=2019-08-07 00:00",
  70. expected: FilterData([]*FilterItem{
  71. {
  72. Key: "created",
  73. Label: "Created",
  74. ItemType: ItemTypeDatetimeRange,
  75. Modifier: ModifierBetween,
  76. Selected: true,
  77. ValueFrom: "2019-08-07 00:00",
  78. ValueTo: "2019-08-09 00:00",
  79. },
  80. {
  81. Key: "name",
  82. Label: "Name",
  83. ItemType: ItemTypeString,
  84. SQLCondition: `name %s ?`,
  85. },
  86. }),
  87. expectedSQLConds: "extract(epoch from created_at) >= ? AND extract(epoch from created_at) < ?",
  88. expectedSQLArgs: []interface{}{mustParseDatetimePickerValue("2019-08-07 00:00"), mustParseDatetimePickerValue("2019-08-09 00:00")},
  89. },
  90. {
  91. name: "date_range_has_two_params",
  92. data: FilterData([]*FilterItem{
  93. {
  94. Key: "created",
  95. ItemType: ItemTypeDateRange,
  96. SQLCondition: "created_at %s ?",
  97. },
  98. }),
  99. qs: "created.lte=2019-04-11&created.gte=2019-04-10",
  100. expected: FilterData([]*FilterItem{
  101. {
  102. Key: "created",
  103. ItemType: ItemTypeDateRange,
  104. Modifier: ModifierBetween,
  105. Selected: true,
  106. ValueFrom: "2019-04-10",
  107. ValueTo: "2019-04-11",
  108. },
  109. }),
  110. expectedSQLConds: "created_at >= ? AND created_at < ?",
  111. expectedSQLArgs: []interface{}{mustParseDatePickerValue("2019-04-10"), mustParseDatePickerValue("2019-04-12")},
  112. },
  113. {
  114. name: "date_range_has_left_param",
  115. data: FilterData([]*FilterItem{
  116. {
  117. Key: "created",
  118. ItemType: ItemTypeDateRange,
  119. SQLCondition: "created_at %s ?",
  120. },
  121. }),
  122. qs: "created.gte=2019-04-10",
  123. expected: FilterData([]*FilterItem{
  124. {
  125. Key: "created",
  126. ItemType: ItemTypeDateRange,
  127. Selected: true,
  128. ValueIs: "2019-04-10",
  129. ValueFrom: "2019-04-10",
  130. },
  131. }),
  132. expectedSQLConds: "created_at >= ?",
  133. expectedSQLArgs: []interface{}{mustParseDatePickerValue("2019-04-10")},
  134. },
  135. {
  136. name: "date_range_has_right_param",
  137. data: FilterData([]*FilterItem{
  138. {
  139. Key: "created",
  140. ItemType: ItemTypeDateRange,
  141. SQLCondition: "created_at %s ?",
  142. },
  143. }),
  144. qs: "created.lte=2019-04-10",
  145. expected: FilterData([]*FilterItem{
  146. {
  147. Key: "created",
  148. ItemType: ItemTypeDateRange,
  149. Selected: true,
  150. ValueIs: "2019-04-10",
  151. ValueTo: "2019-04-10",
  152. },
  153. }),
  154. expectedSQLConds: "created_at < ?",
  155. expectedSQLArgs: []interface{}{mustParseDatePickerValue("2019-04-11")},
  156. },
  157. {
  158. name: "customize SQLCondition",
  159. data: FilterData([]*FilterItem{
  160. {
  161. Key: "missing-source-url",
  162. ItemType: ItemTypeSelect,
  163. Options: []*SelectItem{
  164. {Text: "Yes", Value: "1", SQLCondition: "source_a IS NULL AND source_b IS NULL"},
  165. {Text: "No", Value: "0", SQLCondition: "source_a IS NOT NULL OR source_b IS NOT NULL"},
  166. },
  167. },
  168. }),
  169. qs: "missing-source-url=1",
  170. expected: FilterData([]*FilterItem{
  171. {
  172. Key: "missing-source-url",
  173. ItemType: ItemTypeSelect,
  174. Modifier: ModifierEquals,
  175. Selected: true,
  176. ValueIs: "1",
  177. Options: []*SelectItem{
  178. {Text: "Yes", Value: "1", SQLCondition: "source_a IS NULL AND source_b IS NULL"},
  179. {Text: "No", Value: "0", SQLCondition: "source_a IS NOT NULL OR source_b IS NOT NULL"},
  180. },
  181. },
  182. }),
  183. expectedSQLConds: "source_a IS NULL AND source_b IS NULL",
  184. expectedSQLArgs: nil,
  185. },
  186. {
  187. name: "customize SQLCondition with single value",
  188. data: FilterData([]*FilterItem{
  189. {
  190. Key: "missing-source-url",
  191. ItemType: ItemTypeSelect,
  192. Options: []*SelectItem{
  193. {Text: "Yes", Value: "1", SQLCondition: "source_a IS NULL AND source_b = ?"},
  194. {Text: "No", Value: "0", SQLCondition: "source_a IS NOT NULL OR source_b IS NOT NULL"},
  195. },
  196. },
  197. }),
  198. qs: "missing-source-url=1",
  199. expected: FilterData([]*FilterItem{
  200. {
  201. Key: "missing-source-url",
  202. ItemType: ItemTypeSelect,
  203. Modifier: ModifierEquals,
  204. Selected: true,
  205. ValueIs: "1",
  206. Options: []*SelectItem{
  207. {Text: "Yes", Value: "1", SQLCondition: "source_a IS NULL AND source_b = ?"},
  208. {Text: "No", Value: "0", SQLCondition: "source_a IS NOT NULL OR source_b IS NOT NULL"},
  209. },
  210. },
  211. }),
  212. expectedSQLConds: "source_a IS NULL AND source_b = ?",
  213. expectedSQLArgs: []interface{}{"1"},
  214. },
  215. {
  216. name: "customize SQLCondition with multiple value",
  217. data: FilterData([]*FilterItem{
  218. {
  219. Key: "missing-source-url",
  220. ItemType: ItemTypeSelect,
  221. Options: []*SelectItem{
  222. {Text: "Yes", Value: "1", SQLCondition: "source_a IS NULL AND (source_b = ? OR source_c = ?)"},
  223. {Text: "No", Value: "0", SQLCondition: "source_a IS NOT NULL OR source_b IS NOT NULL"},
  224. },
  225. },
  226. }),
  227. qs: "missing-source-url=1",
  228. expected: FilterData([]*FilterItem{
  229. {
  230. Key: "missing-source-url",
  231. ItemType: ItemTypeSelect,
  232. Modifier: ModifierEquals,
  233. Selected: true,
  234. ValueIs: "1",
  235. Options: []*SelectItem{
  236. {Text: "Yes", Value: "1", SQLCondition: "source_a IS NULL AND (source_b = ? OR source_c = ?)"},
  237. {Text: "No", Value: "0", SQLCondition: "source_a IS NOT NULL OR source_b IS NOT NULL"},
  238. },
  239. },
  240. }),
  241. expectedSQLConds: "source_a IS NULL AND (source_b = ? OR source_c = ?)",
  242. expectedSQLArgs: []interface{}{"1", "1"},
  243. },
  244. {
  245. name: "customize SQLCondition with default operator and single value",
  246. data: FilterData([]*FilterItem{
  247. {
  248. Key: "missing-source-url",
  249. ItemType: ItemTypeSelect,
  250. Options: []*SelectItem{
  251. {Text: "Yes", Value: "1", SQLCondition: "source_a IS NULL AND source_b %s ?"},
  252. },
  253. },
  254. }),
  255. qs: "missing-source-url=1",
  256. expected: FilterData([]*FilterItem{
  257. {
  258. Key: "missing-source-url",
  259. ItemType: ItemTypeSelect,
  260. Modifier: ModifierEquals,
  261. Selected: true,
  262. ValueIs: "1",
  263. Options: []*SelectItem{
  264. {Text: "Yes", Value: "1", SQLCondition: "source_a IS NULL AND source_b %s ?"},
  265. },
  266. },
  267. }),
  268. expectedSQLConds: "source_a IS NULL AND source_b = ?",
  269. expectedSQLArgs: []interface{}{"1"},
  270. },
  271. {
  272. name: "customize SQLCondition with extra operator and multiple value",
  273. data: FilterData([]*FilterItem{
  274. {
  275. Key: "missing-source-url",
  276. ItemType: ItemTypeSelect,
  277. Options: []*SelectItem{
  278. {Text: "Yes", Value: "1", SQLCondition: "source_a IS NULL AND (source_b {op} ? OR source_c {op} ?)"},
  279. },
  280. },
  281. }),
  282. qs: "missing-source-url.gt=1",
  283. expected: FilterData([]*FilterItem{
  284. {
  285. Key: "missing-source-url",
  286. ItemType: ItemTypeSelect,
  287. Selected: true,
  288. ValueIs: "1",
  289. Options: []*SelectItem{
  290. {Text: "Yes", Value: "1", SQLCondition: "source_a IS NULL AND (source_b {op} ? OR source_c {op} ?)"},
  291. },
  292. },
  293. }),
  294. expectedSQLConds: "source_a IS NULL AND (source_b > ? OR source_c > ?)",
  295. expectedSQLArgs: []interface{}{"1", "1"},
  296. },
  297. {
  298. name: "ItemTypeMultipleSelect",
  299. data: FilterData([]*FilterItem{
  300. {
  301. Key: "state",
  302. ItemType: ItemTypeMultipleSelect,
  303. SQLCondition: "state %s ?",
  304. Options: []*SelectItem{
  305. {Text: "Draft", Value: "draft"},
  306. {Text: "Approved", Value: "approved"},
  307. {Text: "Rejected", Value: "rejected"},
  308. },
  309. },
  310. }),
  311. qs: "state.in=draft,rejected",
  312. expected: FilterData([]*FilterItem{
  313. {
  314. Key: "state",
  315. ItemType: ItemTypeMultipleSelect,
  316. Selected: true,
  317. Modifier: ModifierIn,
  318. ValuesAre: []string{
  319. "draft",
  320. "rejected",
  321. },
  322. Options: []*SelectItem{
  323. {Text: "Draft", Value: "draft"},
  324. {Text: "Approved", Value: "approved"},
  325. {Text: "Rejected", Value: "rejected"},
  326. },
  327. },
  328. }),
  329. expectedSQLConds: "state IN ?",
  330. expectedSQLArgs: []interface{}{[]string{"draft", "rejected"}},
  331. },
  332. {
  333. name: "ItemTypeLinkageSelect",
  334. data: FilterData([]*FilterItem{
  335. {
  336. Key: "province_city_district",
  337. ItemType: ItemTypeLinkageSelect,
  338. LinkageSelectData: FilterLinkageSelectData{
  339. Items: [][]*LinkageSelectItem{
  340. {
  341. {ID: "1", Name: "浙江", ChildrenIDs: []string{"1", "2"}},
  342. {ID: "2", Name: "江苏", ChildrenIDs: []string{"3", "4"}},
  343. },
  344. {
  345. {ID: "1", Name: "杭州", ChildrenIDs: []string{"1", "2"}},
  346. {ID: "2", Name: "宁波", ChildrenIDs: []string{"3", "4"}},
  347. {ID: "3", Name: "南京", ChildrenIDs: []string{"5", "6"}},
  348. {ID: "4", Name: "苏州", ChildrenIDs: []string{"7", "8"}},
  349. },
  350. {
  351. {ID: "1", Name: "拱墅区"},
  352. {ID: "2", Name: "西湖区"},
  353. {ID: "3", Name: "镇海区"},
  354. {ID: "4", Name: "鄞州区"},
  355. {ID: "5", Name: "鼓楼区"},
  356. {ID: "6", Name: "玄武区"},
  357. {ID: "7", Name: "常熟区"},
  358. {ID: "8", Name: "吴江区"},
  359. },
  360. },
  361. Labels: []string{"Province", "City", "District"},
  362. SQLConditions: []string{
  363. "province_id = ?",
  364. "city_id = ?",
  365. "district_id = ?",
  366. },
  367. },
  368. },
  369. }),
  370. qs: "province_city_district=2,3,7",
  371. expected: FilterData([]*FilterItem{
  372. {
  373. Key: "province_city_district",
  374. ItemType: ItemTypeLinkageSelect,
  375. Selected: true,
  376. Modifier: ModifierEquals,
  377. ValuesAre: []string{
  378. "2",
  379. "3",
  380. "7",
  381. },
  382. LinkageSelectData: FilterLinkageSelectData{
  383. Items: [][]*LinkageSelectItem{
  384. {
  385. {ID: "1", Name: "浙江", ChildrenIDs: []string{"1", "2"}},
  386. {ID: "2", Name: "江苏", ChildrenIDs: []string{"3", "4"}},
  387. },
  388. {
  389. {ID: "1", Name: "杭州", ChildrenIDs: []string{"1", "2"}},
  390. {ID: "2", Name: "宁波", ChildrenIDs: []string{"3", "4"}},
  391. {ID: "3", Name: "南京", ChildrenIDs: []string{"5", "6"}},
  392. {ID: "4", Name: "苏州", ChildrenIDs: []string{"7", "8"}},
  393. },
  394. {
  395. {ID: "1", Name: "拱墅区"},
  396. {ID: "2", Name: "西湖区"},
  397. {ID: "3", Name: "镇海区"},
  398. {ID: "4", Name: "鄞州区"},
  399. {ID: "5", Name: "鼓楼区"},
  400. {ID: "6", Name: "玄武区"},
  401. {ID: "7", Name: "常熟区"},
  402. {ID: "8", Name: "吴江区"},
  403. },
  404. },
  405. Labels: []string{"Province", "City", "District"},
  406. },
  407. },
  408. }),
  409. expectedSQLConds: "province_id = ? AND city_id = ? AND district_id = ?",
  410. expectedSQLArgs: []interface{}{"2", "3", "7"},
  411. },
  412. {
  413. name: "ItemTypeDate",
  414. data: FilterData([]*FilterItem{
  415. {
  416. Key: "created",
  417. ItemType: ItemTypeDate,
  418. SQLCondition: "created_at %s ?",
  419. },
  420. }),
  421. qs: "created=2019-04-10",
  422. expected: FilterData([]*FilterItem{
  423. {
  424. Key: "created",
  425. ItemType: ItemTypeDate,
  426. Modifier: ModifierEquals,
  427. Selected: true,
  428. ValueIs: "2019-04-10",
  429. },
  430. }),
  431. expectedSQLConds: "created_at >= ? AND created_at < ?",
  432. expectedSQLArgs: []interface{}{mustParseDatetimePickerValue("2019-04-10 00:00"), mustParseDatetimePickerValue("2019-04-11 00:00")},
  433. },
  434. }
  435. func TestSetByQueryString(t *testing.T) {
  436. for _, c := range setByQueryCases {
  437. t.Run(c.name, func(t *testing.T) {
  438. conds, args := c.data.SetByQueryString(c.qs)
  439. diff := testingutils.PrettyJsonDiff(c.expected, c.data)
  440. if len(diff) > 0 {
  441. t.Error(c.name, diff)
  442. }
  443. diff1 := testingutils.PrettyJsonDiff(c.expectedSQLConds, conds)
  444. if len(diff1) > 0 {
  445. t.Error(c.name, "conds", diff1)
  446. }
  447. diff2 := testingutils.PrettyJsonDiff(c.expectedSQLArgs, args)
  448. if len(diff2) > 0 {
  449. t.Error(c.name, "args", diff2)
  450. }
  451. })
  452. }
  453. }