@malloydata/malloy-tests 0.0.149 → 0.0.150
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.
package/package.json
CHANGED
|
@@ -21,13 +21,13 @@
|
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@jest/globals": "^29.4.3",
|
|
24
|
-
"@malloydata/db-bigquery": "^0.0.
|
|
25
|
-
"@malloydata/db-duckdb": "^0.0.
|
|
26
|
-
"@malloydata/db-postgres": "^0.0.
|
|
27
|
-
"@malloydata/db-snowflake": "^0.0.
|
|
28
|
-
"@malloydata/db-trino": "^0.0.
|
|
29
|
-
"@malloydata/malloy": "^0.0.
|
|
30
|
-
"@malloydata/render": "^0.0.
|
|
24
|
+
"@malloydata/db-bigquery": "^0.0.150",
|
|
25
|
+
"@malloydata/db-duckdb": "^0.0.150",
|
|
26
|
+
"@malloydata/db-postgres": "^0.0.150",
|
|
27
|
+
"@malloydata/db-snowflake": "^0.0.150",
|
|
28
|
+
"@malloydata/db-trino": "^0.0.150",
|
|
29
|
+
"@malloydata/malloy": "^0.0.150",
|
|
30
|
+
"@malloydata/render": "^0.0.150",
|
|
31
31
|
"jsdom": "^22.1.0",
|
|
32
32
|
"luxon": "^2.4.0",
|
|
33
33
|
"madge": "^6.0.0"
|
|
@@ -36,5 +36,5 @@
|
|
|
36
36
|
"@types/jsdom": "^21.1.1",
|
|
37
37
|
"@types/luxon": "^2.4.0"
|
|
38
38
|
},
|
|
39
|
-
"version": "0.0.
|
|
39
|
+
"version": "0.0.150"
|
|
40
40
|
}
|
|
@@ -97,19 +97,20 @@ runtimes.runtimeMap.forEach((runtime, databaseName) => {
|
|
|
97
97
|
});
|
|
98
98
|
|
|
99
99
|
// bigquery doesn't support row count based sampling.
|
|
100
|
-
test.when(
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
100
|
+
test.when(
|
|
101
|
+
databaseName !== 'bigquery' &&
|
|
102
|
+
databaseName !== 'trino' &&
|
|
103
|
+
databaseName !== 'presto'
|
|
104
|
+
)(`index rows count - ${databaseName}`, async () => {
|
|
105
|
+
await expect(`
|
|
104
106
|
run: ${databaseName}.table('malloytest.state_facts') extend {
|
|
105
107
|
dimension: one is 'one'
|
|
106
108
|
} -> {index:one, state; sample: 10 }
|
|
107
109
|
-> {select: fieldName, weight, fieldValue; order_by: 2 desc; where: fieldName = 'one'}
|
|
108
110
|
`).malloyResultMatches(runtime, {fieldName: 'one', weight: 10});
|
|
109
|
-
|
|
110
|
-
);
|
|
111
|
+
});
|
|
111
112
|
|
|
112
|
-
it.when(databaseName !== 'trino')(
|
|
113
|
+
it.when(databaseName !== 'trino' && databaseName !== 'presto')(
|
|
113
114
|
`index rows count - ${databaseName}`,
|
|
114
115
|
async () => {
|
|
115
116
|
await expect(`
|
|
@@ -112,32 +112,32 @@ expressionModels.forEach((expressionModel, databaseName) => {
|
|
|
112
112
|
};
|
|
113
113
|
|
|
114
114
|
describe('concat', () => {
|
|
115
|
-
it.when(
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
],
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
)
|
|
139
|
-
|
|
140
|
-
);
|
|
115
|
+
it.when(
|
|
116
|
+
!brokenIn('trino', databaseName) &&
|
|
117
|
+
!brokenIn('presto', databaseName) /* crswenson */
|
|
118
|
+
)(`works - ${databaseName}`, async () => {
|
|
119
|
+
const expected = {
|
|
120
|
+
'bigquery': 'foo2003-01-01 12:00:00+00',
|
|
121
|
+
'snowflake': 'foo2003-01-01T12:00:00.000Z',
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
await funcTestMultiple(
|
|
125
|
+
["concat('foo', 'bar')", 'foobar'],
|
|
126
|
+
["concat(1, 'bar')", '1bar'],
|
|
127
|
+
[
|
|
128
|
+
"concat('cons', true)",
|
|
129
|
+
databaseName === 'postgres' ? 'const' : 'construe',
|
|
130
|
+
],
|
|
131
|
+
["concat('foo', @2003)", 'foo2003-01-01'],
|
|
132
|
+
[
|
|
133
|
+
"concat('foo', @2003-01-01 12:00:00)",
|
|
134
|
+
expected[databaseName] ?? 'foo2003-01-01 12:00:00',
|
|
135
|
+
],
|
|
136
|
+
// TODO Maybe implement consistent null behavior
|
|
137
|
+
// ["concat('foo', null)", null],
|
|
138
|
+
['concat()', '']
|
|
139
|
+
);
|
|
140
|
+
});
|
|
141
141
|
});
|
|
142
142
|
|
|
143
143
|
describe('round', () => {
|
|
@@ -219,13 +219,15 @@ expressionModels.forEach((expressionModel, databaseName) => {
|
|
|
219
219
|
"replace('axbxc', r'(a).(b).(c)', '\\\\0 - \\\\1 - \\\\2 - \\\\3')",
|
|
220
220
|
databaseName === 'postgres'
|
|
221
221
|
? '\\0 - a - b - c'
|
|
222
|
-
: databaseName === 'trino'
|
|
222
|
+
: databaseName === 'trino' || databaseName === 'presto'
|
|
223
223
|
? '0 - 1 - 2 - 3'
|
|
224
224
|
: 'axbxc - a - b - c',
|
|
225
225
|
],
|
|
226
226
|
[
|
|
227
227
|
"replace('aaaa', '', 'c')",
|
|
228
|
-
databaseName === 'trino'
|
|
228
|
+
databaseName === 'trino' || databaseName === 'presto'
|
|
229
|
+
? 'cacacacac'
|
|
230
|
+
: 'aaaa',
|
|
229
231
|
],
|
|
230
232
|
["replace(null, 'a', 'c')", null],
|
|
231
233
|
["replace('aaaa', null, 'c')", null],
|
|
@@ -262,7 +264,8 @@ expressionModels.forEach((expressionModel, databaseName) => {
|
|
|
262
264
|
|
|
263
265
|
describe('stddev', () => {
|
|
264
266
|
// TODO symmetric aggregates don't work with custom aggregate functions in BQ currently
|
|
265
|
-
if (['bigquery', 'snowflake', 'trino'].includes(databaseName))
|
|
267
|
+
if (['bigquery', 'snowflake', 'trino', 'presto'].includes(databaseName))
|
|
268
|
+
return;
|
|
266
269
|
it(`works - ${databaseName}`, async () => {
|
|
267
270
|
await funcTestAgg('round(stddev(aircraft_models.seats))', 29);
|
|
268
271
|
});
|
|
@@ -786,7 +789,7 @@ expressionModels.forEach((expressionModel, databaseName) => {
|
|
|
786
789
|
});
|
|
787
790
|
});
|
|
788
791
|
describe('is_inf', () => {
|
|
789
|
-
const inf = ['trino'].includes(databaseName)
|
|
792
|
+
const inf = ['trino', 'presto'].includes(databaseName)
|
|
790
793
|
? 'infinity!()'
|
|
791
794
|
: "'+inf'::number";
|
|
792
795
|
it(`works - ${databaseName}`, async () => {
|
|
@@ -1327,7 +1330,8 @@ describe.each(runtimes.runtimeList)('%s', (databaseName, runtime) => {
|
|
|
1327
1330
|
|
|
1328
1331
|
it(`works with fanout and order_by - ${databaseName}`, async () => {
|
|
1329
1332
|
// TODO bigquery cannot handle both fanout and order_by today
|
|
1330
|
-
if (['bigquery', 'snowflake', 'trino'].includes(databaseName))
|
|
1333
|
+
if (['bigquery', 'snowflake', 'trino', 'presto'].includes(databaseName))
|
|
1334
|
+
return;
|
|
1331
1335
|
await expect(`##! experimental.aggregate_order_by
|
|
1332
1336
|
run: state_facts extend { join_many:
|
|
1333
1337
|
state_facts2 is ${databaseName}.table('malloytest.state_facts')
|
|
@@ -194,10 +194,10 @@ describe('join expression tests', () => {
|
|
|
194
194
|
`).malloyResultMatches(joinModel, {f_sum2: 60462});
|
|
195
195
|
});
|
|
196
196
|
|
|
197
|
-
test.when(
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
197
|
+
test.when(
|
|
198
|
+
runtime.supportsNesting && runtime.dialect.supportsLeftJoinUnnest
|
|
199
|
+
)(`model: unnest is left join - ${database}`, async () => {
|
|
200
|
+
await expect(`
|
|
201
201
|
// produce a table with 4 rows that has a nested element
|
|
202
202
|
query: a_states is ${database}.table('malloytest.state_facts')-> {
|
|
203
203
|
where: state ? ~ 'A%'
|
|
@@ -220,8 +220,7 @@ describe('join expression tests', () => {
|
|
|
220
220
|
limit: 5
|
|
221
221
|
}
|
|
222
222
|
`).malloyResultMatches(joinModel, [{}, {}, {}, {}, {}]);
|
|
223
|
-
|
|
224
|
-
);
|
|
223
|
+
});
|
|
225
224
|
|
|
226
225
|
// not sure how to solve this one yet, just check for > 4 rows
|
|
227
226
|
it(`All joins at the same level - ${database}`, async () => {
|
|
@@ -482,11 +482,13 @@ runtimes.runtimeMap.forEach((runtime, databaseName) => {
|
|
|
482
482
|
expect(q.sql.toLowerCase()).not.toContain('distinct');
|
|
483
483
|
});
|
|
484
484
|
|
|
485
|
-
it(
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
485
|
+
it.when(runtime.dialect.supportsLeftJoinUnnest)(
|
|
486
|
+
`leafy nested count - ${databaseName}`,
|
|
487
|
+
async () => {
|
|
488
|
+
// in a joined table when the joined is leafiest
|
|
489
|
+
// we need to make sure we don't count rows that
|
|
490
|
+
// don't match the join.
|
|
491
|
+
await expect(`
|
|
490
492
|
source: am_states is ${databaseName}.table('malloytest.state_facts') -> {
|
|
491
493
|
group_by: state,popular_name
|
|
492
494
|
where: state ~ r'^(A|M)'
|
|
@@ -498,7 +500,6 @@ runtimes.runtimeMap.forEach((runtime, databaseName) => {
|
|
|
498
500
|
source: states is ${databaseName}.table('malloytest.state_facts') extend {
|
|
499
501
|
join_many: am_states on state=am_states.state
|
|
500
502
|
}
|
|
501
|
-
|
|
502
503
|
run: states -> {
|
|
503
504
|
where: state = 'CA'
|
|
504
505
|
group_by:
|
|
@@ -509,12 +510,13 @@ runtimes.runtimeMap.forEach((runtime, databaseName) => {
|
|
|
509
510
|
root_count is count()
|
|
510
511
|
}
|
|
511
512
|
`).malloyResultMatches(runtime, {
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
513
|
+
leafy_count: 0,
|
|
514
|
+
root_count: 1,
|
|
515
|
+
state: 'CA',
|
|
516
|
+
am_state: null,
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
);
|
|
518
520
|
|
|
519
521
|
it(`basic index - ${databaseName}`, async () => {
|
|
520
522
|
// Make sure basic indexing works.
|
|
@@ -560,7 +562,6 @@ runtimes.runtimeMap.forEach((runtime, databaseName) => {
|
|
|
560
562
|
}`).malloyResultMatches(runtime, {a: 2});
|
|
561
563
|
});
|
|
562
564
|
|
|
563
|
-
|
|
564
565
|
// average should only include non-null values in the denominator
|
|
565
566
|
it(`avg ignore null- ${databaseName}`, async () => {
|
|
566
567
|
await expect(`
|
|
@@ -1055,11 +1056,23 @@ SELECT row_to_json(finalStage) as row FROM __stage0 AS finalStage`);
|
|
|
1055
1056
|
);
|
|
1056
1057
|
|
|
1057
1058
|
test.when(runtime.supportsNesting && runtime.dialect.readsNestedData)(
|
|
1058
|
-
`can unnest from file - ${databaseName}`,
|
|
1059
|
+
`can unnest simply from file - ${databaseName}`,
|
|
1059
1060
|
async () => {
|
|
1060
1061
|
await expect(`
|
|
1061
1062
|
source: ga_sample is ${databaseName}.table('malloytest.ga_sample')
|
|
1063
|
+
run: ga_sample -> {
|
|
1064
|
+
aggregate:
|
|
1065
|
+
h is hits.count()
|
|
1066
|
+
}
|
|
1067
|
+
`).malloyResultMatches(runtime, {h: 13233});
|
|
1068
|
+
}
|
|
1069
|
+
);
|
|
1062
1070
|
|
|
1071
|
+
test.when(runtime.supportsNesting && runtime.dialect.readsNestedData)(
|
|
1072
|
+
`can unnest from file - ${databaseName}`,
|
|
1073
|
+
async () => {
|
|
1074
|
+
await expect(`
|
|
1075
|
+
source: ga_sample is ${databaseName}.table('malloytest.ga_sample')
|
|
1063
1076
|
run: ga_sample -> {
|
|
1064
1077
|
where: hits.product.productBrand != null
|
|
1065
1078
|
group_by:
|
|
@@ -462,18 +462,17 @@ describe.each(runtimes.runtimeList)('%s date and time', (dbName, runtime) => {
|
|
|
462
462
|
});
|
|
463
463
|
});
|
|
464
464
|
|
|
465
|
-
test.when(
|
|
466
|
-
'
|
|
467
|
-
|
|
468
|
-
|
|
465
|
+
test.when(
|
|
466
|
+
!brokenIn('trino', dbName) && !brokenIn('presto', dbName) /* mtoy */
|
|
467
|
+
)('dependant join dialect fragments', async () => {
|
|
468
|
+
await expect(`
|
|
469
469
|
source: timeData is ${dbName}.sql("""${timeSQL}""")
|
|
470
470
|
run: timeData -> {
|
|
471
471
|
extend: {join_one: joined is timeData on t_date = joined.t_date}
|
|
472
472
|
group_by: t_month is joined.t_timestamp.month
|
|
473
473
|
}
|
|
474
474
|
`).malloyResultMatches(runtime, {t_month: new Date('2021-02-01')});
|
|
475
|
-
|
|
476
|
-
);
|
|
475
|
+
});
|
|
477
476
|
|
|
478
477
|
describe('timezone set correctly', () => {
|
|
479
478
|
test('timezone set in source used by query', async () => {
|
|
@@ -621,63 +620,60 @@ const utc_2020 = LuxonDateTime.fromObject(
|
|
|
621
620
|
);
|
|
622
621
|
|
|
623
622
|
describe.each(runtimes.runtimeList)('%s: tz literals', (dbName, runtime) => {
|
|
624
|
-
test.when(
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
623
|
+
test.when(
|
|
624
|
+
!brokenIn('trino', dbName) && !brokenIn('presto', dbName) /* mtoy */
|
|
625
|
+
)(`${dbName} - default timezone is UTC`, async () => {
|
|
626
|
+
// this makes sure that the tests which use the test timezome are actually
|
|
627
|
+
// testing something ... file this under "abundance of caution". It
|
|
628
|
+
// really tests nothing, but I feel calmer with this here.
|
|
629
|
+
const query = runtime.loadQuery(
|
|
630
|
+
`
|
|
632
631
|
run: ${dbName}.sql("SELECT 1 as one") -> {
|
|
633
632
|
group_by: literal_time is @2020-02-20 00:00:00
|
|
634
633
|
}
|
|
635
634
|
`
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
`
|
|
635
|
+
);
|
|
636
|
+
const result = await query.run();
|
|
637
|
+
const literal = result.data.path(0, 'literal_time').value as Date;
|
|
638
|
+
const have = LuxonDateTime.fromJSDate(literal);
|
|
639
|
+
expect(have.valueOf()).toEqual(utc_2020.valueOf());
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
test.when(
|
|
643
|
+
!brokenIn('trino', dbName) && !brokenIn('presto', dbName) /* mtoy */
|
|
644
|
+
)('literal with zone name', async () => {
|
|
645
|
+
const query = runtime.loadQuery(
|
|
646
|
+
`
|
|
649
647
|
run: ${dbName}.sql("SELECT 1 as one") -> {
|
|
650
648
|
group_by: literal_time is @2020-02-20 00:00:00[America/Mexico_City]
|
|
651
649
|
}
|
|
652
650
|
`
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
);
|
|
651
|
+
);
|
|
652
|
+
const result = await query.run();
|
|
653
|
+
const literal = result.data.path(0, 'literal_time').value as Date;
|
|
654
|
+
const have = LuxonDateTime.fromJSDate(literal);
|
|
655
|
+
expect(have.valueOf()).toEqual(zone_2020.valueOf());
|
|
656
|
+
});
|
|
660
657
|
});
|
|
661
658
|
|
|
662
659
|
describe.each(runtimes.runtimeList)('%s: query tz', (dbName, runtime) => {
|
|
663
660
|
const q = runtime.getQuoter();
|
|
664
|
-
test.when(
|
|
665
|
-
'
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
661
|
+
test.when(
|
|
662
|
+
!brokenIn('trino', dbName) && !brokenIn('presto', dbName) /* mtoy */
|
|
663
|
+
)('literal timestamps', async () => {
|
|
664
|
+
const query = runtime.loadQuery(
|
|
665
|
+
`
|
|
669
666
|
run: ${dbName}.sql("SELECT 1 as one") -> {
|
|
670
667
|
timezone: '${zone}'
|
|
671
668
|
group_by: literal_time is @2020-02-20 00:00:00
|
|
672
669
|
}
|
|
673
670
|
`
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
);
|
|
671
|
+
);
|
|
672
|
+
const result = await query.run();
|
|
673
|
+
const literal = result.data.path(0, 'literal_time').value as Date;
|
|
674
|
+
const have = LuxonDateTime.fromJSDate(literal);
|
|
675
|
+
expect(have.valueOf()).toEqual(zone_2020.valueOf());
|
|
676
|
+
});
|
|
681
677
|
|
|
682
678
|
test('extract', async () => {
|
|
683
679
|
await expect(
|
|
@@ -691,7 +687,9 @@ describe.each(runtimes.runtimeList)('%s: query tz', (dbName, runtime) => {
|
|
|
691
687
|
).malloyResultMatches(runtime, {mex_midnight: 18, mex_day: 19});
|
|
692
688
|
});
|
|
693
689
|
|
|
694
|
-
test.when(
|
|
690
|
+
test.when(
|
|
691
|
+
!brokenIn('trino', dbName) && !brokenIn('presto', dbName) /* mtoy */
|
|
692
|
+
)('truncate day', async () => {
|
|
695
693
|
// At midnight in london it the 19th in Mexico, so that truncates to
|
|
696
694
|
// midnight on the 19th
|
|
697
695
|
const mex_19 = LuxonDateTime.fromISO('2020-02-19T00:00:00', {zone});
|
|
@@ -704,32 +702,30 @@ describe.each(runtimes.runtimeList)('%s: query tz', (dbName, runtime) => {
|
|
|
704
702
|
).malloyResultMatches(runtime, {mex_day: mex_19.toJSDate()});
|
|
705
703
|
});
|
|
706
704
|
|
|
707
|
-
test.when(
|
|
708
|
-
'
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
705
|
+
test.when(
|
|
706
|
+
!brokenIn('trino', dbName) && !brokenIn('presto', dbName) /* mtoy */
|
|
707
|
+
)('cast timestamp to date', async () => {
|
|
708
|
+
// At midnight in london it is the 19th in Mexico, so when we cast that
|
|
709
|
+
// to a date, it should be the 19th.
|
|
710
|
+
await expect(
|
|
711
|
+
`run: ${dbName}.sql("SELECT 1 as x") -> {
|
|
714
712
|
timezone: '${zone}'
|
|
715
713
|
extend: { dimension: utc_midnight is @2020-02-20 00:00:00[UTC] }
|
|
716
714
|
select: mex_day is day(utc_midnight::date)
|
|
717
715
|
}`
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
`run: ${dbName}.sql(""" SELECT DATE '2020-02-20' AS ${q`mex_20`} """) -> {
|
|
716
|
+
).malloyResultMatches(runtime, {mex_day: 19});
|
|
717
|
+
});
|
|
718
|
+
|
|
719
|
+
test.when(
|
|
720
|
+
!brokenIn('trino', dbName) && !brokenIn('presto', dbName) /* mtoy */
|
|
721
|
+
)('cast date to timestamp', async () => {
|
|
722
|
+
await expect(
|
|
723
|
+
`run: ${dbName}.sql(""" SELECT DATE '2020-02-20' AS ${q`mex_20`} """) -> {
|
|
727
724
|
timezone: '${zone}'
|
|
728
725
|
select: mex_ts is mex_20::timestamp
|
|
729
726
|
}`
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
);
|
|
727
|
+
).malloyResultMatches(runtime, {mex_ts: zone_2020.toJSDate()});
|
|
728
|
+
});
|
|
733
729
|
});
|
|
734
730
|
|
|
735
731
|
afterAll(async () => {
|