@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 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. 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.
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
- * 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.
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
- > [!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.
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
- * 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"
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
- and if this is desirable, more work on the custom matcher would be needed to allow expressions like this to be written.
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.295",
25
- "@malloydata/db-duckdb": "0.0.295",
26
- "@malloydata/db-postgres": "0.0.295",
27
- "@malloydata/db-snowflake": "0.0.295",
28
- "@malloydata/db-trino": "0.0.295",
29
- "@malloydata/malloy": "0.0.295",
30
- "@malloydata/malloy-tag": "0.0.295",
31
- "@malloydata/render": "0.0.295",
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.295"
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