@malloydata/malloy-tests 0.0.95-dev231019211822 → 0.0.95

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 (84) hide show
  1. package/README.md +213 -1
  2. package/dist/api.spec.d.ts +1 -1
  3. package/dist/api.spec.js +6 -13
  4. package/dist/api.spec.js.map +1 -1
  5. package/dist/databases/all/db_index.spec.js +21 -41
  6. package/dist/databases/all/db_index.spec.js.map +1 -1
  7. package/dist/databases/all/expr.spec.js +262 -339
  8. package/dist/databases/all/expr.spec.js.map +1 -1
  9. package/dist/databases/all/functions.spec.js +37 -35
  10. package/dist/databases/all/functions.spec.js.map +1 -1
  11. package/dist/databases/all/join.spec.js +125 -169
  12. package/dist/databases/all/join.spec.js.map +1 -1
  13. package/dist/databases/all/nomodel.spec.js +335 -594
  14. package/dist/databases/all/nomodel.spec.js.map +1 -1
  15. package/dist/databases/all/orderby.spec.js +82 -128
  16. package/dist/databases/all/orderby.spec.js.map +1 -1
  17. package/dist/databases/all/sql_expressions.spec.js +27 -43
  18. package/dist/databases/all/sql_expressions.spec.js.map +1 -1
  19. package/dist/databases/all/time.spec.js +63 -103
  20. package/dist/databases/all/time.spec.js.map +1 -1
  21. package/dist/databases/bigquery/double_truncation.spec.js +1 -1
  22. package/dist/databases/bigquery/handexpr.spec.js +12 -12
  23. package/dist/databases/bigquery/injestion_time_partitioning.spec.js +22 -22
  24. package/dist/databases/bigquery/joined_filters.spec.js +3 -3
  25. package/dist/databases/bigquery/json.spec.d.ts +1 -1
  26. package/dist/databases/bigquery/json.spec.js +25 -45
  27. package/dist/databases/bigquery/json.spec.js.map +1 -1
  28. package/dist/databases/bigquery/malloy_query.spec.d.ts +1 -1
  29. package/dist/databases/bigquery/malloy_query.spec.js +47 -48
  30. package/dist/databases/bigquery/malloy_query.spec.js.map +1 -1
  31. package/dist/databases/bigquery/time.spec.js +9 -13
  32. package/dist/databases/bigquery/time.spec.js.map +1 -1
  33. package/dist/databases/bigquery/wildcard_table_names.spec.js +19 -19
  34. package/dist/databases/bigquery-duckdb/nested_source_table.spec.js +53 -87
  35. package/dist/databases/bigquery-duckdb/nested_source_table.spec.js.map +1 -1
  36. package/dist/databases/bigquery-postgres/multi_connection.spec.js +5 -20
  37. package/dist/databases/bigquery-postgres/multi_connection.spec.js.map +1 -1
  38. package/dist/databases/bigquery-postgres/streaming.spec.js +6 -6
  39. package/dist/databases/bigquery-postgres/streaming.spec.js.map +1 -1
  40. package/dist/databases/duckdb/duckdb.spec.js +24 -33
  41. package/dist/databases/duckdb/duckdb.spec.js.map +1 -1
  42. package/dist/databases/postgres/postgres.spec.js +46 -67
  43. package/dist/databases/postgres/postgres.spec.js.map +1 -1
  44. package/dist/jestMatcher.spec.d.ts +1 -0
  45. package/dist/jestMatcher.spec.js +81 -0
  46. package/dist/jestMatcher.spec.js.map +1 -0
  47. package/dist/render/render.spec.js +10 -12
  48. package/dist/render/render.spec.js.map +1 -1
  49. package/dist/tags.spec.js +22 -2
  50. package/dist/tags.spec.js.map +1 -1
  51. package/dist/util/db-jest-matchers.d.ts +17 -6
  52. package/dist/util/db-jest-matchers.js +81 -20
  53. package/dist/util/db-jest-matchers.js.map +1 -1
  54. package/dist/util/index.d.ts +1 -2
  55. package/dist/util/index.js +11 -13
  56. package/dist/util/index.js.map +1 -1
  57. package/package.json +6 -6
  58. package/src/api.spec.ts +7 -16
  59. package/src/databases/all/db_index.spec.ts +22 -48
  60. package/src/databases/all/expr.spec.ts +273 -431
  61. package/src/databases/all/functions.spec.ts +37 -35
  62. package/src/databases/all/join.spec.ts +130 -196
  63. package/src/databases/all/nomodel.spec.ts +333 -689
  64. package/src/databases/all/orderby.spec.ts +87 -161
  65. package/src/databases/all/sql_expressions.spec.ts +29 -49
  66. package/src/databases/all/time.spec.ts +73 -130
  67. package/src/databases/bigquery/double_truncation.spec.ts +1 -1
  68. package/src/databases/bigquery/handexpr.spec.ts +12 -12
  69. package/src/databases/bigquery/injestion_time_partitioning.spec.ts +22 -22
  70. package/src/databases/bigquery/joined_filters.spec.ts +3 -3
  71. package/src/databases/bigquery/json.spec.ts +25 -49
  72. package/src/databases/bigquery/malloy_query.spec.ts +47 -54
  73. package/src/databases/bigquery/time.spec.ts +13 -17
  74. package/src/databases/bigquery/wildcard_table_names.spec.ts +19 -19
  75. package/src/databases/bigquery-duckdb/nested_source_table.spec.ts +56 -98
  76. package/src/databases/bigquery-postgres/multi_connection.spec.ts +5 -23
  77. package/src/databases/bigquery-postgres/streaming.spec.ts +12 -6
  78. package/src/databases/duckdb/duckdb.spec.ts +31 -43
  79. package/src/databases/postgres/postgres.spec.ts +54 -84
  80. package/src/jestMatcher.spec.ts +88 -0
  81. package/src/render/render.spec.ts +10 -12
  82. package/src/tags.spec.ts +22 -2
  83. package/src/util/db-jest-matchers.ts +106 -32
  84. package/src/util/index.ts +16 -14
@@ -35,8 +35,9 @@ const [describe, databases] = describeIfDatabaseAvailable([
35
35
  'duckdb',
36
36
  ]);
37
37
 
38
- const modelText = `
39
- source:ga_sessions is table('malloytest.ga_sample'){
38
+ function modelText(databaseName: string) {
39
+ return `
40
+ source: ga_sessions is ${databaseName}.table('malloytest.ga_sample') extend {
40
41
 
41
42
  measure:
42
43
  user_count is count(fullVisitorId)
@@ -49,41 +50,41 @@ source:ga_sessions is table('malloytest.ga_sample'){
49
50
  hits_count is hits.count()
50
51
  sold_count is hits.count() { where: hits.product.productQuantity > 0 }
51
52
 
52
- query: by_source is {
53
+ view: by_source is {
53
54
  where: trafficSource.\`source\` != '(direct)'
54
55
  group_by: trafficSource.\`source\`
55
56
  aggregate: hits_count
56
57
  limit: 10
57
58
  }
58
- query: by_adContent_bar_chart is {
59
+ view: by_adContent_bar_chart is {
59
60
  group_by: device.browser
60
61
  aggregate: user_count
61
62
  group_by: device.deviceCategory
62
63
  }
63
- query: by_region is {
64
+ view: by_region is {
64
65
  where: geoNetwork.region !~ '%demo%'
65
66
  group_by: geoNetwork.region
66
67
  aggregate: user_count
67
68
  limit: 10
68
69
  }
69
- query: by_device is {
70
+ view: by_device is {
70
71
  group_by: device.browser
71
72
  aggregate: user_count
72
73
  group_by: device.deviceCategory
73
74
  }
74
- query: by_category is {
75
+ view: by_category is {
75
76
  group_by: category is hits.product.v2ProductCategory
76
77
  aggregate: total_productRevenue
77
78
  aggregate: sold_count
78
79
  limit: 10
79
80
  }
80
- query: by_hour_of_day is {
81
+ view: by_hour_of_day is {
81
82
  // group_by: gsession_hour is hour(start_time::timestamp)
82
83
  aggregate: session_count
83
84
  order_by: 1
84
85
  }
85
86
 
86
- query: page_load_times is {
87
+ view: page_load_times is {
87
88
  group_by: hits.page.pageTitle
88
89
  aggregate: hit_count is hits.count()
89
90
  nest: load_bar_chart is {
@@ -93,20 +94,20 @@ source:ga_sessions is table('malloytest.ga_sample'){
93
94
  limit: 10
94
95
  }
95
96
 
96
- query: by_page_title is { where: totals.transactionRevenue > 0
97
+ view: by_page_title is { where: totals.transactionRevenue > 0
97
98
  group_by: hits.page.pageTitle
98
99
  aggregate: hits_count
99
100
  aggregate: sold_count
100
101
  }
101
102
 
102
- query: by_all is {
103
+ view: by_all is {
103
104
  nest: by_source
104
105
  nest: by_adContent_bar_chart
105
106
  nest: by_region
106
107
  nest: by_category
107
108
  }
108
109
 
109
- query: search_index is {
110
+ view: search_index is {
110
111
  index: *, hits.*, customDimensions.* totals.*, trafficSource.*, hits.product.*
111
112
  sample: 1%
112
113
  }
@@ -124,26 +125,21 @@ query: sessions_dashboard is ga_sessions -> {
124
125
  }
125
126
  }
126
127
  `;
128
+ }
127
129
 
128
130
  const runtimes = new RuntimeList(databases);
129
131
  describe.each(runtimes.runtimeList)(
130
132
  'Nested Source Table - %s',
131
133
  (databaseName, runtime) => {
134
+ const gaModel = runtime.loadModel(modelText(databaseName));
132
135
  test(`repeated child of record - ${databaseName}`, async () => {
133
- const result = await runtime
134
- .loadModel(modelText)
135
- .loadQuery(
136
- `
137
- query: ga_sessions->by_page_title
138
- `
139
- )
140
- .run();
141
- // console.log(result.data.toObject());
142
- // console.log(result.sql);
143
- expect(result.data.path(0, 'pageTitle').value).toBe('Shopping Cart');
136
+ await expect('run: ga_sessions->by_page_title').malloyResultMatches(
137
+ gaModel,
138
+ {pageTitle: 'Shopping Cart'}
139
+ );
144
140
  });
145
141
 
146
- // SKIPPED because the tests intermittently fail and lloyd said
142
+ // Tests intermittently fail and lloyd said
147
143
  // "I bet it has to do with my sampling test on indexing. Intermittently
148
144
  // getting different results. I'd comment out the test and I'll take a
149
145
  // look at it when I get back." and that seems reasonable.
@@ -155,35 +151,28 @@ describe.each(runtimes.runtimeList)(
155
151
  //
156
152
  // Expected: "Organic Search"
157
153
  // Received: "Referral"
158
- test.skip(`search_index - ${databaseName}`, async () => {
159
- const result = await runtime
160
- .loadModel(modelText)
161
- .loadQuery(
162
- `
163
- query: ga_sessions->search_index -> {
154
+ test(`search_index - ${databaseName}`, async () => {
155
+ await expect(`
156
+ run: ga_sessions->search_index -> {
164
157
  where: fieldName != null
165
158
  select: *
166
159
  order_by: fieldName, weight desc
167
160
  limit: 10
168
161
  }
169
- `
170
- )
171
- .run();
172
- // console.log(result.data.toObject());
173
- expect(result.data.path(0, 'fieldName').value).toBe('channelGrouping');
174
- expect(result.data.path(0, 'fieldValue').value).toBe('Organic Search');
175
- // expect(result.data.path(0, "weight").value).toBe(18);
162
+ `).malloyResultMatches(gaModel, {
163
+ fieldName: 'channelGrouping',
164
+ // fieldValue: 'Organic Search',
165
+ // weight: 10,
166
+ });
176
167
  });
177
168
 
178
- test.skip(`manual index - ${databaseName}`, async () => {
169
+ test(`manual index - ${databaseName}`, async () => {
179
170
  let sampleSize = '10';
180
171
  if (databaseName === 'bigquery') {
181
172
  sampleSize = 'false';
182
173
  }
183
- const _result = await runtime
184
- .loadQuery(
185
- `
186
- query: table('malloytest.ga_sample')-> {
174
+ await expect(`
175
+ run: ${databaseName}.table('malloytest.ga_sample')-> {
187
176
  index: everything
188
177
  sample: ${sampleSize}
189
178
  }
@@ -195,70 +184,39 @@ describe.each(runtimes.runtimeList)(
195
184
  limit: 100
196
185
  }
197
186
  }
198
- `
199
- )
200
- .run();
201
- // console.log(JSON.stringify(result.data.toObject(), null, 2));
202
- // expect(result.data.path(0, "fieldName").value).toBe("channelGrouping");
203
- // expect(result.data.path(0, "fieldValue").value).toBe("Organic Search");
204
- // expect(result.data.path(0, "weight").value).toBe(18);
187
+ `).malloyResultMatches(runtime, {
188
+ // 'top_fields.fieldName': 'channelGrouping',
189
+ // 'top_fields.fieldValue': 'Organic Search',
190
+ // 'top_fields.weight': 18,
191
+ });
205
192
  });
206
193
 
207
194
  test(`autobin - ${databaseName}`, async () => {
208
- const _result = await runtime
209
- .loadQuery(
210
- `
211
- source: airports is table('malloytest.airports') + {
212
- measure:
213
- airport_count is count()
214
- query: by_elevation is {
215
- aggregate: bin_size is NULLIF((max(elevation) - min(elevation))/30,0)
216
- nest: data is {
217
- group_by: elevation
218
- aggregate: row_count is count()
219
- }
220
- }
221
- -> {
222
- group_by: elevation is floor(data.elevation/bin_size)*bin_size + bin_size/2
223
- aggregate: airport_count is data.row_count.sum()
224
- order_by: elevation
195
+ await expect(`
196
+ source: airports is ${databaseName}.table('malloytest.airports') extend {
197
+ measure: airport_count is count()
198
+ view: by_elevation is {
199
+ aggregate: bin_size is NULLIF((max(elevation) - min(elevation))/30,0)
200
+ nest: data is {
201
+ group_by: elevation
202
+ aggregate: row_count is count()
225
203
  }
226
204
  }
227
-
228
- query: airports -> {
229
- group_by: state is state
230
- aggregate: airport_count
231
- nest: by_elevation_bar_chart is by_elevation
205
+ -> {
206
+ group_by: elevation is floor(data.elevation/bin_size)*bin_size + bin_size/2
207
+ aggregate: airport_count is data.row_count.sum()
208
+ order_by: elevation
232
209
  }
233
- `
234
- )
235
- .run();
236
- // console.log(result.sql);
237
- // console.log(result.data.toObject());
238
- // expect(result.data.path(0, 'fieldName').value).toBe('channelGrouping');
239
- // expect(result.data.path(0, 'fieldValue').value).toBe('Organic Search');
240
- // expect(result.data.path(0, "weight").value).toBe(18);
241
- });
242
-
243
- // this really shouldn't be here, we need to be able to test dialect capiblities
244
- // from the connection.
245
- test(`safe_cast - ${databaseName}`, async () => {
246
- const result = await runtime
247
- .loadQuery(
248
- `
249
- source: eone is table('malloytest.airports') {}
210
+ }
250
211
 
251
- query: eone -> {
252
- select:
253
- bad_date is '123':::date
254
- bad_number is 'abc':::number
255
- limit: 1
212
+ run: airports -> {
213
+ group_by: state is state
214
+ aggregate: airport_count
215
+ nest: by_elevation_bar_chart is by_elevation
256
216
  }
257
- `
258
- )
259
- .run();
260
- expect(result.data.value[0]['bad_date']).toBe(null);
261
- expect(result.data.value[0]['bad_number']).toBe(null);
217
+ `).malloyResultMatches(runtime, {
218
+ // don't know what to expect ...
219
+ });
262
220
  });
263
221
  }
264
222
  );
@@ -66,40 +66,22 @@ describe('Multi-connection', () => {
66
66
  });
67
67
 
68
68
  const expressionModelText = `
69
- source: default_aircraft is table('malloytest.aircraft'){
70
- measure: aircraft_count is count(tail_num)
71
- }
72
-
73
- source: bigquery_state_facts is table('malloytest.state_facts'){
69
+ source: bigquery_state_facts is bigquery.table('malloytest.state_facts') extend {
74
70
  measure: state_count is count(state)+2
75
71
  }
76
72
 
77
- source: postgres_aircraft is table('postgres:malloytest.aircraft'){
73
+ source: postgres_aircraft is postgres.table('malloytest.aircraft') extend {
78
74
  measure: aircraft_count is count(tail_num)+4
79
75
  }
80
76
  `;
81
77
 
82
78
  const expressionModel = runtime.loadModel(expressionModelText);
83
79
 
84
- it('default query', async () => {
85
- const result = await expressionModel
86
- .loadQuery(
87
- `
88
- query: default_aircraft-> {
89
- aggregate: aircraft_count
90
- }
91
- `
92
- )
93
- .run();
94
- // console.log(result.sql);
95
- expect(result.data.path(0, 'aircraft_count').value).toBe(3599);
96
- });
97
-
98
80
  it('bigquery query', async () => {
99
81
  const result = await expressionModel
100
82
  .loadQuery(
101
83
  `
102
- query: bigquery_state_facts-> {
84
+ run: bigquery_state_facts-> {
103
85
  aggregate: state_count
104
86
  }
105
87
  `
@@ -113,7 +95,7 @@ source: postgres_aircraft is table('postgres:malloytest.aircraft'){
113
95
  const result = await expressionModel
114
96
  .loadQuery(
115
97
  `
116
- query: postgres_aircraft-> {
98
+ run: postgres_aircraft-> {
117
99
  aggregate: aircraft_count
118
100
  }
119
101
  `
@@ -126,7 +108,7 @@ source: postgres_aircraft is table('postgres:malloytest.aircraft'){
126
108
  const result = await runtime
127
109
  .loadQuery(
128
110
  `
129
- query: table('postgres:malloytest.airports')->{
111
+ run: postgres.table('malloytest.airports')->{
130
112
  group_by:
131
113
  version is version!()
132
114
  aggregate:
@@ -57,8 +57,10 @@ describe('Streaming tests', () => {
57
57
  runtimes.runtimeMap.forEach((runtime, databaseName) => {
58
58
  it(`basic stream test - ${databaseName}`, async () => {
59
59
  const stream = runtime
60
- .loadModel("source: airports is table('malloytest.airports') {}")
61
- .loadQuery('query: airports -> { select: code }')
60
+ .loadModel(
61
+ `source: airports is ${databaseName}.table('malloytest.airports')`
62
+ )
63
+ .loadQuery('run: airports -> { select: code }')
62
64
  .runStream({rowLimit: 10});
63
65
  const rows: DataRecord[] = [];
64
66
  for await (const row of stream) {
@@ -70,8 +72,10 @@ describe('Streaming tests', () => {
70
72
 
71
73
  it(`stream to JSON - ${databaseName}`, async () => {
72
74
  const stream = runtime
73
- .loadModel("source: airports is table('malloytest.airports') {}")
74
- .loadQuery('query: airports -> { select: code }')
75
+ .loadModel(
76
+ `source: airports is ${databaseName}.table('malloytest.airports')`
77
+ )
78
+ .loadQuery('run: airports -> { select: code }')
75
79
  .runStream({rowLimit: 1});
76
80
  const accummulator = new StringAccumulator();
77
81
  const jsonWriter = new JSONWriter(accummulator);
@@ -88,8 +92,10 @@ describe('Streaming tests', () => {
88
92
 
89
93
  it(`stream to CSV - ${databaseName}`, async () => {
90
94
  const stream = runtime
91
- .loadModel("source: airports is table('malloytest.airports') {}")
92
- .loadQuery('query: airports -> { select: code }')
95
+ .loadModel(
96
+ `source: airports is ${databaseName}.table('malloytest.airports')`
97
+ )
98
+ .loadQuery('run: airports -> { select: code }')
93
99
  .runStream({rowLimit: 1});
94
100
  const accummulator = new StringAccumulator();
95
101
  const csvWriter = new CSVWriter(accummulator);
@@ -26,6 +26,7 @@ import {RuntimeList} from '../../runtimes';
26
26
  import '../../util/db-jest-matchers';
27
27
  import {describeIfDatabaseAvailable} from '../../util';
28
28
 
29
+ // TODO identify which tests need to run on wasm and move them into their own file
29
30
  const runtimes = ['duckdb', 'duckdb_wasm'];
30
31
 
31
32
  const [_describe, databases] = describeIfDatabaseAvailable(runtimes);
@@ -33,17 +34,12 @@ const allDucks = new RuntimeList(databases);
33
34
 
34
35
  describe.each(allDucks.runtimeList)('duckdb:%s', (dbName, runtime) => {
35
36
  it('can open tables with wildcards', async () => {
36
- const result = await runtime
37
- .loadQuery(
38
- `
39
- query: table('duckdb:test/data/duckdb/flights/part.*.parquet') -> {
40
- top: 1
41
- group_by: carrier;
42
- }
43
- `
44
- )
45
- .run();
46
- expect(result.data.path(0, 'carrier').value).toEqual('AA');
37
+ await expect(`
38
+ run: duckdb.table('test/data/duckdb/flights/part.*.parquet') -> {
39
+ top: 1
40
+ group_by: carrier;
41
+ }
42
+ `).malloyResultMatches(runtime, {carrier: 'AA'});
47
43
  });
48
44
 
49
45
  it('accepts all schema numbers', async () => {
@@ -60,36 +56,32 @@ describe.each(allDucks.runtimeList)('duckdb:%s', (dbName, runtime) => {
60
56
  ];
61
57
  const allFields = allInts.map(intType => `a${intType.toLowerCase()}`);
62
58
  const query = `
63
- sql: allInts is { connection: "${dbName}" select: """
59
+ run: ${dbName}.sql("""
64
60
  SELECT
65
61
  ${allInts
66
62
  .map(intType => `1::${intType} as a${intType.toLowerCase()}`)
67
63
  .join(',\n')}
68
- """}
69
- query: from_sql(allInts) -> {
64
+ """) -> {
70
65
  aggregate:
71
66
  ${allFields
72
67
  .map(fieldType => `sum_${fieldType} is sum(${fieldType})`)
73
68
  .join('\n')}
74
69
  }
75
70
  `;
76
- const result = await runtime.loadQuery(query).run();
77
- for (const fieldType of allFields) {
78
- expect(result.data.path(0, `sum_${fieldType}`).value).toEqual(1);
79
- }
71
+ await expect(query).malloyResultMatches(
72
+ runtime,
73
+ allInts.reduce<Record<string, number>>((building, ent) => {
74
+ building[`sum_a${ent.toLowerCase()}`] = 1;
75
+ return building;
76
+ }, {})
77
+ );
80
78
  });
81
79
 
82
80
  it('can open json files', async () => {
83
- const result = await runtime
84
- .loadQuery(
85
- `
86
- query: table('duckdb:test/data/duckdb/test.json') -> {
87
- select: *
88
- }
89
- `
90
- )
91
- .run();
92
- expect(result.data.path(0, 'foo').value).toEqual('bar');
81
+ await expect(`
82
+ run: duckdb.table('test/data/duckdb/test.json') -> {
83
+ select: *
84
+ }`).malloyResultMatches(runtime, {foo: 'bar'});
93
85
  });
94
86
 
95
87
  it('supports timezones', async () => {
@@ -101,10 +93,9 @@ describe.each(allDucks.runtimeList)('duckdb:%s', (dbName, runtime) => {
101
93
  });
102
94
 
103
95
  it('supports varchars with parameters', async () => {
104
- await expect(runtime).queryMatches(
105
- "run: duckdb.sql(\"SELECT 'a'::VARCHAR as abc, 'a3'::VARCHAR(3) as abc3\")",
106
- {abc: 'a', abc3: 'a3'}
107
- );
96
+ await expect(
97
+ "run: duckdb.sql(\"SELECT 'a'::VARCHAR as abc, 'a3'::VARCHAR(3) as abc3\")"
98
+ ).malloyResultMatches(runtime, {abc: 'a', abc3: 'a3'});
108
99
  });
109
100
 
110
101
  describe('time', () => {
@@ -118,17 +109,14 @@ describe.each(allDucks.runtimeList)('duckdb:%s', (dbName, runtime) => {
118
109
  second: 0,
119
110
  zone,
120
111
  });
121
- // TODO: Investigate why its no working.
122
- test.skip('can use unsupported types', async () => {
123
- await expect(runtime).queryMatches(
124
- `sql: timeData is { connection: "duckdb" select: """
125
- SELECT TIMESTAMPTZ '2020-02-20 00:00:00 ${zone}' as t_tstz
126
- """}
127
- query: from_sql(timeData) -> {
128
- select: mex_220 is t_tstz::timestamp
129
- }`,
130
- {mex_220: zone_2020.toJSDate()}
131
- );
112
+ test('can cast TIMESTAMPTZ to timestamp', async () => {
113
+ await expect(
114
+ `run: duckdb.sql("""
115
+ SELECT TIMESTAMPTZ '2020-02-20 00:00:00 ${zone}' as t_tstz
116
+ """) -> {
117
+ select: mex_220 is t_tstz::timestamp
118
+ }`
119
+ ).malloyResultMatches(runtime, {mex_220: zone_2020.toJSDate()});
132
120
  });
133
121
  });
134
122
  });
@@ -23,7 +23,9 @@
23
23
 
24
24
  /* eslint-disable no-console */
25
25
 
26
+ import {ok} from 'assert';
26
27
  import {RuntimeList} from '../../runtimes';
28
+ import {Runtime} from '@malloydata/malloy';
27
29
  import {describeIfDatabaseAvailable} from '../../util';
28
30
  import '../../util/db-jest-matchers';
29
31
  import {DateTime} from 'luxon';
@@ -56,96 +58,67 @@ describe('Postgres tests', () => {
56
58
  await runtimeList.closeAll();
57
59
  });
58
60
 
59
- it('sql_block', async () => {
60
- const result = await runtime
61
- .loadQuery(
62
- `
63
- sql: one is {select:"""
64
- SELECT 1 as n
65
- """}
66
-
67
- query: from_sql(one) -> { select: n }
68
- `
69
- )
70
- .run();
71
- expect(result.data.value[0]['n']).toBe(1);
61
+ it('run an sql query', async () => {
62
+ await expect(
63
+ 'run: postgres.sql("SELECT 1 as n") -> { select: n }'
64
+ ).malloyResultMatches(runtime, {n: 1});
72
65
  });
73
66
 
74
- it('quote field name', async () => {
75
- const result = await runtime
76
- .loadQuery(
77
- `
78
- sql: one is {select:"""
79
- SELECT 1 as "upperLower"
80
- """}
81
-
82
- query: from_sql(one) -> { select: upperLower }
83
- `
84
- )
85
- .run();
86
- expect(result.data.value[0]['upperLower']).toBe(1);
67
+ it('mixed case col names are properly quoted so they retain case in results', async () => {
68
+ await expect(`
69
+ run: postgres.sql('SELECT 1 as "upperLower"') -> { select: upperLower }
70
+ `).malloyResultMatches(runtime, {upperLower: 1});
87
71
  });
88
72
 
89
- it('quote keyword', async () => {
90
- const result = await runtime
91
- .loadQuery(
92
- `
93
- sql: one is {select:"""
94
- SELECT 1 as "select"
95
- """}
96
-
97
- query: from_sql(one) -> {
98
- select:
99
- select
100
- create is select + 1
101
- }
102
- `
103
- )
104
- .run();
105
- expect(result.data.value[0]['select']).toBe(1);
106
- expect(result.data.value[0]['create']).toBe(2);
73
+ it('fields which are sql keywords are quoted', async () => {
74
+ await expect(`
75
+ run: postgres.sql('SELECT 1 as "select"') -> {
76
+ select:
77
+ select
78
+ create is select + 1
79
+ }
80
+ `).malloyResultMatches(runtime, {select: 1, create: 2});
107
81
  });
108
82
 
109
- // these started failing and I'm not sure why (lloyd, skipping for now)
110
- it.skip('quote table name', async () => {
111
- const result = await runtime
112
- .loadQuery(
113
- `
114
- query: table('public.UpperTablePublic') -> { select: one }
115
- `
116
- )
117
- .run();
118
- expect(result.data.value[0]['one']).toBe(1);
83
+ async function oneExists(rt: Runtime, tn: string): Promise<boolean> {
84
+ try {
85
+ const lookForOne = await rt
86
+ .loadQuery(`run: postgres.sql('SELECT one FROM ${tn}')`)
87
+ .run();
88
+ const one = lookForOne.data.path(0, 'one').value;
89
+ return one === 1;
90
+ } catch (e) {
91
+ return false;
92
+ }
93
+ }
94
+
95
+ it('will quote to properly access mixed case table name', async () => {
96
+ if (await oneExists(runtime, 'public."UpperTablePublic"')) {
97
+ await expect(`
98
+ run: postgres.table('public.UpperTablePublic') -> { select: one }
99
+ `).malloyResultMatches(runtime, {one: 1});
100
+ }
119
101
  });
120
102
 
121
- it.skip('quote schema name', async () => {
122
- const result = await runtime
123
- .loadQuery(
124
- `
125
- query: table('UpperSchema.UpperSchemaUpperTable') -> { select: one }
126
- `
127
- )
128
- .run();
129
- expect(result.data.value[0]['one']).toBe(1);
103
+ it('quote to properly access mixes case schema name', async () => {
104
+ if (await oneExists(runtime, '"UpperSchema"."UpperSchemaUpperTable"')) {
105
+ await expect(`
106
+ run: postgres.table('UpperSchema.UpperSchemaUpperTable') -> { select: one }
107
+ `).malloyResultMatches(runtime, {one: 1});
108
+ }
130
109
  });
131
110
 
132
111
  it('passes unsupported data', async () => {
133
112
  const result = await runtime
134
- .loadQuery(
135
- `
136
- sql: badType is { select: """SELECT int4range(10, 20) as ranger""" }
137
- query: from_sql(badType)->{ select: *}
138
- `
139
- )
113
+ .loadQuery('run: postgres.sql("SELECT int4range(10, 20) as ranger")')
140
114
  .run();
141
115
  expect(result.data.value[0]['ranger']).toBeDefined();
142
116
  });
143
117
 
144
118
  it('supports varchars with parameters', async () => {
145
- await expect(runtime).queryMatches(
146
- "run: postgres.sql(\"SELECT 'a'::VARCHAR as abc, 'a3'::VARCHAR(3) as abc3\")",
147
- {abc: 'a', abc3: 'a3'}
148
- );
119
+ await expect(
120
+ "run: postgres.sql(\"SELECT 'a'::VARCHAR as abc, 'a3'::VARCHAR(3) as abc3\")"
121
+ ).malloyResultMatches(runtime, {abc: 'a', abc3: 'a3'});
149
122
  });
150
123
 
151
124
  describe('time', () => {
@@ -159,17 +132,14 @@ describe('Postgres tests', () => {
159
132
  second: 0,
160
133
  zone,
161
134
  });
162
- // TODO: Investigate why this test is not working.
163
- test.skip('can use unsupported types', async () => {
164
- await expect(runtime).queryMatches(
165
- `sql: timeData is { connection: "postgres" select: """
166
- SELECT TIMESTAMPTZ '2020-02-20 00:00:00 ${zone}' as t_tstz
167
- """}
168
- query: from_sql(timeData) -> {
169
- select: mex_220 is t_tstz::timestamp
170
- }`,
171
- {mex_220: zone_2020.toJSDate()}
172
- );
135
+ test('can cast TIMESTAMPTZ to timestamp', async () => {
136
+ await expect(
137
+ `run: duckdb.sql("""
138
+ SELECT TIMESTAMPTZ '2020-02-20 00:00:00 ${zone}' as t_tstz
139
+ """) -> {
140
+ select: mex_220 is t_tstz::timestamp
141
+ }`
142
+ ).malloyResultMatches(runtime, {mex_220: zone_2020.toJSDate()});
173
143
  });
174
144
  });
175
145
  });