@malloydata/malloy-tests 0.0.295 → 0.0.297
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/README.md +15 -13
- package/package.json +9 -9
- package/src/databases/presto-trino/presto-trino.spec.ts +282 -0
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ Assumes that postgres has been installed via nix (installs but doesn't configure
|
|
|
13
13
|
ADD to environment: `export PGHOST=localhost`
|
|
14
14
|
|
|
15
15
|
**postgres_init.sh** - builds a database as the current user in .tmp/data/malloytestdb. Starts server running on localhost:5432
|
|
16
|
-
copies the test data in `malloytest-postgres.sql.gz` into the database.
|
|
16
|
+
copies the test data in `malloytest-postgres.sql.gz` into the database. - You may also need to add state_facts.sql for some tests
|
|
17
17
|
|
|
18
18
|
**postgres_start.sh** - starts the postgres server, once it has been installed (use after a reboot, for example)
|
|
19
19
|
|
|
@@ -30,7 +30,7 @@ Setting up DuckDB:
|
|
|
30
30
|
|
|
31
31
|
# Using the custom matcher for running queries
|
|
32
32
|
|
|
33
|
-
There is now a custom matcher, `malloyResultMatches` for running queries.
|
|
33
|
+
There is now a custom matcher, `malloyResultMatches` for running queries. The customer matcher makes it easy to write readable tests which need to look at query results, and produces useful output when the test fails to make it easier to develop tests or respond to the output of failing tests.
|
|
34
34
|
|
|
35
35
|
## Check for results in the first row of output
|
|
36
36
|
|
|
@@ -54,9 +54,9 @@ import './util/db-jest-matchers';
|
|
|
54
54
|
|
|
55
55
|
This will check the following things.
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
- There is at least one row of data in the output. So `.malloyQueryMatches(rt, {})` will fail if the query returns no rows
|
|
58
|
+
- There are entries in that row for each key. `{}` matches any row
|
|
59
|
+
- The entries are equal, but it will error if the expected data as a number and the returned data is a string.
|
|
60
60
|
|
|
61
61
|
## Accessing nested results
|
|
62
62
|
|
|
@@ -79,11 +79,11 @@ example also shows passing a model instead of a runtime to the matcher.
|
|
|
79
79
|
});
|
|
80
80
|
```
|
|
81
81
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
82
|
+
> [!WARNING]
|
|
83
|
+
> There is currently a ... feature ... where if the source code of a test
|
|
84
|
+
> contains the characters `nest:` and the runtime connection does not support nesting,
|
|
85
|
+
> the test passes without actually doing anything. There will better handling of
|
|
86
|
+
> this problem in the future, but if your test is mysteriously passing, this is why.
|
|
87
87
|
|
|
88
88
|
## Queries returning more than one row of data
|
|
89
89
|
|
|
@@ -102,8 +102,8 @@ An array of match rows may can also be used, if the test needs to verify more th
|
|
|
102
102
|
|
|
103
103
|
This will pass if ..
|
|
104
104
|
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
- There are exactly two rows of output. If there are not exactly two the matcher will fail. If the query makes more than two rows, you will need to add a `limit: 2` to allow the matcher to pass.
|
|
106
|
+
- Each row is matches the matching criteria, again `{}` means "pass if there is a row"
|
|
107
107
|
|
|
108
108
|
## Reading failure output
|
|
109
109
|
|
|
@@ -235,7 +235,9 @@ The old template for a test looked something like
|
|
|
235
235
|
```
|
|
236
236
|
|
|
237
237
|
The actual matcher for a result is limited to an equality test, in the old pattern you would have written something using an existing matcher for a data value
|
|
238
|
+
|
|
238
239
|
```TypeScript
|
|
239
240
|
expect(result.data.patch(0, 'numThings').value).toBeGreaterThan(7);
|
|
240
241
|
```
|
|
241
|
-
|
|
242
|
+
|
|
243
|
+
and if this is desirable, more work on the custom matcher would be needed to allow expressions like this to be written.
|
package/package.json
CHANGED
|
@@ -21,14 +21,14 @@
|
|
|
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/malloy-tag": "0.0.
|
|
31
|
-
"@malloydata/render": "0.0.
|
|
24
|
+
"@malloydata/db-bigquery": "0.0.297",
|
|
25
|
+
"@malloydata/db-duckdb": "0.0.297",
|
|
26
|
+
"@malloydata/db-postgres": "0.0.297",
|
|
27
|
+
"@malloydata/db-snowflake": "0.0.297",
|
|
28
|
+
"@malloydata/db-trino": "0.0.297",
|
|
29
|
+
"@malloydata/malloy": "0.0.297",
|
|
30
|
+
"@malloydata/malloy-tag": "0.0.297",
|
|
31
|
+
"@malloydata/render": "0.0.297",
|
|
32
32
|
"events": "^3.3.0",
|
|
33
33
|
"jsdom": "^22.1.0",
|
|
34
34
|
"luxon": "^2.4.0",
|
|
@@ -38,5 +38,5 @@
|
|
|
38
38
|
"@types/jsdom": "^21.1.1",
|
|
39
39
|
"@types/luxon": "^2.4.0"
|
|
40
40
|
},
|
|
41
|
-
"version": "0.0.
|
|
41
|
+
"version": "0.0.297"
|
|
42
42
|
}
|
|
@@ -27,6 +27,58 @@ describe.each(runtimes.runtimeList)(
|
|
|
27
27
|
`run: ${databaseName}.sql("SELECT 1 as n") -> { select: n }`
|
|
28
28
|
).malloyResultMatches(runtime, {n: 1});
|
|
29
29
|
});
|
|
30
|
+
|
|
31
|
+
describe('HLL Window Functions', () => {
|
|
32
|
+
it.when(presto)(
|
|
33
|
+
`hll_accumulate_moving function - ${databaseName}`,
|
|
34
|
+
async () => {
|
|
35
|
+
await expect(`run: ${databaseName}.sql("""
|
|
36
|
+
SELECT 'A' as category, 'value1' as val, 1 as seq
|
|
37
|
+
UNION ALL SELECT 'A' as category, 'value2' as val, 2 as seq
|
|
38
|
+
UNION ALL SELECT 'B' as category, 'value1' as val, 1 as seq
|
|
39
|
+
UNION ALL SELECT 'B' as category, 'value3' as val, 2 as seq
|
|
40
|
+
""") -> {
|
|
41
|
+
select: *
|
|
42
|
+
order_by: category, seq
|
|
43
|
+
calculate: hll_acc is hll_accumulate_moving(val, 1)
|
|
44
|
+
} -> {
|
|
45
|
+
select:
|
|
46
|
+
*
|
|
47
|
+
hll_moving is hll_estimate(hll_acc)
|
|
48
|
+
}`).malloyResultMatches(runtime, [
|
|
49
|
+
{category: 'A', val: 'value1', seq: 1, hll_moving: 1},
|
|
50
|
+
{category: 'A', val: 'value2', seq: 2, hll_moving: 2},
|
|
51
|
+
{category: 'B', val: 'value1', seq: 1, hll_moving: 2},
|
|
52
|
+
{category: 'B', val: 'value3', seq: 2, hll_moving: 2},
|
|
53
|
+
]);
|
|
54
|
+
}
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
it.when(presto)(
|
|
58
|
+
`hll_combine_moving function - ${databaseName}`,
|
|
59
|
+
async () => {
|
|
60
|
+
await expect(`run: ${databaseName}.sql("""
|
|
61
|
+
SELECT 'A' as category, 'value1' as val, 1 as seq
|
|
62
|
+
UNION ALL SELECT 'A' as category, 'value2' as val, 2 as seq
|
|
63
|
+
UNION ALL SELECT 'B' as category, 'value1' as val, 1 as seq
|
|
64
|
+
""") -> {
|
|
65
|
+
group_by: category
|
|
66
|
+
aggregate: hll_set is hll_accumulate(val)
|
|
67
|
+
} -> {
|
|
68
|
+
select: *
|
|
69
|
+
order_by: category
|
|
70
|
+
calculate: combined_hll is hll_combine_moving(hll_set, 1)
|
|
71
|
+
} -> {
|
|
72
|
+
select:
|
|
73
|
+
*
|
|
74
|
+
final_count is hll_estimate(combined_hll)
|
|
75
|
+
}`).malloyResultMatches(runtime, [
|
|
76
|
+
{category: 'A', final_count: 2},
|
|
77
|
+
{category: 'B', final_count: 2},
|
|
78
|
+
]);
|
|
79
|
+
}
|
|
80
|
+
);
|
|
81
|
+
});
|
|
30
82
|
test.when(databaseName === 'presto')(
|
|
31
83
|
'schema parser does not throw on compound types',
|
|
32
84
|
async () => {
|
|
@@ -598,6 +650,236 @@ describe.each(runtimes.runtimeList)(
|
|
|
598
650
|
""") -> { aggregate: n is count() }
|
|
599
651
|
`).matchesRows(runtime, {n: 1});
|
|
600
652
|
});
|
|
653
|
+
|
|
654
|
+
describe('T-Digest functions', () => {
|
|
655
|
+
const testData = `${databaseName}.sql("""
|
|
656
|
+
SELECT CAST(1.0 AS DOUBLE) as n, CAST(1 AS BIGINT) as w
|
|
657
|
+
UNION ALL SELECT CAST(2.0 AS DOUBLE) as n, CAST(2 AS BIGINT) as w
|
|
658
|
+
UNION ALL SELECT CAST(3.0 AS DOUBLE) as n, CAST(1 AS BIGINT) as w
|
|
659
|
+
UNION ALL SELECT CAST(4.0 AS DOUBLE) as n, CAST(3 AS BIGINT) as w
|
|
660
|
+
UNION ALL SELECT CAST(5.0 AS DOUBLE) as n, CAST(1 AS BIGINT) as w
|
|
661
|
+
UNION ALL SELECT CAST(6.0 AS DOUBLE) as n, CAST(2 AS BIGINT) as w
|
|
662
|
+
UNION ALL SELECT CAST(7.0 AS DOUBLE) as n, CAST(1 AS BIGINT) as w
|
|
663
|
+
UNION ALL SELECT CAST(8.0 AS DOUBLE) as n, CAST(2 AS BIGINT) as w
|
|
664
|
+
UNION ALL SELECT CAST(9.0 AS DOUBLE) as n, CAST(1 AS BIGINT) as w
|
|
665
|
+
UNION ALL SELECT CAST(10.0 AS DOUBLE) as n, CAST(3 AS BIGINT) as w
|
|
666
|
+
""")`;
|
|
667
|
+
|
|
668
|
+
it.when(presto)(
|
|
669
|
+
`runs the basic tdigest_agg function - ${databaseName}`,
|
|
670
|
+
async () => {
|
|
671
|
+
await expect(`run: ${testData} -> {
|
|
672
|
+
aggregate:
|
|
673
|
+
median is value_at_quantile(tdigest_agg(n), 0.5)
|
|
674
|
+
}`).malloyResultMatches(runtime, {
|
|
675
|
+
median: 6,
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
);
|
|
679
|
+
|
|
680
|
+
it.when(presto)(
|
|
681
|
+
`runs the tdigest_agg with weight function - ${databaseName}`,
|
|
682
|
+
async () => {
|
|
683
|
+
await expect(`run: ${testData} -> {
|
|
684
|
+
aggregate:
|
|
685
|
+
weighted_median is value_at_quantile(tdigest_agg(n, w), 0.5)
|
|
686
|
+
}`).malloyResultMatches(runtime, {
|
|
687
|
+
weighted_median: 5.5,
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
);
|
|
691
|
+
|
|
692
|
+
it.when(presto)(
|
|
693
|
+
`runs the tdigest_agg with weight and compression function - ${databaseName}`,
|
|
694
|
+
async () => {
|
|
695
|
+
await expect(`run: ${testData} -> {
|
|
696
|
+
aggregate:
|
|
697
|
+
compressed_median is value_at_quantile(tdigest_agg(n, w, 100), 0.5)
|
|
698
|
+
}`).malloyResultMatches(runtime, {
|
|
699
|
+
compressed_median: 5.5,
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
);
|
|
703
|
+
|
|
704
|
+
it.when(presto)(
|
|
705
|
+
`runs the value_at_quantile function - ${databaseName}`,
|
|
706
|
+
async () => {
|
|
707
|
+
await expect(`run: ${testData} -> {
|
|
708
|
+
aggregate:
|
|
709
|
+
q25 is value_at_quantile(tdigest_agg(n), 0.25)
|
|
710
|
+
q50 is value_at_quantile(tdigest_agg(n), 0.5)
|
|
711
|
+
q75 is value_at_quantile(tdigest_agg(n), 0.75)
|
|
712
|
+
q90 is value_at_quantile(tdigest_agg(n), 0.9)
|
|
713
|
+
}`).malloyResultMatches(runtime, {
|
|
714
|
+
q25: 3,
|
|
715
|
+
q50: 6,
|
|
716
|
+
q75: 8,
|
|
717
|
+
q90: 10,
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
);
|
|
721
|
+
|
|
722
|
+
it.when(presto)(
|
|
723
|
+
`runs the quantile_at_value function - ${databaseName}`,
|
|
724
|
+
async () => {
|
|
725
|
+
await expect(`run: ${testData} -> {
|
|
726
|
+
aggregate:
|
|
727
|
+
q_at_5 is quantile_at_value(tdigest_agg(n), 5.0)
|
|
728
|
+
q_at_2 is quantile_at_value(tdigest_agg(n), 2.0)
|
|
729
|
+
q_at_8 is quantile_at_value(tdigest_agg(n), 8.0)
|
|
730
|
+
}`).malloyResultMatches(runtime, {
|
|
731
|
+
q_at_5: 0.45,
|
|
732
|
+
q_at_2: 0.15,
|
|
733
|
+
q_at_8: 0.75,
|
|
734
|
+
});
|
|
735
|
+
}
|
|
736
|
+
);
|
|
737
|
+
|
|
738
|
+
it.when(presto)(
|
|
739
|
+
`runs the values_at_quantiles function - ${databaseName}`,
|
|
740
|
+
async () => {
|
|
741
|
+
await expect(`run: ${testData} -> {
|
|
742
|
+
aggregate:
|
|
743
|
+
quantiles is values_at_quantiles(tdigest_agg(n), [0.25, 0.5, 0.75])
|
|
744
|
+
}`).malloyResultMatches(runtime, {
|
|
745
|
+
quantiles: [3, 6, 8],
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
);
|
|
749
|
+
|
|
750
|
+
it.when(presto)(
|
|
751
|
+
`runs the scale_tdigest function - ${databaseName}`,
|
|
752
|
+
async () => {
|
|
753
|
+
await expect(`run: ${testData} -> {
|
|
754
|
+
aggregate:
|
|
755
|
+
original_median is value_at_quantile(tdigest_agg(n), 0.5)
|
|
756
|
+
scaled_median is value_at_quantile(scale_tdigest(tdigest_agg(n), 2.0), 0.5)
|
|
757
|
+
}`).malloyResultMatches(runtime, {
|
|
758
|
+
original_median: 6,
|
|
759
|
+
scaled_median: 5.5,
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
);
|
|
763
|
+
|
|
764
|
+
it.when(presto)(
|
|
765
|
+
`runs the trimmed_mean function - ${databaseName}`,
|
|
766
|
+
async () => {
|
|
767
|
+
await expect(`run: ${testData} -> {
|
|
768
|
+
aggregate:
|
|
769
|
+
trimmed_mean_10_90 is trimmed_mean(tdigest_agg(n), 0.1, 0.9)
|
|
770
|
+
trimmed_mean_25_75 is trimmed_mean(tdigest_agg(n), 0.25, 0.75)
|
|
771
|
+
}`).malloyResultMatches(runtime, {
|
|
772
|
+
trimmed_mean_10_90: 6,
|
|
773
|
+
trimmed_mean_25_75: 5.5,
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
);
|
|
777
|
+
|
|
778
|
+
it.when(presto)(
|
|
779
|
+
`runs the merge tdigest function - ${databaseName}`,
|
|
780
|
+
async () => {
|
|
781
|
+
await expect(`run: ${testData} -> {
|
|
782
|
+
group_by: group_col is case when n <= 5 then 'A' else 'B' end
|
|
783
|
+
aggregate: td is tdigest_agg(n)
|
|
784
|
+
} -> {
|
|
785
|
+
aggregate:
|
|
786
|
+
overall_median is value_at_quantile(merge_tdigest(td), 0.5)
|
|
787
|
+
}`).malloyResultMatches(runtime, {
|
|
788
|
+
overall_median: 6,
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
);
|
|
792
|
+
|
|
793
|
+
it.when(presto)(
|
|
794
|
+
`runs the merge_tdigest array function - ${databaseName}`,
|
|
795
|
+
async () => {
|
|
796
|
+
await expect(`run: ${testData} -> {
|
|
797
|
+
group_by: group_col is case when n <= 5 then 'A' else 'B' end
|
|
798
|
+
aggregate: td is tdigest_agg(n)
|
|
799
|
+
} -> {
|
|
800
|
+
aggregate:
|
|
801
|
+
overall_median is value_at_quantile(merge_tdigest_array(array_agg(td)), 0.5)
|
|
802
|
+
}`).malloyResultMatches(runtime, {
|
|
803
|
+
overall_median: 6,
|
|
804
|
+
});
|
|
805
|
+
}
|
|
806
|
+
);
|
|
807
|
+
|
|
808
|
+
it.skip(`runs the destructure_tdigest function - ${databaseName}`, async () => {
|
|
809
|
+
// TODO: Fix this test - destructure_tdigest returns a record type
|
|
810
|
+
// which needs special handling in the test
|
|
811
|
+
await expect(`run: ${testData} -> {
|
|
812
|
+
aggregate:
|
|
813
|
+
destructured is destructure_tdigest(tdigest_agg(n))
|
|
814
|
+
}`).malloyResultMatches(runtime, {
|
|
815
|
+
destructured: {
|
|
816
|
+
centroid_means: [],
|
|
817
|
+
centroid_weights: [],
|
|
818
|
+
min_value: 1.0,
|
|
819
|
+
max_value: 10.0,
|
|
820
|
+
sum_value: 55.0,
|
|
821
|
+
count_value: 10,
|
|
822
|
+
},
|
|
823
|
+
});
|
|
824
|
+
});
|
|
825
|
+
|
|
826
|
+
it.skip(`runs the construct_tdigest function - ${databaseName}`, async () => {
|
|
827
|
+
// This test is complex because construct_tdigest needs the output of destructure_tdigest
|
|
828
|
+
// For now, we'll test that the function exists and can be called with sample data
|
|
829
|
+
await expect(`run: ${databaseName}.sql("SELECT 1 as dummy") -> {
|
|
830
|
+
select:
|
|
831
|
+
test_construct is value_at_quantile(
|
|
832
|
+
construct_tdigest(
|
|
833
|
+
[1.0, 2.0, 3.0],
|
|
834
|
+
[1.0, 1.0, 1.0],
|
|
835
|
+
1.0,
|
|
836
|
+
3.0,
|
|
837
|
+
6.0,
|
|
838
|
+
3.0,
|
|
839
|
+
100
|
|
840
|
+
),
|
|
841
|
+
0.5
|
|
842
|
+
)
|
|
843
|
+
}`).malloyResultMatches(runtime, {
|
|
844
|
+
test_construct: 2,
|
|
845
|
+
});
|
|
846
|
+
});
|
|
847
|
+
|
|
848
|
+
it.when(presto)(
|
|
849
|
+
`runs tdigest functions with edge cases - ${databaseName}`,
|
|
850
|
+
async () => {
|
|
851
|
+
const singleValueData = `${databaseName}.sql("SELECT CAST(5.0 AS DOUBLE) as n")`;
|
|
852
|
+
await expect(`run: ${singleValueData} -> {
|
|
853
|
+
aggregate:
|
|
854
|
+
median is value_at_quantile(tdigest_agg(n), 0.5)
|
|
855
|
+
q25 is value_at_quantile(tdigest_agg(n), 0.25)
|
|
856
|
+
q75 is value_at_quantile(tdigest_agg(n), 0.75)
|
|
857
|
+
}`).malloyResultMatches(runtime, {
|
|
858
|
+
median: 5.0,
|
|
859
|
+
q25: 5.0,
|
|
860
|
+
q75: 5.0,
|
|
861
|
+
});
|
|
862
|
+
}
|
|
863
|
+
);
|
|
864
|
+
|
|
865
|
+
it.when(presto)(
|
|
866
|
+
`runs tdigest functions with extreme quantiles - ${databaseName}`,
|
|
867
|
+
async () => {
|
|
868
|
+
await expect(`run: ${testData} -> {
|
|
869
|
+
aggregate:
|
|
870
|
+
min_quantile is value_at_quantile(tdigest_agg(n), 0.0)
|
|
871
|
+
max_quantile is value_at_quantile(tdigest_agg(n), 1.0)
|
|
872
|
+
q_at_min is quantile_at_value(tdigest_agg(n), 1.0)
|
|
873
|
+
q_at_max is quantile_at_value(tdigest_agg(n), 10.0)
|
|
874
|
+
}`).malloyResultMatches(runtime, {
|
|
875
|
+
min_quantile: 1,
|
|
876
|
+
max_quantile: 10,
|
|
877
|
+
q_at_min: 0.05,
|
|
878
|
+
q_at_max: 0.95,
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
);
|
|
882
|
+
});
|
|
601
883
|
}
|
|
602
884
|
);
|
|
603
885
|
|