@malloydata/malloy-tests 0.0.68-dev230808000650 → 0.0.68

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/dist/databases/all/db_index.spec.d.ts +1 -0
  2. package/dist/databases/all/db_index.spec.js +36 -0
  3. package/dist/databases/all/db_index.spec.js.map +1 -0
  4. package/dist/databases/all/expr.spec.d.ts +0 -2
  5. package/dist/databases/all/expr.spec.js +2 -547
  6. package/dist/databases/all/expr.spec.js.map +1 -1
  7. package/dist/databases/all/functions.spec.d.ts +1 -2
  8. package/dist/databases/all/functions.spec.js +2 -751
  9. package/dist/databases/all/functions.spec.js.map +1 -1
  10. package/dist/databases/all/join.spec.d.ts +1 -2
  11. package/dist/databases/all/join.spec.js +2 -276
  12. package/dist/databases/all/join.spec.js.map +1 -1
  13. package/dist/databases/all/nomodel.spec.d.ts +1 -2
  14. package/dist/databases/all/nomodel.spec.js +2 -923
  15. package/dist/databases/all/nomodel.spec.js.map +1 -1
  16. package/dist/databases/all/orderby.spec.d.ts +1 -2
  17. package/dist/databases/all/orderby.spec.js +2 -191
  18. package/dist/databases/all/orderby.spec.js.map +1 -1
  19. package/dist/databases/all/problems.spec.d.ts +1 -2
  20. package/dist/databases/all/problems.spec.js +2 -80
  21. package/dist/databases/all/problems.spec.js.map +1 -1
  22. package/dist/databases/all/sql_expressions.spec.d.ts +1 -2
  23. package/dist/databases/all/sql_expressions.spec.js +2 -62
  24. package/dist/databases/all/sql_expressions.spec.js.map +1 -1
  25. package/dist/databases/all/time.spec.d.ts +1 -3
  26. package/dist/databases/all/time.spec.js +2 -615
  27. package/dist/databases/all/time.spec.js.map +1 -1
  28. package/dist/databases/{all/index.spec.d.ts → shared/db_index.d.ts} +1 -0
  29. package/dist/databases/{all/index.spec.js → shared/db_index.js} +2 -33
  30. package/dist/databases/shared/db_index.js.map +1 -0
  31. package/dist/databases/shared/expr.d.ts +3 -0
  32. package/dist/databases/shared/expr.js +551 -0
  33. package/dist/databases/shared/expr.js.map +1 -0
  34. package/dist/databases/shared/functions.d.ts +3 -0
  35. package/dist/databases/shared/functions.js +754 -0
  36. package/dist/databases/shared/functions.js.map +1 -0
  37. package/dist/databases/shared/join.d.ts +3 -0
  38. package/dist/databases/shared/join.js +302 -0
  39. package/dist/databases/shared/join.js.map +1 -0
  40. package/dist/databases/shared/nomodel.d.ts +3 -0
  41. package/dist/databases/shared/nomodel.js +950 -0
  42. package/dist/databases/shared/nomodel.js.map +1 -0
  43. package/dist/databases/shared/orderby.d.ts +3 -0
  44. package/dist/databases/shared/orderby.js +217 -0
  45. package/dist/databases/shared/orderby.js.map +1 -0
  46. package/dist/databases/shared/problems.d.ts +3 -0
  47. package/dist/databases/shared/problems.js +106 -0
  48. package/dist/databases/shared/problems.js.map +1 -0
  49. package/dist/databases/shared/sql_expressions.d.ts +3 -0
  50. package/dist/databases/shared/sql_expressions.js +88 -0
  51. package/dist/databases/shared/sql_expressions.js.map +1 -0
  52. package/dist/databases/shared/test_list.js +18 -18
  53. package/dist/databases/shared/test_list.js.map +1 -1
  54. package/dist/databases/shared/time.d.ts +3 -0
  55. package/dist/databases/shared/time.js +640 -0
  56. package/dist/databases/shared/time.js.map +1 -0
  57. package/dist/index.d.ts +9 -10
  58. package/dist/index.js +19 -21
  59. package/dist/index.js.map +1 -1
  60. package/package.json +6 -6
  61. package/src/databases/all/db_index.spec.ts +37 -0
  62. package/src/databases/all/expr.spec.ts +2 -670
  63. package/src/databases/all/functions.spec.ts +1 -1101
  64. package/src/databases/all/join.spec.ts +1 -315
  65. package/src/databases/all/nomodel.spec.ts +2 -1124
  66. package/src/databases/all/orderby.spec.ts +1 -234
  67. package/src/databases/all/problems.spec.ts +1 -87
  68. package/src/databases/all/sql_expressions.spec.ts +1 -71
  69. package/src/databases/all/time.spec.ts +1 -761
  70. package/src/databases/{all/index.spec.ts → shared/db_index.ts} +2 -13
  71. package/src/databases/shared/expr.ts +695 -0
  72. package/src/databases/shared/functions.ts +1126 -0
  73. package/src/databases/shared/join.ts +340 -0
  74. package/src/databases/shared/nomodel.ts +1150 -0
  75. package/src/databases/shared/orderby.ts +260 -0
  76. package/src/databases/shared/problems.ts +113 -0
  77. package/src/databases/shared/sql_expressions.ts +96 -0
  78. package/src/databases/shared/test_list.ts +9 -9
  79. package/src/databases/shared/time.ts +786 -0
  80. package/src/index.ts +10 -11
  81. package/dist/databases/all/index.spec.js.map +0 -1
@@ -0,0 +1,1126 @@
1
+ /* eslint-disable no-console */
2
+ /*
3
+ * Copyright 2023 Google LLC
4
+ *
5
+ * Permission is hereby granted, free of charge, to any person obtaining
6
+ * a copy of this software and associated documentation files
7
+ * (the "Software"), to deal in the Software without restriction,
8
+ * including without limitation the rights to use, copy, modify, merge,
9
+ * publish, distribute, sublicense, and/or sell copies of the Software,
10
+ * and to permit persons to whom the Software is furnished to do so,
11
+ * subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be
14
+ * included in all copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+ */
24
+ import {RuntimeList} from '../../runtimes';
25
+ import '../../util/db-jest-matchers';
26
+ import * as malloy from '@malloydata/malloy';
27
+
28
+ export const functionsSharedTests = (
29
+ runtimes: RuntimeList,
30
+ _splitFunction?: (column: string, splitChar: string) => string
31
+ ) => {
32
+ const expressionModelText = `
33
+ source: aircraft_models is table('malloytest.aircraft_models'){
34
+ primary_key: aircraft_model_code
35
+ }
36
+
37
+ source: aircraft is table('malloytest.aircraft'){
38
+ primary_key: tail_num
39
+ join_one: aircraft_models with aircraft_model_code
40
+ measure: aircraft_count is count()
41
+ }
42
+
43
+ source: airports is table('malloytest.airports') {}
44
+
45
+ source: state_facts is table('malloytest.state_facts') {}
46
+ `;
47
+
48
+ const expressionModels = new Map<string, malloy.ModelMaterializer>();
49
+ runtimes.runtimeMap.forEach((runtime, databaseName) =>
50
+ expressionModels.set(databaseName, runtime.loadModel(expressionModelText))
51
+ );
52
+
53
+ expressionModels.forEach((expressionModel, databaseName) => {
54
+ const funcTestGeneral = async (
55
+ expr: string,
56
+ type: 'group_by' | 'aggregate',
57
+ expected:
58
+ | {error: string; success?: undefined}
59
+ | {success: string | boolean | number | null; error?: undefined}
60
+ ) => {
61
+ const run = async () => {
62
+ return await expressionModel
63
+ .loadQuery(
64
+ `
65
+ query: aircraft -> { ${type}: f is ${expr} }`
66
+ )
67
+ .run();
68
+ };
69
+
70
+ if (expected.success !== undefined) {
71
+ const result = await run();
72
+ expect(result.data.path(0, 'f').value).toBe(expected.success);
73
+ } else {
74
+ expect(run).rejects.toThrowError(expected.error);
75
+ }
76
+ };
77
+
78
+ const funcTest = (
79
+ expr: string,
80
+ expexted: string | boolean | number | null
81
+ ) => funcTestGeneral(expr, 'group_by', {success: expexted});
82
+
83
+ const funcTestAgg = (
84
+ expr: string,
85
+ expexted: string | boolean | number | null
86
+ ) => funcTestGeneral(expr, 'aggregate', {success: expexted});
87
+
88
+ const funcTestMultiple = async (
89
+ ...testCases: [string, string | boolean | number | null][]
90
+ ) => {
91
+ const run = async () => {
92
+ return await expressionModel
93
+ .loadQuery(
94
+ `
95
+ query: aircraft -> { ${testCases.map(
96
+ (testCase, i) => `group_by: f${i} is ${testCase[0]}`
97
+ )} }`
98
+ )
99
+ .run();
100
+ };
101
+
102
+ const result = await run();
103
+ testCases.forEach((testCase, i) => {
104
+ expect(result.data.path(0, `f${i}`).value).toBe(testCase[1]);
105
+ });
106
+ };
107
+
108
+ describe('concat', () => {
109
+ it(`works - ${databaseName}`, async () => {
110
+ await funcTestMultiple(
111
+ ["concat('foo', 'bar')", 'foobar'],
112
+ ["concat(1, 'bar')", '1bar'],
113
+ [
114
+ "concat('cons', true)",
115
+ databaseName === 'postgres' ? 'const' : 'construe',
116
+ ],
117
+ ["concat('foo', @2003)", 'foo2003-01-01'],
118
+ [
119
+ "concat('foo', @2003-01-01 12:00:00)",
120
+ databaseName === 'bigquery'
121
+ ? 'foo2003-01-01 12:00:00+00'
122
+ : 'foo2003-01-01 12:00:00',
123
+ ],
124
+ // TODO Maybe implement consistent null behavior
125
+ // ["concat('foo', null)", null],
126
+ ['concat()', '']
127
+ );
128
+ });
129
+ });
130
+
131
+ describe('round', () => {
132
+ it(`works - ${databaseName}`, async () => {
133
+ await funcTestMultiple(
134
+ ['round(1.2)', 1],
135
+ // TODO Remove when we upgrade to DuckDB 0.8.X -- DuckDB has some bugs with rounding
136
+ // that are fixed in 0.8.
137
+ ...(databaseName === 'duckdb_wasm'
138
+ ? []
139
+ : ([['round(12.222, 1)', 12.2]] as [string, number][])),
140
+ ['round(12.2, -1)', 10],
141
+ ['round(null)', null],
142
+ ['round(1, null)', null]
143
+ );
144
+ });
145
+ });
146
+
147
+ describe('floor', () => {
148
+ it(`works - ${databaseName}`, async () => {
149
+ await funcTestMultiple(
150
+ ['floor(1.9)', 1],
151
+ // TODO Remove when we upgrade to DuckDB 0.8.X -- DuckDB has some bugs with rounding
152
+ // that are fixed in 0.8.
153
+ ...(databaseName === 'duckdb_wasm'
154
+ ? []
155
+ : ([['floor(-1.9)', -2]] as [string, number][])),
156
+ ['floor(null)', null]
157
+ );
158
+ await funcTest('floor(1.9)', 1);
159
+ });
160
+ });
161
+
162
+ describe('ceil', () => {
163
+ it(`works - ${databaseName}`, async () => {
164
+ await funcTestMultiple(
165
+ ['ceil(1.9)', 2],
166
+ // TODO Remove when we upgrade to DuckDB 0.8.X -- DuckDB has some bugs with rounding
167
+ // that are fixed in 0.8.
168
+ ...(databaseName === 'duckdb_wasm'
169
+ ? []
170
+ : ([['ceil(-1.9)', -1]] as [string, number][])),
171
+ ['ceil(null)', null]
172
+ );
173
+ });
174
+ });
175
+
176
+ describe('length', () => {
177
+ it(`works - ${databaseName}`, async () => {
178
+ await funcTestMultiple(["length('foo')", 3], ['length(null)', null]);
179
+ });
180
+ });
181
+
182
+ describe('lower', () => {
183
+ it(`works - ${databaseName}`, async () => {
184
+ await funcTestMultiple(["lower('FoO')", 'foo'], ['lower(null)', null]);
185
+ });
186
+ });
187
+
188
+ describe('upper', () => {
189
+ it(`works - ${databaseName}`, async () => {
190
+ await funcTestMultiple(["upper('fOo')", 'FOO'], ['upper(null)', null]);
191
+ });
192
+ });
193
+
194
+ describe('regexp_extract', () => {
195
+ it(`works - ${databaseName}`, async () => {
196
+ await funcTestMultiple(
197
+ ["regexp_extract('I have a dog', r'd[aeiou]g')", 'dog'],
198
+ ["regexp_extract(null, r'd[aeiou]g')", null],
199
+ ["regexp_extract('foo', null)", null],
200
+ ["regexp_extract('I have a d0g', r'd\\dg')", 'd0g']
201
+ );
202
+ });
203
+ });
204
+
205
+ describe('replace', () => {
206
+ it(`works - ${databaseName}`, async () => {
207
+ await funcTestMultiple(
208
+ ["replace('aaaa', 'a', 'c')", 'cccc'],
209
+ ["replace('aaaa', r'.', 'c')", 'cccc'],
210
+ [
211
+ "replace('axbxc', r'(a).(b).(c)', '\\\\0 - \\\\1 - \\\\2 - \\\\3')",
212
+ databaseName === 'postgres'
213
+ ? '\\0 - a - b - c'
214
+ : 'axbxc - a - b - c',
215
+ ],
216
+ ["replace('aaaa', '', 'c')", 'aaaa'],
217
+ ["replace(null, 'a', 'c')", null],
218
+ ["replace('aaaa', null, 'c')", null],
219
+ ["replace('aaaa', 'a', null)", null]
220
+ );
221
+ });
222
+ });
223
+
224
+ describe('substr', () => {
225
+ it(`works - ${databaseName}`, async () => {
226
+ await funcTestMultiple(
227
+ ["substr('foo', 2)", 'oo'],
228
+ ["substr('foo', 2, 1)", 'o'],
229
+ ["substr('foo bar baz', -3)", 'baz'],
230
+ ['substr(null, 1, 2)', null],
231
+ ["substr('aaaa', null, 1)", null],
232
+ ["substr('aaaa', 1, null)", null]
233
+ );
234
+ });
235
+ });
236
+
237
+ describe('raw function call', () => {
238
+ it(`works - ${databaseName}`, async () => {
239
+ await funcTestMultiple(
240
+ ['floor(cbrt!(27)::number)', 3],
241
+ ['floor(cbrt!number(27))', 3],
242
+ ["substr('foo bar baz', -3)", 'baz'],
243
+ ['substr(null, 1, 2)', null],
244
+ ["substr('aaaa', null, 1)", null],
245
+ ["substr('aaaa', 1, null)", null]
246
+ );
247
+ });
248
+ });
249
+
250
+ describe('stddev', () => {
251
+ // TODO symmetric aggregates don't work with custom aggregate functions in BQ currently
252
+ if (databaseName === 'bigquery') return;
253
+ it(`works - ${databaseName}`, async () => {
254
+ await funcTestAgg('round(stddev(aircraft_models.seats))', 29);
255
+ });
256
+
257
+ it(`works with struct - ${databaseName}`, async () => {
258
+ await funcTestAgg(
259
+ 'round(aircraft_models.stddev(aircraft_models.seats))',
260
+ 41
261
+ );
262
+ });
263
+
264
+ it(`works with implicit parameter - ${databaseName}`, async () => {
265
+ await funcTestAgg('round(aircraft_models.seats.stddev())', 41);
266
+ });
267
+
268
+ it(`works with filter - ${databaseName}`, async () => {
269
+ await funcTestAgg(
270
+ 'round(aircraft_models.seats.stddev() { where: 1 = 1 })',
271
+ 41
272
+ );
273
+ await funcTestAgg(
274
+ 'round(aircraft_models.seats.stddev() { where: aircraft_models.seats > 4 })',
275
+ 69
276
+ );
277
+ });
278
+ });
279
+
280
+ describe('row_number', () => {
281
+ it(`works when the order by is a dimension - ${databaseName}`, async () => {
282
+ const result = await expressionModel
283
+ .loadQuery(
284
+ `query: state_facts -> {
285
+ group_by: state
286
+ calculate: row_num is row_number()
287
+ }`
288
+ )
289
+ .run();
290
+ expect(result.data.path(0, 'row_num').value).toBe(1);
291
+ expect(result.data.path(1, 'row_num').value).toBe(2);
292
+ });
293
+
294
+ it(`works when the order by is a dimension in the other order - ${databaseName}`, async () => {
295
+ const result = await expressionModel
296
+ .loadQuery(
297
+ `query: state_facts -> {
298
+ calculate: row_num is row_number()
299
+ group_by: state
300
+ }`
301
+ )
302
+ .run();
303
+ expect(result.data.path(0, 'row_num').value).toBe(1);
304
+ expect(result.data.path(1, 'row_num').value).toBe(2);
305
+ });
306
+
307
+ it(`works when the order by is a measure - ${databaseName}`, async () => {
308
+ const result = await expressionModel
309
+ .loadQuery(
310
+ `query: state_facts -> {
311
+ group_by: popular_name
312
+ aggregate: c is count()
313
+ calculate: row_num is row_number()
314
+ }`
315
+ )
316
+ .run();
317
+ expect(result.data.path(0, 'row_num').value).toBe(1);
318
+ expect(result.data.path(1, 'row_num').value).toBe(2);
319
+ });
320
+
321
+ it(`works when the order by is a measure but there is no group by - ${databaseName}`, async () => {
322
+ const result = await expressionModel
323
+ .loadQuery(
324
+ `query: state_facts -> {
325
+ aggregate: c is count()
326
+ calculate: row_num is row_number()
327
+ }`
328
+ )
329
+ .run();
330
+ expect(result.data.path(0, 'row_num').value).toBe(1);
331
+ });
332
+
333
+ it(`works inside nest - ${databaseName}`, async () => {
334
+ const result = await expressionModel
335
+ .loadQuery(
336
+ `query: state_facts { join_one: airports on airports.state = state } -> {
337
+ group_by: state
338
+ nest: q is {
339
+ group_by: airports.county
340
+ calculate: row_num is row_number()
341
+ }
342
+ }`
343
+ )
344
+ .run();
345
+ expect(result.data.path(0, 'q', 0, 'row_num').value).toBe(1);
346
+ expect(result.data.path(0, 'q', 1, 'row_num').value).toBe(2);
347
+ expect(result.data.path(1, 'q', 0, 'row_num').value).toBe(1);
348
+ expect(result.data.path(1, 'q', 1, 'row_num').value).toBe(2);
349
+ });
350
+
351
+ test(`works outside nest, but with a nest nearby - ${databaseName}`, async () => {
352
+ const result = await expressionModel
353
+ .loadQuery(
354
+ `query: state_facts -> {
355
+ group_by: state
356
+ calculate: row_num is row_number()
357
+ nest: nested is {
358
+ group_by: state
359
+ }
360
+ }`
361
+ )
362
+ .run();
363
+ expect(result.data.path(0, 'row_num').value).toBe(1);
364
+ expect(result.data.path(1, 'row_num').value).toBe(2);
365
+ });
366
+ });
367
+
368
+ describe('rank', () => {
369
+ it(`works ordered by dimension - ${databaseName}`, async () => {
370
+ const result = await expressionModel
371
+ .loadQuery(
372
+ `query: state_facts -> {
373
+ group_by:
374
+ state,
375
+ births_ballpark is ceil(births / 1000000) * 1000000
376
+ order_by: births_ballpark desc
377
+ calculate: births_ballpark_rank is rank()
378
+ limit: 20
379
+ }`
380
+ )
381
+ .run({rowLimit: 20});
382
+ expect(result.data.path(0, 'births_ballpark_rank').value).toBe(1);
383
+ expect(result.data.path(1, 'births_ballpark_rank').value).toBe(2);
384
+ expect(result.data.path(8, 'births_ballpark_rank').value).toBe(9);
385
+ expect(result.data.path(9, 'births_ballpark_rank').value).toBe(9);
386
+ expect(result.data.path(10, 'births_ballpark_rank').value).toBe(9);
387
+ expect(result.data.path(11, 'births_ballpark_rank').value).toBe(12);
388
+ });
389
+
390
+ it(`works ordered by aggregate - ${databaseName}`, async () => {
391
+ const result = await expressionModel
392
+ .loadQuery(
393
+ `query: state_facts -> {
394
+ group_by: first_letter is substr(state, 1, 1)
395
+ aggregate: states_with_first_letter_ish is round(count() / 2) * 2
396
+ calculate: r is rank()
397
+ }`
398
+ )
399
+ .run();
400
+ expect(result.data.path(0, 'r').value).toBe(1);
401
+ expect(result.data.path(1, 'r').value).toBe(1);
402
+ expect(result.data.path(2, 'r').value).toBe(3);
403
+ expect(result.data.path(3, 'r').value).toBe(3);
404
+ });
405
+ });
406
+
407
+ describe('lag', () => {
408
+ it(`works with one param - ${databaseName}`, async () => {
409
+ const result = await expressionModel
410
+ .loadQuery(
411
+ `query: state_facts -> {
412
+ group_by: state
413
+ calculate: prev_state is lag(state)
414
+ }`
415
+ )
416
+ .run();
417
+ expect(result.data.path(0, 'state').value).toBe('AK');
418
+ expect(result.data.path(0, 'prev_state').value).toBe(null);
419
+ expect(result.data.path(1, 'prev_state').value).toBe('AK');
420
+ expect(result.data.path(1, 'state').value).toBe('AL');
421
+ expect(result.data.path(2, 'prev_state').value).toBe('AL');
422
+ });
423
+
424
+ it(`works with expression field - ${databaseName}`, async () => {
425
+ const result = await expressionModel
426
+ .loadQuery(
427
+ `query: state_facts -> {
428
+ group_by: lower_state is lower(state)
429
+ calculate: prev_state is lag(lower_state)
430
+ }`
431
+ )
432
+ .run();
433
+ expect(result.data.path(0, 'lower_state').value).toBe('ak');
434
+ expect(result.data.path(0, 'prev_state').value).toBe(null);
435
+ expect(result.data.path(1, 'prev_state').value).toBe('ak');
436
+ expect(result.data.path(1, 'lower_state').value).toBe('al');
437
+ expect(result.data.path(2, 'prev_state').value).toBe('al');
438
+ });
439
+
440
+ it(`works with expression - ${databaseName}`, async () => {
441
+ const result = await expressionModel
442
+ .loadQuery(
443
+ `query: state_facts -> {
444
+ group_by: state
445
+ calculate: prev_state is lag(lower(state))
446
+ }`
447
+ )
448
+ .run();
449
+ expect(result.data.path(0, 'state').value).toBe('AK');
450
+ expect(result.data.path(0, 'prev_state').value).toBe(null);
451
+ expect(result.data.path(1, 'prev_state').value).toBe('ak');
452
+ expect(result.data.path(1, 'state').value).toBe('AL');
453
+ expect(result.data.path(2, 'prev_state').value).toBe('al');
454
+ });
455
+
456
+ it(`works with field, ordering by expression field - ${databaseName}`, async () => {
457
+ const result = await expressionModel
458
+ .loadQuery(
459
+ `query: state_facts -> {
460
+ group_by: lower_state is lower(state)
461
+ aggregate: c is count()
462
+ order_by: lower_state
463
+ calculate: prev_count is lag(c)
464
+ }`
465
+ )
466
+ .run();
467
+ expect(result.data.path(0, 'lower_state').value).toBe('ak');
468
+ expect(result.data.path(0, 'prev_count').value).toBe(null);
469
+ expect(result.data.path(1, 'prev_count').value).toBe(1);
470
+ expect(result.data.path(1, 'lower_state').value).toBe('al');
471
+ expect(result.data.path(2, 'prev_count').value).toBe(1);
472
+ });
473
+
474
+ it(`works with offset - ${databaseName}`, async () => {
475
+ const result = await expressionModel
476
+ .loadQuery(
477
+ `query: state_facts -> {
478
+ group_by: state
479
+ calculate: prev_prev_state is lag(state, 2)
480
+ }`
481
+ )
482
+ .run();
483
+ expect(result.data.path(0, 'state').value).toBe('AK');
484
+ expect(result.data.path(0, 'prev_prev_state').value).toBe(null);
485
+ expect(result.data.path(1, 'prev_prev_state').value).toBe(null);
486
+ expect(result.data.path(2, 'prev_prev_state').value).toBe('AK');
487
+ expect(result.data.path(1, 'state').value).toBe('AL');
488
+ expect(result.data.path(3, 'prev_prev_state').value).toBe('AL');
489
+ });
490
+
491
+ it(`works with default value - ${databaseName}`, async () => {
492
+ const result = await expressionModel
493
+ .loadQuery(
494
+ `query: state_facts -> {
495
+ group_by: state
496
+ calculate: prev_state is lag(state, 1, 'NONE')
497
+ }`
498
+ )
499
+ .run();
500
+ expect(result.data.path(0, 'prev_state').value).toBe('NONE');
501
+ });
502
+
503
+ it(`works with now as the default value - ${databaseName}`, async () => {
504
+ const result = await expressionModel
505
+ .loadQuery(
506
+ `
507
+ query: state_facts -> {
508
+ group_by: state
509
+ calculate: lag_val is lag(@2011-11-11 11:11:11, 1, now).year = now.year
510
+ }`
511
+ )
512
+ .run();
513
+ expect(result.data.path(0, 'lag_val').value).toBe(true);
514
+ expect(result.data.path(1, 'lag_val').value).toBe(false);
515
+ });
516
+ });
517
+
518
+ describe('output field in calculate', () => {
519
+ it(`dotted aggregates work with an output field - ${databaseName}`, async () => {
520
+ const result = await expressionModel
521
+ .loadQuery(
522
+ `query: aircraft -> {
523
+ group_by: aircraft_models.seats
524
+ aggregate: s is aircraft_models.seats.sum()
525
+ calculate: a is lag(seats.sum())
526
+ }`
527
+ )
528
+ .run();
529
+ expect(result.data.path(1, 'a').value).toBe(
530
+ result.data.path(0, 's').value
531
+ );
532
+ });
533
+ });
534
+
535
+ describe('first_value', () => {
536
+ test(`works in nest - ${databaseName}`, async () => {
537
+ const result = await expressionModel
538
+ .loadQuery(
539
+ `
540
+ query: aircraft -> {
541
+ group_by: state
542
+ where: state != null
543
+ nest: by_county is {
544
+ limit: 2
545
+ group_by: county
546
+ aggregate: aircraft_count
547
+ calculate: first_count is first_value(count())
548
+ }
549
+ }`
550
+ )
551
+ .run();
552
+ expect(result.data.path(0, 'by_county', 1, 'first_count').value).toBe(
553
+ result.data.path(0, 'by_county', 0, 'aircraft_count').value
554
+ );
555
+ expect(result.data.path(1, 'by_county', 1, 'first_count').value).toBe(
556
+ result.data.path(1, 'by_county', 0, 'aircraft_count').value
557
+ );
558
+ });
559
+ it(`works outside nest - ${databaseName}`, async () => {
560
+ const result = await expressionModel
561
+ .loadQuery(
562
+ `
563
+ query: state_facts -> {
564
+ group_by: state, births
565
+ order_by: births desc
566
+ calculate: most_births is first_value(births)
567
+ }`
568
+ )
569
+ .run();
570
+ const firstBirths = result.data.path(0, 'births').value;
571
+ expect(result.data.path(0, 'most_births').value).toBe(firstBirths);
572
+ expect(result.data.path(1, 'most_births').value).toBe(firstBirths);
573
+ });
574
+ it(`works with an aggregate which is not in the query - ${databaseName}`, async () => {
575
+ const result = await expressionModel
576
+ .loadQuery(
577
+ `
578
+ query: airports { measure: airport_count is count() } -> {
579
+ group_by: state
580
+ where: state != null
581
+ calculate: prev_airport_count is lag(airport_count)
582
+ }`
583
+ )
584
+ .run();
585
+ expect(result.data.path(0, 'prev_airport_count').value).toBe(null);
586
+ expect(result.data.path(1, 'prev_airport_count').value).toBe(608);
587
+ expect(result.data.path(2, 'prev_airport_count').value).toBe(260);
588
+ });
589
+ it(`works with a localized aggregate - ${databaseName}`, async () => {
590
+ const result = await expressionModel
591
+ .loadQuery(
592
+ `
593
+ query: aircraft -> {
594
+ group_by: aircraft_models.seats,
595
+ calculate: prev_sum_of_seats is lag(aircraft_models.seats.sum())
596
+ }`
597
+ )
598
+ .run();
599
+ expect(result.data.path(0, 'prev_sum_of_seats').value).toBe(null);
600
+ expect(result.data.path(1, 'prev_sum_of_seats').value).toBe(0);
601
+ expect(result.data.path(2, 'prev_sum_of_seats').value).toBe(230);
602
+ });
603
+ });
604
+
605
+ describe('trunc', () => {
606
+ it(`works - ${databaseName}`, async () => {
607
+ await funcTestMultiple(
608
+ ['trunc(1.9)', 1],
609
+ // TODO Remove when we upgrade to DuckDB 0.8.X -- DuckDB has some bugs with rounding
610
+ // that are fixed in 0.8.
611
+ ...(databaseName === 'duckdb_wasm'
612
+ ? []
613
+ : ([['trunc(-1.9)', -1]] as [string, number][])),
614
+ ['trunc(12.29, 1)', 12.2],
615
+ ['trunc(19.2, -1)', 10],
616
+ ['trunc(null)', null],
617
+ ['trunc(1, null)', null]
618
+ );
619
+ });
620
+ });
621
+ describe('log', () => {
622
+ it(`works - ${databaseName}`, async () => {
623
+ await funcTestMultiple(
624
+ ['log(10, 10)', 1],
625
+ ['log(100, 10)', 2],
626
+ ['log(32, 2)', 5],
627
+ ['log(null, 2)', null],
628
+ ['log(10, null)', null]
629
+ );
630
+ });
631
+ });
632
+ describe('ln', () => {
633
+ it(`works - ${databaseName}`, async () => {
634
+ await funcTestMultiple(
635
+ ['ln(exp(1))', 1],
636
+ ['ln(exp(2))', 2],
637
+ ['ln(null)', null]
638
+ );
639
+ });
640
+ });
641
+ describe('exp', () => {
642
+ it(`works - ${databaseName}`, async () => {
643
+ await funcTestMultiple(
644
+ ['exp(0)', 1],
645
+ ['ln(exp(1))', 1],
646
+ ['exp(null)', null]
647
+ );
648
+ });
649
+ });
650
+
651
+ // TODO trig functions could have more exhaustive tests -- these are mostly just here to
652
+ // ensure they exist
653
+ describe('cos', () => {
654
+ it(`works - ${databaseName}`, async () => {
655
+ await funcTestMultiple(['cos(0)', 1], ['cos(null)', null]);
656
+ });
657
+ });
658
+ describe('acos', () => {
659
+ it(`works - ${databaseName}`, async () => {
660
+ await funcTestMultiple(['acos(1)', 0], ['acos(null)', null]);
661
+ });
662
+ });
663
+
664
+ describe('sin', () => {
665
+ it(`works - ${databaseName}`, async () => {
666
+ await funcTestMultiple(['sin(0)', 0], ['sin(null)', null]);
667
+ });
668
+ });
669
+ describe('asin', () => {
670
+ it(`works - ${databaseName}`, async () => {
671
+ await funcTestMultiple(['asin(0)', 0], ['asin(null)', null]);
672
+ });
673
+ });
674
+
675
+ describe('tan', () => {
676
+ it(`works - ${databaseName}`, async () => {
677
+ await funcTestMultiple(['tan(0)', 0], ['tan(null)', null]);
678
+ });
679
+ });
680
+ describe('atan', () => {
681
+ it(`works - ${databaseName}`, async () => {
682
+ await funcTestMultiple(['atan(0)', 0], ['atan(null)', null]);
683
+ });
684
+ });
685
+ describe('atan2', () => {
686
+ it(`works - ${databaseName}`, async () => {
687
+ await funcTestMultiple(
688
+ ['atan2(0, 1)', 0],
689
+ ['atan2(null, 1)', null],
690
+ ['atan2(1, null)', null]
691
+ );
692
+ });
693
+ });
694
+ describe('sqrt', () => {
695
+ it(`works - ${databaseName}`, async () => {
696
+ await funcTestMultiple(
697
+ ['sqrt(9)', 3],
698
+ ['sqrt(6.25)', 2.5],
699
+ ['sqrt(null)', null]
700
+ );
701
+ });
702
+ });
703
+ describe('pow', () => {
704
+ it(`works - ${databaseName}`, async () => {
705
+ await funcTestMultiple(
706
+ ['pow(2, 3)', 8],
707
+ ['pow(null, 3)', null],
708
+ ['pow(2, null)', null]
709
+ );
710
+ });
711
+ });
712
+ describe('abs', () => {
713
+ it(`works - ${databaseName}`, async () => {
714
+ await funcTestMultiple(
715
+ ['abs(-3)', 3],
716
+ ['abs(3)', 3],
717
+ ['abs(null)', null]
718
+ );
719
+ });
720
+ });
721
+ describe('sign', () => {
722
+ it(`works - ${databaseName}`, async () => {
723
+ await funcTestMultiple(
724
+ ['sign(100)', 1],
725
+ ['sign(-2)', -1],
726
+ ['sign(0)', 0],
727
+ ['sign(null)', null]
728
+ );
729
+ });
730
+ });
731
+ describe('is_inf', () => {
732
+ it(`works - ${databaseName}`, async () => {
733
+ await funcTestMultiple(
734
+ ["is_inf('+inf'::number)", true],
735
+ ['is_inf(100)', false],
736
+ ['is_inf(null)', false]
737
+ );
738
+ });
739
+ });
740
+ describe('is_nan', () => {
741
+ it(`works - ${databaseName}`, async () => {
742
+ await funcTestMultiple(
743
+ ["is_nan('NaN'::number)", true],
744
+ ['is_nan(100)', false],
745
+ ['is_nan(null)', false]
746
+ );
747
+ });
748
+ });
749
+ describe('greatest', () => {
750
+ it(`works - ${databaseName}`, async () => {
751
+ await funcTestMultiple(
752
+ ['greatest(1, 10, -100)', 10],
753
+ ['greatest(@2003, @2004, @1994) = @2004', true],
754
+ [
755
+ 'greatest(@2023-05-26 11:58:00, @2023-05-26 11:59:00) = @2023-05-26 11:59:00',
756
+ true,
757
+ ],
758
+ ["greatest('a', 'b')", 'b'],
759
+ ['greatest(1, null, 0)', null],
760
+ ['greatest(null, null)', null]
761
+ );
762
+ });
763
+ });
764
+ describe('least', () => {
765
+ it(`works - ${databaseName}`, async () => {
766
+ await funcTestMultiple(
767
+ ['least(1, 10, -100)', -100],
768
+ ['least(@2003, @2004, @1994) = @1994', true],
769
+ [
770
+ 'least(@2023-05-26 11:58:00, @2023-05-26 11:59:00) = @2023-05-26 11:58:00',
771
+ true,
772
+ ],
773
+ ["least('a', 'b')", 'a'],
774
+ ['least(1, null, 0)', null],
775
+ ['least(null, null)', null]
776
+ );
777
+ });
778
+ });
779
+ describe('div', () => {
780
+ it(`works - ${databaseName}`, async () => {
781
+ await funcTestMultiple(
782
+ ['div(3, 2)', 1],
783
+ ['div(null, 2)', null],
784
+ ['div(2, null)', null]
785
+ );
786
+ });
787
+ });
788
+ describe('strpos', () => {
789
+ it(`works - ${databaseName}`, async () => {
790
+ await funcTestMultiple(
791
+ ["strpos('123456789', '3')", 3],
792
+ ["strpos('123456789', '0')", 0],
793
+ ["strpos(null, '0')", null],
794
+ ["strpos('123456789', null)", null]
795
+ );
796
+ });
797
+ });
798
+ describe('starts_with', () => {
799
+ it(`works - ${databaseName}`, async () => {
800
+ await funcTestMultiple(
801
+ ["starts_with('hello world', 'hello')", true],
802
+ ["starts_with('hello world', 'world')", false],
803
+ ["starts_with(null, 'world')", false],
804
+ ["starts_with('hello world', null)", false]
805
+ );
806
+ });
807
+ });
808
+ describe('ends_with', () => {
809
+ it(`works - ${databaseName}`, async () => {
810
+ await funcTestMultiple(
811
+ ["ends_with('hello world', 'world')", true],
812
+ ["ends_with('hello world', 'hello')", false],
813
+ ["ends_with(null, 'world')", false],
814
+ ["ends_with('hello world', null)", false]
815
+ );
816
+ });
817
+ });
818
+ describe('trim', () => {
819
+ it(`works - ${databaseName}`, async () => {
820
+ await funcTestMultiple(
821
+ ["trim(' keep this ')", 'keep this'],
822
+ ["trim('_ _keep_this_ _', '_ ')", 'keep_this'],
823
+ ["trim(' keep everything ', '')", ' keep everything '],
824
+ ["trim('null example', null)", null],
825
+ ["trim(null, 'a')", null],
826
+ ['trim(null)', null]
827
+ );
828
+ });
829
+ });
830
+ describe('ltrim', () => {
831
+ it(`works - ${databaseName}`, async () => {
832
+ await funcTestMultiple(
833
+ ["ltrim(' keep this -> ')", 'keep this -> '],
834
+ ["ltrim('_ _keep_this -> _ _', '_ ')", 'keep_this -> _ _'],
835
+ ["ltrim(' keep everything ', '')", ' keep everything '],
836
+ ["ltrim('null example', null)", null],
837
+ ["ltrim(null, 'a')", null],
838
+ ['ltrim(null)', null]
839
+ );
840
+ });
841
+ });
842
+ describe('rtrim', () => {
843
+ it(`works - ${databaseName}`, async () => {
844
+ await funcTestMultiple(
845
+ ["rtrim(' <- keep this ')", ' <- keep this'],
846
+ ["rtrim('_ _ <- keep_this_ _', '_ ')", '_ _ <- keep_this'],
847
+ ["rtrim(' keep everything ', '')", ' keep everything '],
848
+ ["rtrim('null example', null)", null],
849
+ ["rtrim(null, 'a')", null],
850
+ ['rtrim(null)', null]
851
+ );
852
+ });
853
+ });
854
+ describe('rand', () => {
855
+ it(`is usually not the same value - ${databaseName}`, async () => {
856
+ // There are around a billion values that rand() can be, so if this
857
+ // test fails, most likely something is broken. Otherwise, you're the lucky
858
+ // one in a billion!
859
+ await funcTest('rand() = rand()', false);
860
+ });
861
+ });
862
+ describe('pi', () => {
863
+ it(`is pi - ${databaseName}`, async () => {
864
+ await funcTest('abs(pi() - 3.141592653589793) < 0.0000000000001', true);
865
+ });
866
+ });
867
+
868
+ describe('byte_length', () => {
869
+ it(`works - ${databaseName}`, async () => {
870
+ await funcTestMultiple(
871
+ ["byte_length('hello')", 5],
872
+ ["byte_length('©')", 2],
873
+ ['byte_length(null)', null]
874
+ );
875
+ });
876
+ });
877
+ describe('ifnull', () => {
878
+ it(`works - ${databaseName}`, async () => {
879
+ await funcTestMultiple(
880
+ ["ifnull('a', 'b')", 'a'],
881
+ ["ifnull(null, 'b')", 'b'],
882
+ ["ifnull('a', null)", 'a'],
883
+ ['ifnull(null, null)', null]
884
+ );
885
+ });
886
+ });
887
+ describe('coalesce', () => {
888
+ it(`works - ${databaseName}`, async () => {
889
+ await funcTestMultiple(
890
+ ["coalesce('a')", 'a'],
891
+ ["coalesce('a', 'b')", 'a'],
892
+ ["coalesce(null, 'a', 'b')", 'a'],
893
+ ["coalesce(null, 'b')", 'b'],
894
+ ["coalesce('a', null)", 'a'],
895
+ ['coalesce(null, null)', null],
896
+ ['coalesce(null)', null]
897
+ );
898
+ });
899
+ });
900
+ describe('nullif', () => {
901
+ it(`works - ${databaseName}`, async () => {
902
+ await funcTestMultiple(
903
+ ["nullif('a', 'a')", null],
904
+ ["nullif('a', 'b')", 'a'],
905
+ ["nullif('a', null)", 'a'],
906
+ ['nullif(null, null)', null],
907
+ ['nullif(null, 2)', null]
908
+ );
909
+ });
910
+ });
911
+ describe('chr', () => {
912
+ it(`works - ${databaseName}`, async () => {
913
+ await funcTestMultiple(
914
+ ['chr(65)', 'A'],
915
+ ['chr(255)', 'ÿ'],
916
+ ['chr(null)', null],
917
+ // BigQuery's documentation says that `chr(0)` returns the empty string, but it doesn't,
918
+ // it actually returns the null character. We generate code so that it does this.
919
+ ['chr(0)', '']
920
+ );
921
+ });
922
+ });
923
+ describe('ascii', () => {
924
+ it(`works - ${databaseName}`, async () => {
925
+ await funcTestMultiple(
926
+ ["ascii('A')", 65],
927
+ ["ascii('ABC')", 65],
928
+ ["ascii('')", 0],
929
+ ['ascii(null)', null]
930
+ );
931
+ });
932
+ });
933
+ describe('unicode', () => {
934
+ it(`works - ${databaseName}`, async () => {
935
+ await funcTestMultiple(
936
+ ["unicode('A')", 65],
937
+ ["unicode('â')", 226],
938
+ ["unicode('âBC')", 226],
939
+ ["unicode('')", 0],
940
+ ['unicode(null)', null]
941
+ );
942
+ });
943
+ });
944
+
945
+ describe('repeat', () => {
946
+ it(`works - ${databaseName}`, async () => {
947
+ await funcTestMultiple(
948
+ ["repeat('foo', 0)", ''],
949
+ ["repeat('foo', 1)", 'foo'],
950
+ ["repeat('foo', 2)", 'foofoo'],
951
+ ['repeat(null, 2)', null],
952
+ ["repeat('foo', null)", null]
953
+ );
954
+ });
955
+ // TODO how does a user do this: the second argument needs to be an integer, but floor doesn't cast to "integer" type.
956
+ it.skip(`works floor decimal - ${databaseName}`, async () => {
957
+ await funcTest("repeat('foo', floor(2.5))", 'foofoo');
958
+ });
959
+ // undefined behavior when negative, undefined behavior (likely error) when non-integer
960
+ });
961
+ describe('reverse', () => {
962
+ it(`works - ${databaseName}`, async () => {
963
+ await funcTestMultiple(
964
+ ["reverse('foo')", 'oof'],
965
+ ["reverse('')", ''],
966
+ ['reverse(null)', null]
967
+ );
968
+ });
969
+ });
970
+
971
+ describe('lead', () => {
972
+ it(`works with one param - ${databaseName}`, async () => {
973
+ const result = await expressionModel
974
+ .loadQuery(
975
+ `query: state_facts -> {
976
+ group_by: state
977
+ calculate: next_state is lead(state)
978
+ }`
979
+ )
980
+ .run();
981
+ expect(result.data.path(0, 'state').value).toBe('AK');
982
+ expect(result.data.path(0, 'next_state').value).toBe('AL');
983
+ expect(result.data.path(1, 'state').value).toBe('AL');
984
+ });
985
+
986
+ it(`works with offset - ${databaseName}`, async () => {
987
+ const result = await expressionModel
988
+ .loadQuery(
989
+ `query: state_facts -> {
990
+ group_by: state
991
+ calculate: next_next_state is lead(state, 2)
992
+ }`
993
+ )
994
+ .run();
995
+ expect(result.data.path(0, 'state').value).toBe('AK');
996
+ expect(result.data.path(0, 'next_next_state').value).toBe('AR');
997
+ expect(result.data.path(1, 'next_next_state').value).toBe('AZ');
998
+ expect(result.data.path(1, 'state').value).toBe('AL');
999
+ expect(result.data.path(2, 'state').value).toBe('AR');
1000
+ expect(result.data.path(3, 'state').value).toBe('AZ');
1001
+ });
1002
+
1003
+ it(`works with default value - ${databaseName}`, async () => {
1004
+ const result = await expressionModel
1005
+ .loadQuery(
1006
+ `query: state_facts -> { project: *; limit: 10 } -> {
1007
+ group_by: state
1008
+ calculate: next_state is lead(state, 1, 'NONE')
1009
+ }`
1010
+ )
1011
+ .run();
1012
+ expect(result.data.path(9, 'next_state').value).toBe('NONE');
1013
+ });
1014
+ });
1015
+ describe('last_value', () => {
1016
+ it(`works - ${databaseName}`, async () => {
1017
+ const result = await expressionModel
1018
+ .loadQuery(
1019
+ `
1020
+ query: state_facts -> {
1021
+ group_by: state, births
1022
+ order_by: births desc
1023
+ calculate: least_births is last_value(births)
1024
+ }`
1025
+ )
1026
+ .run({rowLimit: 100});
1027
+ const numRows = result.data.toObject().length;
1028
+ const lastBirths = result.data.path(numRows - 1, 'births').value;
1029
+ expect(result.data.path(0, 'least_births').value).toBe(lastBirths);
1030
+ expect(result.data.path(1, 'least_births').value).toBe(lastBirths);
1031
+ });
1032
+ });
1033
+ describe('avg_moving', () => {
1034
+ it(`works - ${databaseName}`, async () => {
1035
+ const result = await expressionModel
1036
+ .loadQuery(
1037
+ `
1038
+ query: state_facts -> {
1039
+ group_by: state, births
1040
+ order_by: births desc
1041
+ calculate: rolling_avg is avg_moving(births, 2)
1042
+ }`
1043
+ )
1044
+ .run({rowLimit: 100});
1045
+ const births0 = result.data.path(0, 'births').number.value;
1046
+ const births1 = result.data.path(1, 'births').number.value;
1047
+ const births2 = result.data.path(2, 'births').number.value;
1048
+ const births3 = result.data.path(3, 'births').number.value;
1049
+ expect(result.data.path(0, 'rolling_avg').number.value).toBe(births0);
1050
+ expect(
1051
+ Math.floor(result.data.path(1, 'rolling_avg').number.value)
1052
+ ).toBe(Math.floor((births0 + births1) / 2));
1053
+ expect(
1054
+ Math.floor(result.data.path(2, 'rolling_avg').number.value)
1055
+ ).toBe(Math.floor((births0 + births1 + births2) / 3));
1056
+ expect(
1057
+ Math.floor(result.data.path(3, 'rolling_avg').number.value)
1058
+ ).toBe(Math.floor((births1 + births2 + births3) / 3));
1059
+ });
1060
+
1061
+ it(`works forward - ${databaseName}`, async () => {
1062
+ const result = await expressionModel
1063
+ .loadQuery(
1064
+ `
1065
+ query: state_facts -> { project: *; limit: 3 } -> {
1066
+ group_by: state, births
1067
+ order_by: births desc
1068
+ calculate: rolling_avg is avg_moving(births, 0, 2)
1069
+ }`
1070
+ )
1071
+ .run({rowLimit: 100});
1072
+ const births0 = result.data.path(0, 'births').number.value;
1073
+ const births1 = result.data.path(1, 'births').number.value;
1074
+ const births2 = result.data.path(2, 'births').number.value;
1075
+ expect(
1076
+ Math.floor(result.data.path(0, 'rolling_avg').number.value)
1077
+ ).toBe(Math.floor((births0 + births1 + births2) / 3));
1078
+ expect(
1079
+ Math.floor(result.data.path(1, 'rolling_avg').number.value)
1080
+ ).toBe(Math.floor((births1 + births2) / 2));
1081
+ expect(result.data.path(2, 'rolling_avg').number.value).toBe(births2);
1082
+ });
1083
+ });
1084
+ describe('min, max, sum / window, cumulative', () => {
1085
+ it(`works - ${databaseName}`, async () => {
1086
+ const result = await expressionModel
1087
+ .loadQuery(
1088
+ `
1089
+ query: state_facts -> { project: *; limit: 5 } -> {
1090
+ group_by: state, births
1091
+ order_by: births asc
1092
+ calculate: min_c is min_cumulative(births)
1093
+ calculate: max_c is max_cumulative(births)
1094
+ calculate: sum_c is sum_cumulative(births)
1095
+ calculate: min_w is min_window(births)
1096
+ calculate: max_w is max_window(births)
1097
+ calculate: sum_w is sum_window(births)
1098
+ }`
1099
+ )
1100
+ .run({rowLimit: 100});
1101
+ const births0 = result.data.path(0, 'births').number.value;
1102
+ const births1 = result.data.path(1, 'births').number.value;
1103
+ const births2 = result.data.path(2, 'births').number.value;
1104
+ const births3 = result.data.path(3, 'births').number.value;
1105
+ const births4 = result.data.path(4, 'births').number.value;
1106
+ const births = [births0, births1, births2, births3, births4];
1107
+ for (let r = 0; r < 5; r++) {
1108
+ expect(result.data.path(r, 'min_c').number.value).toBe(births0);
1109
+ expect(result.data.path(r, 'max_c').number.value).toBe(births[r]);
1110
+ expect(result.data.path(r, 'sum_c').number.value).toBe(
1111
+ births.slice(0, r + 1).reduce((a, b) => a + b)
1112
+ );
1113
+ expect(result.data.path(r, 'min_w').number.value).toBe(births0);
1114
+ expect(result.data.path(r, 'max_w').number.value).toBe(births4);
1115
+ expect(result.data.path(r, 'sum_w').number.value).toBe(
1116
+ births.reduce((a, b) => a + b)
1117
+ );
1118
+ }
1119
+ });
1120
+ });
1121
+ });
1122
+
1123
+ afterAll(async () => {
1124
+ await runtimes.closeAll();
1125
+ });
1126
+ };