@malloydata/malloy 0.0.43-dev230621180201 → 0.0.43-dev230621212545

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.
@@ -22,76 +22,34 @@
22
22
  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23
23
  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
24
  */
25
- var __importDefault = (this && this.__importDefault) || function (mod) {
26
- return (mod && mod.__esModule) ? mod : { "default": mod };
27
- };
28
25
  Object.defineProperty(exports, "__esModule", { value: true });
29
26
  const model_1 = require("../../model");
30
27
  const test_translator_1 = require("./test-translator");
31
- const isEqual_1 = __importDefault(require("lodash/isEqual"));
32
28
  const granular_result_1 = require("../ast/types/granular-result");
33
29
  require("./parse-expects");
34
- function exprOK(s) {
35
- return () => {
36
- expect(new test_translator_1.BetaExpression(s)).modelParsed();
37
- return undefined;
38
- };
39
- }
40
- function exprType(s, t) {
41
- return () => {
42
- const expr = new test_translator_1.BetaExpression(s);
43
- expect(expr).modelParsed();
44
- expect(expr.generated().dataType).toBe(t);
45
- return undefined;
46
- };
47
- }
48
- function modelOK(s) {
49
- return () => {
50
- const m = new test_translator_1.TestTranslator(s);
51
- expect(m).modelCompiled();
52
- return undefined;
53
- };
54
- }
55
- function badModel(s, msg) {
56
- return () => {
57
- const src = typeof s === 'string' ? s : s.code;
58
- const emsg = `Error expectation not met\nExpected error: '${msg}'\nSource:\n${src}`;
59
- const m = new test_translator_1.TestTranslator(src);
60
- const t = m.translate();
61
- if (t.translated) {
62
- fail(emsg);
63
- }
64
- else {
65
- const errList = m.errors().errors;
66
- const firstError = errList[0];
67
- if (firstError.message !== msg) {
68
- fail(`Received errror: ${firstError.message}\n${emsg}`);
69
- }
70
- if (typeof s !== 'string') {
71
- if (!(0, isEqual_1.default)(errList[0].at, s.locations[0])) {
72
- fail(`Expected location: ${s.locations[0]}\n` +
73
- `Received location: ${errList[0].at}\n${emsg}`);
74
- }
75
- }
76
- }
77
- return undefined;
78
- };
79
- }
80
30
  describe('model statements', () => {
81
31
  describe('source:', () => {
82
- test('table', modelOK("source: testA is table('aTable')"));
32
+ test('table', () => {
33
+ expect("source: testA is table('aTable')").toTranslate();
34
+ });
83
35
  test('shorcut fitlered table', () => {
84
- expect("source: testA is table('aTable') {? astr ~ 'a%' } ").toCompile();
85
- });
86
- test('fitlered table', modelOK(`
87
- source: testA is table('aTable') { where: astr ~ 'a%' }
88
- `));
89
- test('ref source with no refinement', modelOK('source: testA is a'));
90
- test('from(query)', modelOK('source: testA is from(a->{group_by: astr})'));
91
- test('refine source', modelOK('source: aa is a { dimension: a is astr }'));
36
+ expect("source: xA is table('aTable') {? astr ~ 'a%' }").toTranslate();
37
+ });
38
+ test('fitlered table', () => {
39
+ expect("source: testA is table('aTable') { where: astr ~ 'a%' }").toTranslate();
40
+ });
41
+ test('ref source with no refinement', () => {
42
+ expect('source: testA is a').toTranslate();
43
+ });
44
+ test('from(query)', () => {
45
+ expect('source: testA is from(a->{group_by: astr})').toTranslate();
46
+ });
47
+ test('refine source', () => {
48
+ expect('source: aa is a { dimension: a is astr }').toTranslate();
49
+ });
92
50
  test('source refinement preserves original', () => {
93
51
  const x = new test_translator_1.TestTranslator('source: na is a + { dimension: one is 1 }');
94
- expect(x).modelCompiled();
52
+ expect(x).toTranslate();
95
53
  const a = x.getSourceDef('a');
96
54
  if (a) {
97
55
  const aFields = a.fields.map(f => f.as || f.name);
@@ -101,45 +59,67 @@ describe('model statements', () => {
101
59
  });
102
60
  });
103
61
  describe('query:', () => {
104
- test('anonymous query', modelOK("query: table('aTable') -> { group_by: astr }"));
105
- test('query', modelOK("query: name is table('aTable') -> { group_by: astr }"));
106
- test('query from query', modelOK(`
62
+ test('anonymous query', () => {
63
+ expect("query: table('aTable') -> { group_by: astr }").toTranslate();
64
+ });
65
+ test('query', () => {
66
+ expect("query: name is table('aTable') -> { group_by: astr }").toTranslate();
67
+ });
68
+ test('query from query', () => {
69
+ expect(`
107
70
  query: q1 is ab->{ group_by: astr limit: 10 }
108
71
  query: q2 is ->q1
109
- `));
110
- test('query with refinements from query', modelOK(`
72
+ `).toTranslate();
73
+ });
74
+ test('query with refinements from query', () => {
75
+ expect(`
111
76
  query: q1 is ab->{ group_by: astr limit: 10 }
112
77
  query: q2 is ->q1 { aggregate: acount }
113
- `));
114
- test('chained query operations', modelOK(`
78
+ `).toTranslate();
79
+ });
80
+ test('chained query operations', () => {
81
+ expect(`
115
82
  query: ab
116
83
  -> { group_by: astr; aggregate: acount }
117
84
  -> { top: 5; where: astr ~ 'a%' group_by: astr }
118
- `));
119
- test('from(query) refined into query', modelOK('query: from(ab -> {group_by: astr}) { dimension: bigstr is UPPER(astr) } -> { group_by: bigstr }'));
120
- test('query with shortcut filtered turtle', modelOK("query: allA is ab->aturtle {? astr ~ 'a%' }"));
121
- test('query with filtered turtle', modelOK("query: allA is ab->aturtle { where: astr ~ 'a%' }"));
122
- test('nest: in group_by:', modelOK(`
85
+ `).toTranslate();
86
+ });
87
+ test('from(query) refined into query', () => {
88
+ expect('query: from(ab -> {group_by: astr}) { dimension: bigstr is UPPER(astr) } -> { group_by: bigstr }').toTranslate();
89
+ });
90
+ test('query with shortcut filtered turtle', () => {
91
+ expect("query: allA is ab->aturtle {? astr ~ 'a%' }").toTranslate();
92
+ });
93
+ test('query with filtered turtle', () => {
94
+ expect("query: allA is ab->aturtle { where: astr ~ 'a%' }").toTranslate();
95
+ });
96
+ test('nest: in group_by:', () => {
97
+ expect(`
123
98
  query: ab -> {
124
99
  group_by: astr;
125
100
  nest: nested_count is {
126
101
  aggregate: acount
127
102
  }
128
103
  }
129
- `));
130
- test('reduce pipe project', modelOK(`
104
+ `).toTranslate();
105
+ });
106
+ test('reduce pipe project', () => {
107
+ expect(`
131
108
  query: a -> { aggregate: f is count() } -> { project: f2 is f + 1 }
132
- `));
133
- test('refine and extend query', modelOK(`
109
+ `).toTranslate();
110
+ });
111
+ test('refine and extend query', () => {
112
+ expect(`
134
113
  query: a_by_str is a -> { group_by: astr }
135
114
  query: -> a_by_str { aggregate: str_count is count() }
136
- `));
115
+ `).toTranslate();
116
+ });
137
117
  test('query refinement preserves original', () => {
138
118
  const x = new test_translator_1.TestTranslator(`
139
119
  query: q is a -> { aggregate: acount is count() }
140
120
  query: nq is -> q + { group_by: astr }
141
121
  `);
142
- expect(x).modelCompiled();
122
+ expect(x).toTranslate();
143
123
  const q = x.getQuery('q');
144
124
  expect(q).toBeDefined();
145
125
  if (q) {
@@ -152,14 +132,15 @@ describe('model statements', () => {
152
132
  query: q is ab -> { aggregate: acount }
153
133
  query: nq is -> q -> { project: * }
154
134
  `);
155
- expect(x).modelCompiled();
135
+ expect(x).toTranslate();
156
136
  const q = x.getQuery('q');
157
137
  expect(q).toBeDefined();
158
138
  if (q) {
159
139
  expect(q.pipeline.length).toBe(1);
160
140
  }
161
141
  });
162
- test('all ungroup with args', modelOK(`
142
+ test('all ungroup with args', () => {
143
+ expect(`
163
144
  query: a -> {
164
145
  group_by: astr
165
146
  nest: by_int is {
@@ -167,7 +148,8 @@ describe('model statements', () => {
167
148
  aggregate: bi_count is all(count(), ai)
168
149
  }
169
150
  }
170
- `));
151
+ `).toTranslate();
152
+ });
171
153
  test('all ungroup checks args', () => {
172
154
  expect(`
173
155
  query: a -> {
@@ -177,9 +159,10 @@ describe('model statements', () => {
177
159
  aggregate: bi_count is all(count(), afloat)
178
160
  }
179
161
  }
180
- `).compileToFailWith("all() 'afloat' is missing from query output");
162
+ `).translationToFailWith("all() 'afloat' is missing from query output");
181
163
  });
182
- test('exclude ungroup with args', modelOK(`
164
+ test('exclude ungroup with args', () => {
165
+ expect(`
183
166
  query: a -> {
184
167
  group_by: aa is 'a'
185
168
  nest: by_b is {
@@ -190,7 +173,8 @@ describe('model statements', () => {
190
173
  }
191
174
  }
192
175
  }
193
- `));
176
+ `).toTranslate();
177
+ });
194
178
  test('exclude ungroup checks args', () => {
195
179
  expect(`
196
180
  query: a -> {
@@ -203,9 +187,10 @@ describe('model statements', () => {
203
187
  }
204
188
  }
205
189
  }
206
- `).compileToFailWith("exclude() 'aaa' is missing from query output");
190
+ `).translationToFailWith("exclude() 'aaa' is missing from query output");
207
191
  });
208
- test('exclude problem revealed by production models', modelOK(`
192
+ test('exclude problem revealed by production models', () => {
193
+ expect(`
209
194
  source: carriers is table('malloytest.carriers') {
210
195
  primary_key: code
211
196
  }
@@ -222,89 +207,92 @@ describe('model statements', () => {
222
207
  }
223
208
  }
224
209
  }
225
- `));
210
+ `).toTranslate();
211
+ });
226
212
  describe('query operation typechecking', () => {
227
213
  describe('field declarations', () => {
228
214
  test('cannot use aggregate in group_by', () => {
229
- expect('query: a -> { group_by: s is count()}').compileToFailWith('Cannot use an aggregate field in a group_by operation, did you mean to use an aggregate operation instead?');
215
+ expect('query: a -> { group_by: s is count()}').translationToFailWith('Cannot use an aggregate field in a group_by operation, did you mean to use an aggregate operation instead?');
230
216
  });
231
217
  test('cannot use ungrouped_aggregate in group_by', () => {
232
- expect('query: a -> { group_by: s is all(count())}').compileToFailWith('Cannot use an aggregate field in a group_by operation, did you mean to use an aggregate operation instead?');
218
+ expect('query: a -> { group_by: s is all(count())}').translationToFailWith('Cannot use an aggregate field in a group_by operation, did you mean to use an aggregate operation instead?');
233
219
  });
234
220
  test('cannot use analytic in group_by', () => {
235
- expect('query: a -> { group_by: s is row_number()}').compileToFailWith('Cannot use an analytic field in a group_by operation, did you mean to use a calculate operation instead?');
221
+ expect('query: a -> { group_by: s is row_number()}').translationToFailWith('Cannot use an analytic field in a group_by operation, did you mean to use a calculate operation instead?');
236
222
  });
237
223
  test('cannot use aggregate in dimension', () => {
238
- expect('source: a1 is a { dimension: s is count()}').compileToFailWith('Cannot use an aggregate field in a dimension declaration, did you mean to use a measure declaration instead?');
224
+ expect('source: a1 is a { dimension: s is count()}').translationToFailWith('Cannot use an aggregate field in a dimension declaration, did you mean to use a measure declaration instead?');
239
225
  });
240
226
  test('cannot use ungrouped_aggregate in dimension', () => {
241
- expect('source: a1 is a { dimension: s is all(count())}').compileToFailWith('Cannot use an aggregate field in a dimension declaration, did you mean to use a measure declaration instead?');
227
+ expect('source: a1 is a { dimension: s is all(count())}').translationToFailWith('Cannot use an aggregate field in a dimension declaration, did you mean to use a measure declaration instead?');
242
228
  });
243
229
  test('cannot use analytic in dimension', () => {
244
- expect('source: a1 is a { dimension: s is row_number()}').compileToFailWith('Cannot use an analytic field in a dimension declaration');
230
+ expect('source: a1 is a { dimension: s is row_number()}').translationToFailWith('Cannot use an analytic field in a dimension declaration');
245
231
  });
246
232
  test('cannot use scalar in measure', () => {
247
- expect('source: a1 is a { measure: s is 1}').compileToFailWith('Cannot use a scalar field in a measure declaration, did you mean to use a dimension declaration instead?');
233
+ expect('source: a1 is a { measure: s is 1}').translationToFailWith('Cannot use a scalar field in a measure declaration, did you mean to use a dimension declaration instead?');
248
234
  });
249
235
  test('cannot use analytic in measure', () => {
250
- expect('source: a1 is a { measure: s is lag(count())}').compileToFailWith('Cannot use an analytic field in a measure declaration');
236
+ expect('source: a1 is a { measure: s is lag(count())}').translationToFailWith('Cannot use an analytic field in a measure declaration');
251
237
  });
252
238
  test('cannot use scalar in aggregate', () => {
253
- expect('query: a -> { aggregate: s is 1}').compileToFailWith('Cannot use a scalar field in an aggregate operation, did you mean to use a group_by or project operation instead?');
239
+ expect('query: a -> { aggregate: s is 1}').translationToFailWith('Cannot use a scalar field in an aggregate operation, did you mean to use a group_by or project operation instead?');
254
240
  });
255
241
  test('cannot use analytic in aggregate', () => {
256
- expect('query: a -> { aggregate: s is lag(count())}').compileToFailWith('Cannot use an analytic field in an aggregate operation, did you mean to use a calculate operation instead?');
242
+ expect('query: a -> { aggregate: s is lag(count())}').translationToFailWith('Cannot use an analytic field in an aggregate operation, did you mean to use a calculate operation instead?');
257
243
  });
258
244
  test('cannot use scalar in calculate', () => {
259
- expect('query: a -> { group_by: a is 1; calculate: s is 1 }').compileToFailWith('Cannot use a scalar field in a calculate operation, did you mean to use a group_by or project operation instead?');
245
+ expect('query: a -> { group_by: a is 1; calculate: s is 1 }').translationToFailWith('Cannot use a scalar field in a calculate operation, did you mean to use a group_by or project operation instead?');
260
246
  });
261
247
  test('cannot use aggregate in calculate', () => {
262
- expect('query: a -> { group_by: a is 1; calculate: s is count() }').compileToFailWith('Cannot use an aggregate field in a calculate operation, did you mean to use an aggregate operation instead?');
248
+ expect('query: a -> { group_by: a is 1; calculate: s is count() }').translationToFailWith('Cannot use an aggregate field in a calculate operation, did you mean to use an aggregate operation instead?');
263
249
  });
264
250
  test('cannot use aggregate in project', () => {
265
- expect('query: a -> { project: s is count() }').compileToFailWith('Cannot use an aggregate field in a project operation, did you mean to use an aggregate operation instead?');
251
+ expect('query: a -> { project: s is count() }').translationToFailWith('Cannot use an aggregate field in a project operation, did you mean to use an aggregate operation instead?');
266
252
  });
267
253
  test('cannot use analytic in project', () => {
268
- expect('query: a -> { project: s is row_number() }').compileToFailWith('Cannot use an analytic field in a project operation, did you mean to use a calculate operation instead?');
254
+ expect('query: a -> { project: s is row_number() }').translationToFailWith('Cannot use an analytic field in a project operation, did you mean to use a calculate operation instead?');
269
255
  });
270
256
  test('cannot use analytic in declare', () => {
271
- expect('query: a -> { group_by: a is 1; declare: s is row_number() }').compileToFailWith('Analytic expressions can not be used in a declare block');
257
+ expect('query: a -> { group_by: a is 1; declare: s is row_number() }').translationToFailWith('Analytic expressions can not be used in a declare block');
272
258
  });
273
259
  test('cannot use aggregate in index', () => {
274
- expect('query: a { measure: acount is count() } -> { index: acount }').compileToFailWith('Cannot use an aggregate field in an index operation');
260
+ expect('query: a { measure: acount is count() } -> { index: acount }').translationToFailWith('Cannot use an aggregate field in an index operation');
275
261
  });
276
- test('can use aggregate in except', modelOK(`
262
+ test('can use aggregate in except', () => {
263
+ expect(`
277
264
  source: b1 is a { measure: acount is count() }
278
265
  source: c1 is b1 { except: acount }
279
- `));
266
+ `).toTranslate();
267
+ });
280
268
  });
281
269
  describe('field references', () => {
282
270
  test('cannot use aggregate in group_by', () => {
283
- expect('query: a -> { declare: acount is count(); group_by: acount }').compileToFailWith('Cannot use an aggregate field in a group_by operation, did you mean to use an aggregate operation instead?');
271
+ expect('query: a -> { declare: acount is count(); group_by: acount }').translationToFailWith('Cannot use an aggregate field in a group_by operation, did you mean to use an aggregate operation instead?');
284
272
  });
285
273
  test('cannot use query in group_by', () => {
286
- expect('query: a { query: q is { group_by: x is 1 } } -> { group_by: q }').compileToFailWith('Cannot use a query field in a group_by operation, did you mean to use a nest operation instead?');
274
+ expect('query: a { query: q is { group_by: x is 1 } } -> { group_by: q }').translationToFailWith('Cannot use a query field in a group_by operation, did you mean to use a nest operation instead?');
287
275
  });
288
276
  test('cannot use scalar in aggregate', () => {
289
- expect('query: a -> { declare: aconst is 1; aggregate: aconst }').compileToFailWith('Cannot use a scalar field in an aggregate operation, did you mean to use a group_by or project operation instead?');
277
+ expect('query: a -> { declare: aconst is 1; aggregate: aconst }').translationToFailWith('Cannot use a scalar field in an aggregate operation, did you mean to use a group_by or project operation instead?');
290
278
  });
291
279
  test('cannot use scalar in calculate', () => {
292
- expect('query: a -> { declare: aconst is 1; group_by: x is 1; calculate: aconst }').compileToFailWith('Cannot use a scalar field in a calculate operation, did you mean to use a group_by or project operation instead?');
280
+ expect('query: a -> { declare: aconst is 1; group_by: x is 1; calculate: aconst }').translationToFailWith('Cannot use a scalar field in a calculate operation, did you mean to use a group_by or project operation instead?');
293
281
  });
294
282
  test('cannot use aggregate in calculate', () => {
295
- expect('query: a -> { declare: acount is count(); group_by: x is 1; calculate: acount }').compileToFailWith('Cannot use an aggregate field in a calculate operation, did you mean to use an aggregate operation instead?');
283
+ expect('query: a -> { declare: acount is count(); group_by: x is 1; calculate: acount }').translationToFailWith('Cannot use an aggregate field in a calculate operation, did you mean to use an aggregate operation instead?');
296
284
  });
297
285
  test('cannot use query in project', () => {
298
- expect('query: a { query: q is { group_by: x is 1 } } -> { project: q }').compileToFailWith('Cannot use a query field in a project operation, did you mean to use a nest operation instead?');
286
+ expect('query: a { query: q is { group_by: x is 1 } } -> { project: q }').translationToFailWith('Cannot use a query field in a project operation, did you mean to use a nest operation instead?');
299
287
  });
300
288
  test('cannot use query in index', () => {
301
- expect('query: a { query: q is { group_by: x is 1 } } -> { index: q }').compileToFailWith('Cannot use a query field in an index operation');
289
+ expect('query: a { query: q is { group_by: x is 1 } } -> { index: q }').translationToFailWith('Cannot use a query field in an index operation');
302
290
  });
303
291
  test('cannot use query in calculate', () => {
304
- expect('query: a { query: q is { group_by: x is 1 } } -> { group_by: x is 1; calculate: q }').compileToFailWith('Cannot use a query field in a calculate operation, did you mean to use a nest operation instead?');
292
+ expect('query: a { query: q is { group_by: x is 1 } } -> { group_by: x is 1; calculate: q }').translationToFailWith('Cannot use a query field in a calculate operation, did you mean to use a nest operation instead?');
305
293
  });
306
294
  test('cannot use query in aggregate', () => {
307
- expect('query: a { query: q is { group_by: x is 1 } } -> { aggregate: q }').compileToFailWith('Cannot use a query field in an aggregate operation, did you mean to use a nest operation instead?');
295
+ expect('query: a { query: q is { group_by: x is 1 } } -> { aggregate: q }').translationToFailWith('Cannot use a query field in an aggregate operation, did you mean to use a nest operation instead?');
308
296
  });
309
297
  test('cannot use aggregate in calculate, preserved over refinement', () => {
310
298
  expect(`query: a1 is a -> {
@@ -312,7 +300,7 @@ describe('model statements', () => {
312
300
  }
313
301
  query: -> a1 {
314
302
  calculate: b is c
315
- }`).compileToFailWith('Cannot use an aggregate field in a calculate operation, did you mean to use an aggregate operation instead?');
303
+ }`).translationToFailWith('Cannot use an aggregate field in a calculate operation, did you mean to use an aggregate operation instead?');
316
304
  });
317
305
  test('cannot use scalar in calculate, preserved over refinement', () => {
318
306
  expect(`query: a1 is a -> {
@@ -320,7 +308,7 @@ describe('model statements', () => {
320
308
  }
321
309
  query: -> a1 {
322
310
  calculate: b is c
323
- }`).compileToFailWith('Cannot use a scalar field in a calculate operation, did you mean to use a group_by or project operation instead?');
311
+ }`).translationToFailWith('Cannot use a scalar field in a calculate operation, did you mean to use a group_by or project operation instead?');
324
312
  });
325
313
  test('cannot use analytic in group_by, preserved over refinement', () => {
326
314
  expect(`query: a1 is a -> {
@@ -329,7 +317,7 @@ describe('model statements', () => {
329
317
  }
330
318
  query: -> a1 {
331
319
  group_by: b is c2
332
- }`).compileToFailWith(
320
+ }`).translationToFailWith(
333
321
  // c2 is not defined because group_by doesn't know to look in the output space
334
322
  "'c2' is not defined", "Cannot define 'b', value has unknown type");
335
323
  });
@@ -340,85 +328,99 @@ describe('model statements', () => {
340
328
  }
341
329
  query: -> a1 {
342
330
  order_by: c2
343
- }`).compileToFailWith('Illegal order by of analytic field c2');
331
+ }`).translationToFailWith('Illegal order by of analytic field c2');
344
332
  });
345
333
  test('cannot ungroup an ungrouped', () => {
346
334
  expect(`query: a1 is a -> {
347
335
  group_by: c is 1
348
336
  aggregate: c2 is all(all(sum(ai)))
349
- }`).compileToFailWith('all() expression must not already be ungrouped', "Cannot define 'c2', value has unknown type");
337
+ }`).translationToFailWith('all() expression must not already be ungrouped', "Cannot define 'c2', value has unknown type");
350
338
  });
351
339
  test('cannot aggregate an ungrouped', () => {
352
340
  expect(`query: a1 is a -> {
353
341
  group_by: c is 1
354
342
  aggregate: c2 is sum(all(sum(ai)))
355
- }`).compileToFailWith('Aggregate expression cannot be aggregate', "Cannot define 'c2', value has unknown type");
343
+ }`).translationToFailWith('Aggregate expression cannot be aggregate', "Cannot define 'c2', value has unknown type");
356
344
  });
357
345
  test('cannot aggregate an aggregate', () => {
358
346
  expect(`query: a1 is a -> {
359
347
  group_by: c is 1
360
348
  aggregate: c2 is sum(sum(ai))
361
- }`).compileToFailWith('Aggregate expression cannot be aggregate', "Cannot define 'c2', value has unknown type");
349
+ }`).translationToFailWith('Aggregate expression cannot be aggregate', "Cannot define 'c2', value has unknown type");
362
350
  });
363
- test('can use field def in group_by, preserved over refinement', modelOK(`query: a1 is a -> {
351
+ test('can use field def in group_by, preserved over refinement', () => {
352
+ expect(`query: a1 is a -> {
364
353
  group_by: c is 1
365
354
  }
366
355
  query: -> a1 {
367
356
  order_by: c
368
- }`));
369
- test('can use field ref in group_by, preserved over refinement', modelOK(`query: a1 is a -> {
357
+ }`).toTranslate();
358
+ });
359
+ test('can use field ref in group_by, preserved over refinement', () => {
360
+ expect(`query: a1 is a -> {
370
361
  group_by: c is astr
371
362
  }
372
363
  query: -> a1 {
373
364
  order_by: c
374
- }`));
365
+ }`).toTranslate();
366
+ });
375
367
  });
376
368
  });
377
369
  describe('function typechecking', () => {
378
- test('use function correctly', modelOK(`query: a -> {
370
+ test('use function correctly', () => {
371
+ expect(`query: a -> {
379
372
  group_by: s is concat('a', 'b')
380
- }`));
373
+ }`).toTranslate();
374
+ });
381
375
  test('function no matching overload', () => {
382
376
  expect(`query: a -> {
383
377
  group_by: s is floor('a', 'b')
384
- }`).compileToFailWith('No matching overload for function floor(string, string)', "Cannot define 's', value has unknown type");
378
+ }`).translationToFailWith('No matching overload for function floor(string, string)', "Cannot define 's', value has unknown type");
385
379
  });
386
380
  test('unknown function', () => {
387
381
  expect(`query: a -> {
388
382
  group_by: s is asdfasdf()
389
- }`).compileToFailWith("Unknown function 'asdfasdf'. Use 'asdfasdf!(...)' to call a SQL function directly.", "Cannot define 's', value has unknown type");
383
+ }`).translationToFailWith("Unknown function 'asdfasdf'. Use 'asdfasdf!(...)' to call a SQL function directly.", "Cannot define 's', value has unknown type");
390
384
  });
391
- test('can select different overload', modelOK('query: a -> { group_by: s is concat() }'));
392
- test('can pass different expression types', modelOK(`query: a -> {
385
+ test('can select different overload', () => {
386
+ expect('query: a -> { group_by: s is concat() }').toTranslate();
387
+ });
388
+ test('can pass different expression types', () => {
389
+ expect(`query: a -> {
393
390
  group_by: f1 is sqrt(1)
394
391
  aggregate: f2 is sqrt(count())
395
392
  aggregate: f3 is sqrt(all(count()))
396
393
  calculate: f4 is sqrt(lag(f1))
397
394
  calculate: f5 is sqrt(lag(count()))
398
- }`));
399
- test('function return type correct', modelOK(`query: a -> {
395
+ }`).toTranslate();
396
+ });
397
+ test('function return type correct', () => {
398
+ expect(`query: a -> {
400
399
  group_by: s is floor(1.2) + 1
401
- }`));
400
+ }`).toTranslate();
401
+ });
402
402
  test('function return type incorrect', () => {
403
403
  expect(`query: a -> {
404
404
  group_by: s is floor(1.2) + 'a'
405
- }`).compileToFailWith("Non numeric('number,string') value with '+'", "Cannot define 's', value has unknown type");
405
+ }`).translationToFailWith("Non numeric('number,string') value with '+'", "Cannot define 's', value has unknown type");
406
406
  });
407
- test('can use output value in calculate', modelOK(`query: a -> {
407
+ test('can use output value in calculate', () => {
408
+ expect(`query: a -> {
408
409
  group_by: x is 1
409
410
  calculate: s is lag(x)
410
- }`));
411
+ }`).toTranslate();
412
+ });
411
413
  test('cannot use output value in group_by', () => {
412
414
  expect(`query: a -> {
413
415
  group_by: x is 1
414
416
  group_by: y is x
415
- }`).compileToFailWith("'x' is not defined", "Cannot define 'y', value has unknown type");
417
+ }`).translationToFailWith("'x' is not defined", "Cannot define 'y', value has unknown type");
416
418
  });
417
419
  test('lag can check that other args are constant', () => {
418
420
  expect(`query: a -> {
419
421
  group_by: x is 1
420
422
  calculate: s is lag(x, 1, x)
421
- }`).compileToFailWith(
423
+ }`).translationToFailWith(
422
424
  // TODO improve this error message
423
425
  "Parameter 3 ('default') of lag must be literal or constant, but received output");
424
426
  });
@@ -426,7 +428,7 @@ describe('model statements', () => {
426
428
  expect(`query: a -> {
427
429
  group_by: x is 1
428
430
  calculate: s is lag(x, 1 + 1)
429
- }`).compileToFailWith(
431
+ }`).translationToFailWith(
430
432
  // TODO improve this error message
431
433
  "Parameter 2 ('offset') of lag must be literal, but received constant");
432
434
  });
@@ -434,28 +436,32 @@ describe('model statements', () => {
434
436
  expect(`query: a -> {
435
437
  group_by: x is 1
436
438
  calculate: s is lag(x, null)
437
- }`).compileToFailWith("Parameter 2 ('offset') of lag must not be a literal null");
439
+ }`).translationToFailWith("Parameter 2 ('offset') of lag must not be a literal null");
438
440
  });
439
- test('lag can use constant values for other args', modelOK(`query: a -> {
441
+ test('lag can use constant values for other args', () => {
442
+ expect(`query: a -> {
440
443
  group_by: x is 1
441
444
  calculate: s is lag(x, 2)
442
- }`));
445
+ }`).toTranslate();
446
+ });
443
447
  test('cannot name top level objects same as functions', () => {
444
- expect((0, test_translator_1.markSource) `query: ${'concat is a -> { group_by: x is 1 }'}`).compileToFailWith(
448
+ expect((0, test_translator_1.markSource) `query: ${'concat is a -> { group_by: x is 1 }'}`).translationToFailWith(
445
449
  // TODO improve this error message
446
450
  "'concat' is already defined, cannot redefine");
447
451
  });
448
- test('`now` is considered constant`', modelOK(`query: a -> {
452
+ test('`now` is considered constant`', () => {
453
+ expect(`query: a -> {
449
454
  group_by: n is now
450
455
  calculate: l is lag(n, 1, now)
451
- }`));
456
+ }`).toTranslate();
457
+ });
452
458
  // TODO it might be nice to reference a field which is a constant, and be able to
453
459
  // use that as a constant param. Same with a literal.
454
460
  test('cannot use a field which is a constant in a constant param', () => {
455
461
  expect(`query: a -> {
456
462
  group_by: ai, pi is pi()
457
463
  calculate: l is lag(ai, 1, pi)
458
- }`).compileToFailWith("Parameter 3 ('default') of lag must be literal or constant, but received output");
464
+ }`).translationToFailWith("Parameter 3 ('default') of lag must be literal or constant, but received output");
459
465
  });
460
466
  // TODO we don't handle referencing a join as a field correctly in all cases today.
461
467
  // For now, it at least is considered type `struct` and therefore fails to parse
@@ -465,58 +471,66 @@ describe('model statements', () => {
465
471
  expect(`query: a {join_one: b with astr } -> {
466
472
  group_by: b
467
473
  calculate: foo is lag(b)
468
- }`).compileToFailWith('No matching overload for function lag(struct)', "Cannot define 'foo', value has unknown type");
474
+ }`).translationToFailWith('No matching overload for function lag(struct)', "Cannot define 'foo', value has unknown type");
469
475
  });
470
476
  // TODO this doesn't work today, we're not rigorous enough with integer
471
477
  // subtypes. But we should probably make this typecheck properly.
472
478
  test.skip('cannot use float in round precision', () => {
473
479
  expect(`query: a -> {
474
480
  group_by: x is round(1.5, 1.6)
475
- }`).compileToFailWith(
481
+ }`).translationToFailWith(
476
482
  // TODO improve this error message
477
483
  "Parameter 2 ('precision') for round must be integer, received float");
478
484
  });
479
485
  test('cannot use stddev with no arguments', () => {
480
486
  expect(`query: a -> {
481
487
  aggregate: x is stddev()
482
- }`).compileToFailWith('No matching overload for function stddev()', "Cannot define 'x', value has unknown type");
488
+ }`).translationToFailWith('No matching overload for function stddev()', "Cannot define 'x', value has unknown type");
483
489
  });
484
- test('can use stddev with postfix syntax', modelOK(`query: a -> {
490
+ test('can use stddev with postfix syntax', () => {
491
+ expect(`query: a -> {
485
492
  declare: y is 1
486
493
  aggregate: x is y.stddev()
487
- }`));
488
- test('can use stddev with postfix syntax on join', modelOK(`query: a -> {
494
+ }`).toTranslate();
495
+ });
496
+ test('can use stddev with postfix syntax on join', () => {
497
+ expect(`query: a -> {
489
498
  join_one: b with astr
490
499
  aggregate: x is b.ai.stddev()
491
- }`));
492
- test('can use calculate with a measure', modelOK(`query: a { measure: c is count() } -> {
500
+ }`).toTranslate();
501
+ });
502
+ test('can use calculate with a measure', () => {
503
+ expect(`query: a { measure: c is count() } -> {
493
504
  group_by: y is 1
494
505
  calculate: x is lag(c)
495
- }`));
506
+ }`).toTranslate();
507
+ });
496
508
  test('cannot use calculate with input fields', () => {
497
509
  expect(`query: a -> {
498
510
  group_by: y is 1
499
511
  calculate: x is lag(ai)
500
- }`).compileToFailWith(
512
+ }`).translationToFailWith(
501
513
  // TODO improve this error message:
502
514
  // Parameter 1 ('value') of 'lag' must be a constant, an aggregate, or an expression using
503
515
  // only fields that appear in the query output. Received an expression which uses a field
504
516
  // that is not in the query output.
505
517
  "Parameter 1 ('value') of lag must be literal, constant or output, but received input");
506
518
  });
507
- test('can use calculate with aggregate field which is not in query', modelOK(`query: a { measure: acount is count() } -> {
519
+ test('can use calculate with aggregate field which is not in query', () => {
520
+ expect(`query: a { measure: acount is count() } -> {
508
521
  group_by: astr
509
522
  calculate: pc is lag(acount)
510
- }`));
523
+ }`).toTranslate();
524
+ });
511
525
  test('cannot use agregate as argument to agg function', () => {
512
526
  expect(`query: a -> {
513
527
  aggregate: x is stddev(count())
514
- }`).compileToFailWith("Parameter 1 ('value') of stddev must be scalar, but received aggregate");
528
+ }`).translationToFailWith("Parameter 1 ('value') of stddev must be scalar, but received aggregate");
515
529
  });
516
530
  test('cannot use calculate with no other fields', () => {
517
531
  expect(`query: a -> {
518
532
  calculate: x is row_number()
519
- }`).compileToFailWith("Can't determine query type (group_by/aggregate/nest,project,index)");
533
+ }`).translationToFailWith("Can't determine query type (group_by/aggregate/nest,project,index)");
520
534
  });
521
535
  // TODO someday make it so we can order by an analytic function
522
536
  test('today: cannot order by analytic function', () => {
@@ -524,7 +538,7 @@ describe('model statements', () => {
524
538
  group_by: astr
525
539
  calculate: row_num is row_number()
526
540
  order_by: row_num desc
527
- }`).compileToFailWith('Illegal order by of analytic field row_num');
541
+ }`).translationToFailWith('Illegal order by of analytic field row_num');
528
542
  });
529
543
  test('cannot use analytic in calculate -- and preserved over refinement', () => {
530
544
  expect(`query: a1 is a -> {
@@ -533,72 +547,97 @@ describe('model statements', () => {
533
547
  }
534
548
  query: -> a1 {
535
549
  calculate: p1 is lag(p)
536
- }`).compileToFailWith("Parameter 1 ('value') of lag must be scalar or aggregate, but received scalar_analytic");
550
+ }`).translationToFailWith("Parameter 1 ('value') of lag must be scalar or aggregate, but received scalar_analytic");
537
551
  });
538
552
  test('cannot use aggregate analytic in project', () => {
539
553
  expect(`query: a -> {
540
554
  project: astr
541
555
  calculate: p is lag(count())
542
- }`).compileToFailWith('Cannot add aggregate analyics to project');
556
+ }`).translationToFailWith('Cannot add aggregate analyics to project');
543
557
  });
544
- test('reference field in join', modelOK(`query: a -> {
558
+ test('reference field in join', () => {
559
+ expect(`query: a -> {
545
560
  join_one: b with astr
546
561
  group_by: b.ai
547
- }`));
562
+ }`).toTranslate();
563
+ });
548
564
  // TODO decide whether this syntax should be legal, really...
549
- test('reference join name in group_by', modelOK(`query: a -> {
565
+ test('reference join name in group_by', () => {
566
+ expect(`query: a -> {
550
567
  join_one: b with astr
551
568
  group_by: b
552
- }`));
553
- test('can reference project: inline join.* field in calculate', modelOK(`query: a -> {
569
+ }`).toTranslate();
570
+ });
571
+ test('can reference project: inline join.* field in calculate', () => {
572
+ expect(`query: a -> {
554
573
  join_one: b with astr
555
574
  project: b.*
556
575
  calculate: s is lag(ai)
557
- }`));
558
- test('can reference project: join.* field in calculate', modelOK(`query: a { join_one: b with astr } -> {
576
+ }`).toTranslate();
577
+ });
578
+ test('can reference project: join.* field in calculate', () => {
579
+ expect(`query: a { join_one: b with astr } -> {
559
580
  project: b.*
560
581
  calculate: s is lag(ai)
561
- }`));
582
+ }`).toTranslate();
583
+ });
562
584
  });
563
585
  });
564
586
  test('errors on redefinition of query', () => {
565
- expect('query: q1 is a -> { project: * }, q1 is a -> { project: * }').compileToFailWith("'q1' is already defined, cannot redefine");
587
+ expect('query: q1 is a -> { project: * }, q1 is a -> { project: * }').translationToFailWith("'q1' is already defined, cannot redefine");
566
588
  });
567
589
  });
568
590
  describe('source properties', () => {
569
- test('single dimension', modelOK('source: aa is a { dimension: x is 1 }'));
570
- test('multiple dimensions', modelOK(`
591
+ test('single dimension', () => {
592
+ expect('source: aa is a { dimension: x is 1 }').toTranslate();
593
+ });
594
+ test('multiple dimensions', () => {
595
+ expect(`
571
596
  source: aa is a {
572
597
  dimension:
573
598
  x is 1
574
599
  y is 2
575
600
  }
576
- `));
577
- test('single declare', modelOK('source: aa is a { declare: x is 1 }'));
578
- test('multiple declare', modelOK(`
601
+ `).toTranslate();
602
+ });
603
+ test('single declare', () => {
604
+ expect('source: aa is a { declare: x is 1 }').toTranslate();
605
+ });
606
+ test('multiple declare', () => {
607
+ expect(`
579
608
  source: aa is a {
580
609
  declare:
581
610
  x is 1
582
611
  y is 2
583
612
  }
584
- `));
585
- test('single measure', modelOK('source: aa is a { measure: x is count() }'));
586
- test('multiple measures', modelOK(`
613
+ `).toTranslate();
614
+ });
615
+ test('single measure', () => {
616
+ expect('source: aa is a { measure: x is count() }').toTranslate();
617
+ });
618
+ test('multiple measures', () => {
619
+ expect(`
587
620
  source: aa is a {
588
621
  measure:
589
622
  x is count()
590
623
  y is x * x
591
624
  }
592
- `));
593
- test('single where', modelOK('source: aa is a { where: ai > 10 }'));
594
- test('multiple where', modelOK(`
625
+ `).toTranslate();
626
+ });
627
+ test('single where', () => {
628
+ expect('source: aa is a { where: ai > 10 }').toTranslate();
629
+ });
630
+ test('multiple where', () => {
631
+ expect(`
595
632
  source: aa is a {
596
633
  where:
597
634
  ai > 10,
598
635
  af < 1000
599
636
  }
600
- `));
601
- test('where clause can use the join namespace in source refined query', modelOK(`
637
+ `).toTranslate();
638
+ });
639
+ test('where clause can use the join namespace in source refined query', () => {
640
+ expect(`
602
641
  source: flights is table('malloytest.flights') + {
603
642
  query: boo is {
604
643
  join_one: carriers is table('malloytest.carriers') on carrier = carriers.code
@@ -606,61 +645,97 @@ describe('source properties', () => {
606
645
  group_by: carriers.nickname
607
646
  aggregate: flight_count is count()
608
647
  }
609
- }`));
648
+ }`).toTranslate();
649
+ });
610
650
  describe('joins', () => {
611
- test('with', modelOK('source: x is a { join_one: b with astr }'));
612
- test('with', modelOK('source: x is a { join_one: y is b with astr }'));
613
- test('with dotted ref', modelOK('source: x is ab { join_one: xz is a with b.astr }'));
614
- test('one on', modelOK('source: x is a { join_one: b on astr = b.astr }'));
615
- test('one is on', modelOK('source: x is a { join_one: y is b on astr = y.astr }'));
616
- test('many on', modelOK('source: nab is a { join_many: b on astr = b.astr }'));
617
- test('many is on', modelOK('source: y is a { join_many: x is b on astr = x.astr }'));
618
- test('cross', modelOK('source: nab is a { join_cross: b }'));
619
- test('cross is', modelOK('source: nab is a { join_cross: xb is b }'));
620
- test('cross on', modelOK('source: nab is a { join_cross: b on true}'));
621
- test('multiple joins', modelOK(`
651
+ test('with', () => {
652
+ expect('source: x is a { join_one: b with astr }').toTranslate();
653
+ });
654
+ test('with', () => {
655
+ expect((0, test_translator_1.model) `source: x is a { join_one: y is b with astr }`).toTranslate();
656
+ });
657
+ test('with dotted ref', () => {
658
+ expect((0, test_translator_1.model) `source: x is ab { join_one: xz is a with b.astr }`).toTranslate();
659
+ });
660
+ test('one on', () => {
661
+ expect((0, test_translator_1.model) `source: x is a { join_one: b on astr = b.astr }`).toTranslate();
662
+ });
663
+ test('one is on', () => {
664
+ expect('source: x is a { join_one: y is b on astr = y.astr }').toTranslate();
665
+ });
666
+ test('many on', () => {
667
+ expect('source: nab is a { join_many: b on astr = b.astr }').toTranslate();
668
+ });
669
+ test('many is on', () => {
670
+ expect('source: y is a { join_many: x is b on astr = x.astr }').toTranslate();
671
+ });
672
+ test('cross', () => {
673
+ expect('source: nab is a { join_cross: b }').toTranslate();
674
+ });
675
+ test('cross is', () => {
676
+ expect('source: nab is a { join_cross: xb is b }').toTranslate();
677
+ });
678
+ test('cross on', () => {
679
+ expect('source: nab is a { join_cross: b on true}').toTranslate();
680
+ });
681
+ test('multiple joins', () => {
682
+ expect(`
622
683
  source: nab is a {
623
684
  join_one:
624
685
  b with astr,
625
686
  br is b with astr
626
687
  }
627
- `));
688
+ `).toTranslate();
689
+ });
628
690
  test('with requires primary key', () => {
629
691
  expect((0, test_translator_1.markSource) `
630
692
  source: nb is b {
631
693
  join_one: ${"bb is table('aTable') with astr"}
632
694
  }
633
- `).compileToFailWith('join_one: Cannot use with unless source has a primary key');
695
+ `).translationToFailWith('join_one: Cannot use with unless source has a primary key');
634
696
  });
635
697
  });
636
- test('primary_key', modelOK('source: c is a { primary_key: ai }'));
637
- test('rename', modelOK('source: c is a { rename: nn is ai }'));
698
+ test('primary_key', () => {
699
+ expect('source: c is a { primary_key: ai }').toTranslate();
700
+ });
701
+ test('rename', () => {
702
+ expect('source: c is a { rename: nn is ai }').toTranslate();
703
+ });
638
704
  test('accept single', () => {
639
705
  const onlyAstr = new test_translator_1.TestTranslator('source: c is a { accept: astr }');
640
- expect(onlyAstr).modelCompiled();
706
+ expect(onlyAstr).toTranslate();
641
707
  const c = onlyAstr.getSourceDef('c');
642
708
  if (c) {
643
709
  expect(c.fields.length).toBe(1);
644
710
  }
645
711
  });
646
- test('accept multi', modelOK('source: c is a { accept: astr, af }'));
712
+ test('accept multi', () => {
713
+ expect('source: c is a { accept: astr, af }').toTranslate();
714
+ });
647
715
  test('except single', () => {
648
716
  const noAstr = new test_translator_1.TestTranslator('source: c is a { except: astr }');
649
- expect(noAstr).modelCompiled();
717
+ expect(noAstr).toTranslate();
650
718
  const c = noAstr.getSourceDef('c');
651
719
  if (c) {
652
720
  const foundAstr = c.fields.find(f => f.name === 'astr');
653
721
  expect(foundAstr).toBeUndefined();
654
722
  }
655
723
  });
656
- test('except multi', modelOK('source: c is a { except: astr, af }'));
657
- test('explore-query', modelOK('source: c is a {query: q is { group_by: astr } }'));
658
- test('refined explore-query', modelOK(`
724
+ test('except multi', () => {
725
+ expect('source: c is a { except: astr, af }').toTranslate();
726
+ });
727
+ test('explore-query', () => {
728
+ expect('source: c is a {query: q is { group_by: astr } }').toTranslate();
729
+ });
730
+ test('refined explore-query', () => {
731
+ expect(`
659
732
  source: abNew is ab {
660
733
  query: for1 is aturtle {? ai = 1 }
661
734
  }
662
- `));
663
- test('chained explore-query', modelOK(`
735
+ `).toTranslate();
736
+ });
737
+ test('chained explore-query', () => {
738
+ expect(`
664
739
  source: c is a {
665
740
  query: chain is {
666
741
  group_by: astr
@@ -669,40 +744,69 @@ describe('source properties', () => {
669
744
  project: *
670
745
  }
671
746
  }
672
- `));
673
- test('multiple explore-query', modelOK(`
747
+ `).toTranslate();
748
+ });
749
+ test('multiple explore-query', () => {
750
+ expect(`
674
751
  source: abNew is ab {
675
752
  query:
676
753
  q1 is { group_by: astr },
677
754
  q2 is { group_by: ai }
678
755
  }
679
- `));
756
+ `).toTranslate();
757
+ });
680
758
  });
681
759
  describe('qops', () => {
682
- test('group by single', modelOK('query: a->{ group_by: astr }'));
683
- test("group_by x is x'", modelOK('query: a->{ group_by: ai is ai/2 }'));
684
- test('group by multiple', modelOK('query: a->{ group_by: astr,ai }'));
685
- test('aggregate single', modelOK('query: a->{ aggregate: num is count() }'));
686
- test('aggregate multiple', modelOK(`
760
+ test('group by single', () => {
761
+ expect('query: a->{ group_by: astr }').toTranslate();
762
+ });
763
+ test("group_by x is x'", () => {
764
+ expect('query: a->{ group_by: ai is ai/2 }').toTranslate();
765
+ });
766
+ test('group by multiple', () => {
767
+ expect('query: a->{ group_by: astr,ai }').toTranslate();
768
+ });
769
+ test('aggregate single', () => {
770
+ expect('query: a->{ aggregate: num is count() }').toTranslate();
771
+ });
772
+ test('aggregate multiple', () => {
773
+ expect(`
687
774
  query: a->{
688
775
  aggregate: num is count(), total is sum(ai)
689
776
  }
690
- `));
691
- test('project ref', modelOK('query:ab->{ project: b.astr }'));
692
- test('project *', modelOK('query:ab->{ project: * }'));
693
- test('project def', modelOK('query:ab->{ project: one is 1 }'));
694
- test('project multiple', modelOK(`
777
+ `).toTranslate();
778
+ });
779
+ test('project ref', () => {
780
+ expect('query:ab->{ project: b.astr }').toTranslate();
781
+ });
782
+ test('project *', () => {
783
+ expect('query:ab->{ project: * }').toTranslate();
784
+ });
785
+ test('project def', () => {
786
+ expect('query:ab->{ project: one is 1 }').toTranslate();
787
+ });
788
+ test('project multiple', () => {
789
+ expect(`
695
790
  query: a->{
696
791
  project: one is 1, astr
697
792
  }
698
- `));
699
- test('index single', modelOK('query:a->{index: astr}'));
700
- test('index path', modelOK('query:ab->{index: ab.astr}'));
701
- test('index unique on path', modelOK('query:ab->{index: b.astr, ab.astr}'));
702
- test('index join.*', modelOK('query:ab->{index: ab.*}'));
793
+ `).toTranslate();
794
+ });
795
+ test('index single', () => {
796
+ expect('query:a->{index: astr}').toTranslate();
797
+ });
798
+ test('index path', () => {
799
+ expect('query:ab->{index: ab.astr}').toTranslate();
800
+ });
801
+ test('index unique on path', () => {
802
+ expect('query:ab->{index: b.astr, ab.astr}').toTranslate();
803
+ });
804
+ test('index join.*', () => {
805
+ expect('query:ab->{index: ab.*}').toTranslate();
806
+ });
703
807
  test('index multiple', () => {
704
808
  const model = new test_translator_1.TestTranslator('query:a->{index: af, astr}');
705
- expect(model).modelCompiled();
809
+ expect(model).toTranslate();
706
810
  const q = model.getQuery(0);
707
811
  expect(q).toBeDefined();
708
812
  if (q) {
@@ -713,7 +817,7 @@ describe('qops', () => {
713
817
  });
714
818
  test('index star', () => {
715
819
  const model = new test_translator_1.TestTranslator('query:a->{index: *, astr}');
716
- expect(model).modelCompiled();
820
+ expect(model).toTranslate();
717
821
  const q = model.getQuery(0);
718
822
  expect(q).toBeDefined();
719
823
  if (q) {
@@ -722,12 +826,18 @@ describe('qops', () => {
722
826
  expect(index.fields).toEqual(['*', 'astr']);
723
827
  }
724
828
  });
725
- test('index by', modelOK('query:a->{index: * by ai}'));
726
- test('index sampled', modelOK('query:a->{index: *; sample: true}'));
727
- test('index unsampled', modelOK('query:a->{index: *; sample: false}'));
829
+ test('index by', () => {
830
+ expect('query:a->{index: * by ai}').toTranslate();
831
+ });
832
+ test('index sampled', () => {
833
+ expect('query:a->{index: *; sample: true}').toTranslate();
834
+ });
835
+ test('index unsampled', () => {
836
+ expect('query:a->{index: *; sample: false}').toTranslate();
837
+ });
728
838
  test('index sample-percent', () => {
729
839
  const model = new test_translator_1.TestTranslator('query:a->{index: *; sample: 42%}');
730
- expect(model).modelCompiled();
840
+ expect(model).toTranslate();
731
841
  const q = model.getQuery(0);
732
842
  expect(q).toBeDefined();
733
843
  if (q) {
@@ -738,35 +848,67 @@ describe('qops', () => {
738
848
  }
739
849
  }
740
850
  });
741
- test('index sample-rows', modelOK('query:a->{index: *; sample: 100000}'));
742
- test('top N', modelOK('query: a->{ top: 5; group_by: astr }'));
743
- test('top N by field', modelOK('query: a->{top: 5 by astr; group_by: astr}'));
744
- test('top N by expression', modelOK('query: ab->{top: 5 by ai + 1; group_by: ai}'));
745
- test('top N by field must be in the output space', () => expect('query: a->{top: 5 by af; group_by: astr}').compileToFailWith('Unknown field af in output space'));
746
- test('limit N', modelOK('query: a->{ limit: 5; group_by: astr }'));
747
- test('order by', modelOK('query: a->{ order_by: astr; group_by: astr }'));
748
- test('order by preserved over refinement', modelOK(`
851
+ test('index sample-rows', () => {
852
+ expect('query:a->{index: *; sample: 100000}').toTranslate();
853
+ });
854
+ test('top N', () => {
855
+ expect('query: a->{ top: 5; group_by: astr }').toTranslate();
856
+ });
857
+ test('top N by field', () => {
858
+ expect('query: a->{top: 5 by astr; group_by: astr}').toTranslate();
859
+ });
860
+ test('top N by expression', () => {
861
+ expect('query: ab->{top: 5 by ai + 1; group_by: ai}').toTranslate();
862
+ });
863
+ test('top N by field must be in the output space', () => expect('query: a->{top: 5 by af; group_by: astr}').translationToFailWith('Unknown field af in output space'));
864
+ test('limit N', () => {
865
+ expect('query: a->{ limit: 5; group_by: astr }').toTranslate();
866
+ });
867
+ test('order by', () => {
868
+ expect('query: a->{ order_by: astr; group_by: astr }').toTranslate();
869
+ });
870
+ test('order by preserved over refinement', () => {
871
+ expect(`
749
872
  query: a1 is a -> { group_by: astr }
750
873
  query: -> a1 { order_by: astr }
751
- `));
752
- test('order by must be in the output space', () => expect('query: a -> { order_by: af; group_by: astr }').compileToFailWith('Unknown field af in output space'));
753
- test('order by asc', modelOK('query: a->{ order_by: astr asc; group_by: astr }'));
754
- test('order by desc', modelOK('query: a->{ order_by: astr desc; group_by: astr }'));
755
- test('order by N', modelOK('query: a->{ order_by: 1 asc; group_by: astr }'));
756
- test('order by multiple', modelOK(`
874
+ `).toTranslate();
875
+ });
876
+ test('order by must be in the output space', () => expect('query: a -> { order_by: af; group_by: astr }').translationToFailWith('Unknown field af in output space'));
877
+ test('order by asc', () => {
878
+ expect('query: a->{ order_by: astr asc; group_by: astr }').toTranslate();
879
+ });
880
+ test('order by desc', () => {
881
+ expect('query: a->{ order_by: astr desc; group_by: astr }').toTranslate();
882
+ });
883
+ test('order by N', () => {
884
+ expect('query: a->{ order_by: 1 asc; group_by: astr }').toTranslate();
885
+ });
886
+ test('order by multiple', () => {
887
+ expect(`
757
888
  query: a->{
758
889
  order_by: 1 asc, af desc
759
890
  group_by: astr, af
760
891
  }
761
- `));
762
- test('where single', modelOK('query:a->{ group_by: astr; where: af > 10 }'));
763
- test('having single', modelOK('query:ab->{ aggregate: acount; group_by: astr; having: acount > 10 }'));
764
- test('compound having still works', modelOK('query:ab->{ aggregate: acount; having: acount > 10 and acount < 100 }'));
765
- test('compound aggregate still works', modelOK('query:ab->{ aggregate: thing is acount > 10 and acount < 100 }'));
766
- test('where multiple', modelOK("query:a->{ group_by: astr; where: af > 10,astr~'a%' }"));
892
+ `).toTranslate();
893
+ });
894
+ test('where single', () => {
895
+ expect('query:a->{ group_by: astr; where: af > 10 }').toTranslate();
896
+ });
897
+ test('having single', () => {
898
+ expect('query:ab->{ aggregate: acount; group_by: astr; having: acount > 10 }').toTranslate();
899
+ });
900
+ test('compound having still works', () => {
901
+ expect('query:ab->{ aggregate: acount; having: acount > 10 and acount < 100 }').toTranslate();
902
+ });
903
+ test('compound aggregate still works', () => {
904
+ expect('query:ab->{ aggregate: thing is acount > 10 and acount < 100 }').toTranslate();
905
+ });
906
+ test('where multiple', () => {
907
+ expect("query:a->{ group_by: astr; where: af > 10,astr~'a%' }").toTranslate();
908
+ });
767
909
  test('filters preserve source formatting in code:', () => {
768
910
  const model = new test_translator_1.TestTranslator("source: notb is a + { where: astr != 'b' }");
769
- expect(model).modelCompiled();
911
+ expect(model).toTranslate();
770
912
  const notb = model.getSourceDef('notb');
771
913
  expect(notb).toBeDefined();
772
914
  if (notb) {
@@ -779,7 +921,7 @@ describe('qops', () => {
779
921
  });
780
922
  test('field expressions preserve source formatting in code:', () => {
781
923
  const model = new test_translator_1.TestTranslator('source: notb is a + { dimension: d is 1 + 2 }');
782
- expect(model).modelCompiled();
924
+ expect(model).toTranslate();
783
925
  const notb = model.getSourceDef('notb');
784
926
  expect(notb).toBeDefined();
785
927
  if (notb) {
@@ -791,21 +933,27 @@ describe('qops', () => {
791
933
  }
792
934
  }
793
935
  });
794
- test('nest single', modelOK(`
936
+ test('nest single', () => {
937
+ expect(`
795
938
  query: a->{
796
939
  group_by: ai
797
940
  nest: nestbystr is { group_by: astr; aggregate: N is count() }
798
941
  }
799
- `));
800
- test('nest multiple', modelOK(`
942
+ `).toTranslate();
943
+ });
944
+ test('nest multiple', () => {
945
+ expect(`
801
946
  query: a->{
802
947
  group_by: ai
803
948
  nest:
804
949
  nestbystr is { group_by: astr; aggregate: N is count() },
805
950
  renest is { group_by: astr; aggregate: N is count() }
806
951
  }
807
- `));
808
- test('nest ref', modelOK('query: ab->{group_by: ai; nest: aturtle}'));
952
+ `).toTranslate();
953
+ });
954
+ test('nest ref', () => {
955
+ expect('query: ab->{group_by: ai; nest: aturtle}').toTranslate();
956
+ });
809
957
  test('refine query with extended source', () => {
810
958
  const m = new test_translator_1.TestTranslator(`
811
959
  source: nab is ab {
@@ -815,7 +963,7 @@ describe('qops', () => {
815
963
  }
816
964
  query: nab -> xturtle + { aggregate: aratio }
817
965
  `);
818
- expect(m).modelCompiled();
966
+ expect(m).toTranslate();
819
967
  const t = m.translate();
820
968
  if (t.translated) {
821
969
  const q = t.translated.queryList[0].pipeline[0];
@@ -839,7 +987,7 @@ describe('qops', () => {
839
987
  aggregate: aratio
840
988
  }
841
989
  `);
842
- expect(m).modelCompiled();
990
+ expect(m).toTranslate();
843
991
  const t = m.translate();
844
992
  if (t.translated) {
845
993
  const q = t.translated.queryList[0].pipeline[0];
@@ -863,7 +1011,7 @@ describe('qops', () => {
863
1011
  group_by: foo is bb.astr
864
1012
  }
865
1013
  `);
866
- expect(m).modelCompiled();
1014
+ expect(m).toTranslate();
867
1015
  const t = m.translate();
868
1016
  if (t.translated) {
869
1017
  const q = t.translated.queryList[0].pipeline[0];
@@ -878,10 +1026,20 @@ describe('qops', () => {
878
1026
  });
879
1027
  });
880
1028
  describe('literals', () => {
881
- test('integer', exprOK('42'));
882
- test('string', exprOK("'fortywo-two'"));
883
- test('string with quoted quote', exprOK("'Isn" + '\\' + "'t this nice'"));
884
- test('string with quoted backslash', exprOK("'Is " + '\\' + '\\' + " nice'"));
1029
+ test('integer', () => {
1030
+ expect((0, test_translator_1.expr) `42`).toTranslate();
1031
+ });
1032
+ test('string', () => {
1033
+ expect((0, test_translator_1.expr) `'fortywo-two'`).toTranslate();
1034
+ });
1035
+ test('string with quoted quote', () => {
1036
+ const str = "'Isn" + '\\' + "'t this nice'";
1037
+ expect(new test_translator_1.BetaExpression(str)).toTranslate();
1038
+ });
1039
+ test('string with quoted backslash', () => {
1040
+ const str = "'Is " + '\\' + '\\' + " nice'";
1041
+ expect(new test_translator_1.BetaExpression(str)).toTranslate();
1042
+ });
885
1043
  const literalTimes = [
886
1044
  ['@1960', 'date', 'year', { literal: '1960-01-01' }],
887
1045
  ['@1960-Q2', 'date', 'quarter', { literal: '1960-04-01' }],
@@ -925,7 +1083,7 @@ describe('literals', () => {
925
1083
  ];
926
1084
  test.each(literalTimes)('%s', (expr, timeType, timeframe, result) => {
927
1085
  const exprModel = new test_translator_1.BetaExpression(expr);
928
- expect(exprModel).modelCompiled();
1086
+ expect(exprModel).toTranslate();
929
1087
  const ir = exprModel.generated();
930
1088
  expect(ir.dataType).toEqual(timeType);
931
1089
  if (timeframe) {
@@ -949,7 +1107,7 @@ describe('literals', () => {
949
1107
  ];
950
1108
  test.each(morphicLiterals)('morphic value for %s is %s', (expr, morphic) => {
951
1109
  const exprModel = new test_translator_1.BetaExpression(expr);
952
- expect(exprModel).modelCompiled();
1110
+ expect(exprModel).toTranslate();
953
1111
  const ir = exprModel.generated();
954
1112
  const morphTo = ir.morphic && ir.morphic['timestamp'];
955
1113
  if (morphic) {
@@ -962,13 +1120,27 @@ describe('literals', () => {
962
1120
  expect(morphTo).toBeUndefined();
963
1121
  }
964
1122
  });
965
- test('minute+locale', exprOK('@1960-06-30 10:30[America/Los_Angeles]'));
966
- test('second 8601', exprOK('@1960-06-30T10:30:31'));
967
- test('null', exprOK('null'));
968
- test('now', exprOK('now'));
969
- test('true', exprOK('true'));
970
- test('false', exprOK('false'));
971
- test('regex', exprOK("r'RegularExpression'"));
1123
+ test('minute+locale', () => {
1124
+ expect((0, test_translator_1.expr) `@1960-06-30 10:30[America/Los_Angeles]`).toTranslate();
1125
+ });
1126
+ test('second 8601', () => {
1127
+ expect((0, test_translator_1.expr) `@1960-06-30T10:30:31`).toTranslate();
1128
+ });
1129
+ test('null', () => {
1130
+ expect((0, test_translator_1.expr) `null`).toTranslate();
1131
+ });
1132
+ test('now', () => {
1133
+ expect((0, test_translator_1.expr) `now`).toTranslate();
1134
+ });
1135
+ test('true', () => {
1136
+ expect((0, test_translator_1.expr) `true`).toTranslate();
1137
+ });
1138
+ test('false', () => {
1139
+ expect((0, test_translator_1.expr) `false`).toTranslate();
1140
+ });
1141
+ test('regex', () => {
1142
+ expect((0, test_translator_1.expr) `r'RegularExpression'`).toTranslate();
1143
+ });
972
1144
  });
973
1145
  describe('expressions', () => {
974
1146
  describe('timeframes', () => {
@@ -983,69 +1155,115 @@ describe('expressions', () => {
983
1155
  'year',
984
1156
  ];
985
1157
  test.each(timeframes.map(x => [x]))('truncate %s', unit => {
986
- expect(new test_translator_1.BetaExpression(`ats.${unit}`)).modelParsed();
1158
+ expect(new test_translator_1.BetaExpression(`ats.${unit}`)).toParse();
987
1159
  });
988
1160
  // mtoy todo units missing: implement, or document
989
1161
  const diffable = ['second', 'minute', 'hour', 'day'];
990
1162
  test.each(diffable.map(x => [x]))('timestamp difference - %s', unit => {
991
- expect(new test_translator_1.BetaExpression(`${unit}(@2021 to ats)`)).modelParsed();
1163
+ expect(new test_translator_1.BetaExpression(`${unit}(@2021 to ats)`)).toParse();
992
1164
  });
993
1165
  test.each(diffable.map(x => [x]))('timestamp difference - %s', unit => {
994
- expect(new test_translator_1.BetaExpression(`${unit}(ats to @2030)`)).modelParsed();
1166
+ expect(new test_translator_1.BetaExpression(`${unit}(ats to @2030)`)).toParse();
995
1167
  });
996
1168
  });
997
- test('field name', exprOK('astr'));
998
- test('function call', exprOK("concat('foo')"));
1169
+ test('field name', () => {
1170
+ expect((0, test_translator_1.expr) `astr`).toTranslate();
1171
+ });
1172
+ test('function call', () => {
1173
+ expect((0, test_translator_1.expr) `concat('foo')`).toTranslate();
1174
+ });
999
1175
  describe('operators', () => {
1000
- test('addition', exprOK('42 + 7'));
1001
- test('subtraction', exprOK('42 - 7'));
1002
- test('multiplication', exprOK('42 * 7'));
1003
- test('mod', exprOK('42 % 7'));
1004
- test('division', exprOK('42 / 7'));
1005
- test('unary negation', exprOK('- ai'));
1006
- test('equal', exprOK('42 = 7'));
1007
- test('not equal', exprOK('42 != 7'));
1008
- test('greater than', exprOK('42 > 7'));
1009
- test('greater than or equal', exprOK('42 >= 7'));
1010
- test('less than or equal', exprOK('42 <= 7'));
1011
- test('less than', exprOK('42 < 7'));
1012
- test('match', exprOK("'forty-two' ~ 'fifty-four'"));
1013
- test('not match', exprOK("'forty-two' !~ 'fifty-four'"));
1014
- test('apply', exprOK("'forty-two' ? 'fifty-four'"));
1015
- test('not', exprOK('not true'));
1016
- test('and', exprOK('true and false'));
1017
- test('or', exprOK('true or false'));
1018
- test('null-check (??)', exprOK('ai ?? 7'));
1176
+ test('addition', () => {
1177
+ expect((0, test_translator_1.expr) `42 + 7`).toTranslate();
1178
+ });
1179
+ test('subtraction', () => {
1180
+ expect((0, test_translator_1.expr) `42 - 7`).toTranslate();
1181
+ });
1182
+ test('multiplication', () => {
1183
+ expect((0, test_translator_1.expr) `42 * 7`).toTranslate();
1184
+ });
1185
+ test('mod', () => {
1186
+ expect((0, test_translator_1.expr) `42 % 7`).toTranslate();
1187
+ });
1188
+ test('division', () => {
1189
+ expect((0, test_translator_1.expr) `42 / 7`).toTranslate();
1190
+ });
1191
+ test('unary negation', () => {
1192
+ expect((0, test_translator_1.expr) `- ai`).toTranslate();
1193
+ });
1194
+ test('equal', () => {
1195
+ expect((0, test_translator_1.expr) `42 = 7`).toTranslate();
1196
+ });
1197
+ test('not equal', () => {
1198
+ expect((0, test_translator_1.expr) `42 != 7`).toTranslate();
1199
+ });
1200
+ test('greater than', () => {
1201
+ expect((0, test_translator_1.expr) `42 > 7`).toTranslate();
1202
+ });
1203
+ test('greater than or equal', () => {
1204
+ expect((0, test_translator_1.expr) `42 >= 7`).toTranslate();
1205
+ });
1206
+ test('less than or equal', () => {
1207
+ expect((0, test_translator_1.expr) `42 <= 7`).toTranslate();
1208
+ });
1209
+ test('less than', () => {
1210
+ expect((0, test_translator_1.expr) `42 < 7`).toTranslate();
1211
+ });
1212
+ test('match', () => {
1213
+ expect((0, test_translator_1.expr) `'forty-two' ~ 'fifty-four'`).toTranslate();
1214
+ });
1215
+ test('not match', () => {
1216
+ expect((0, test_translator_1.expr) `'forty-two' !~ 'fifty-four'`).toTranslate();
1217
+ });
1218
+ test('apply', () => {
1219
+ expect((0, test_translator_1.expr) `'forty-two' ? 'fifty-four'`).toTranslate();
1220
+ });
1221
+ test('not', () => {
1222
+ expect((0, test_translator_1.expr) `not true`).toTranslate();
1223
+ });
1224
+ test('and', () => {
1225
+ expect((0, test_translator_1.expr) `true and false`).toTranslate();
1226
+ });
1227
+ test('or', () => {
1228
+ expect((0, test_translator_1.expr) `true or false`).toTranslate();
1229
+ });
1230
+ test('null-check (??)', () => {
1231
+ expect((0, test_translator_1.expr) `ai ?? 7`).toTranslate();
1232
+ });
1019
1233
  test('disallow date OP number', () => {
1020
- expect(new test_translator_1.BetaExpression('@2001 = 7')).compileToFailWith('Cannot compare a date to a number');
1234
+ expect(new test_translator_1.BetaExpression('@2001 = 7')).translationToFailWith('Cannot compare a date to a number');
1021
1235
  });
1022
1236
  test('disallow date OP timestamp', () => {
1023
- expect(new test_translator_1.BetaExpression('ad = ats')).compileToFailWith('Cannot compare a date to a timestamp');
1237
+ expect(new test_translator_1.BetaExpression('ad = ats')).translationToFailWith('Cannot compare a date to a timestamp');
1024
1238
  });
1025
1239
  test('disallow interval from date to timestamp', () => {
1026
- expect(new test_translator_1.BetaExpression('days(ad to ats)')).compileToFailWith('Cannot measure from date to timestamp');
1240
+ expect(new test_translator_1.BetaExpression('days(ad to ats)')).translationToFailWith('Cannot measure from date to timestamp');
1027
1241
  });
1028
1242
  test('comparison promotes date literal to timestamp', () => {
1029
- expect('@2001 = ats').expressionCompiled();
1243
+ expect((0, test_translator_1.expr) `@2001 = ats`).toTranslate();
1030
1244
  });
1031
1245
  test('can apply range to date', () => {
1032
- expect('ad ? @2001 for 1 day').expressionCompiled();
1246
+ expect((0, test_translator_1.expr) `ad ? @2001 for 1 day`).toTranslate();
1033
1247
  });
1034
1248
  const noOffset = ['second', 'minute', 'hour'];
1035
1249
  test.each(noOffset.map(x => [x]))('disallow date delta %s', unit => {
1036
- expect(new test_translator_1.BetaExpression(`ad + 10 ${unit}s`)).compileToFailWith(`Cannot offset date by ${unit}`);
1250
+ expect(new test_translator_1.BetaExpression(`ad + 10 ${unit}s`)).translationToFailWith(`Cannot offset date by ${unit}`);
1037
1251
  });
1038
1252
  });
1039
- test('filtered measure', exprOK("acount {? astr = 'why?' }"));
1040
- test('filtered ungrouped aggregate', modelOK(`
1253
+ test('filtered measure', () => {
1254
+ expect((0, test_translator_1.expr) `acount {? astr = 'why?' }`).toTranslate();
1255
+ });
1256
+ test('filtered ungrouped aggregate', () => {
1257
+ expect(`
1041
1258
  query: a -> {
1042
1259
  group_by: ai
1043
1260
  aggregate: x is all(avg(ai)) { where: true }
1044
1261
  }
1045
- `));
1262
+ `).toTranslate();
1263
+ });
1046
1264
  test('correctly flags filtered scalar', () => {
1047
1265
  const e = new test_translator_1.BetaExpression('ai { where: true }');
1048
- expect(e).compileToFailWith('Filtered expression requires an aggregate computation');
1266
+ expect(e).translationToFailWith('Filtered expression requires an aggregate computation');
1049
1267
  });
1050
1268
  test('correctly flags filtered analytic', () => {
1051
1269
  expect((0, test_translator_1.markSource) `
@@ -1053,65 +1271,93 @@ describe('expressions', () => {
1053
1271
  group_by: ai
1054
1272
  calculate: l is lag(ai) { where: true }
1055
1273
  }
1056
- `).compileToFailWith('Filtered expression requires an aggregate computation');
1274
+ `).translationToFailWith('Filtered expression requires an aggregate computation');
1057
1275
  });
1058
1276
  describe('aggregate forms', () => {
1059
- test('count', exprOK('count()'));
1060
- test('count distinct', exprOK('count(distinct astr)'));
1061
- test('join.count()', exprOK('b.count()'));
1277
+ test('count', () => {
1278
+ expect((0, test_translator_1.expr) `count()`).toTranslate();
1279
+ });
1280
+ test('count distinct', () => {
1281
+ expect((0, test_translator_1.expr) `count(distinct astr)`).toTranslate();
1282
+ });
1283
+ test('join.count()', () => {
1284
+ expect((0, test_translator_1.expr) `b.count()`).toTranslate();
1285
+ });
1062
1286
  for (const f of ['sum', 'min', 'max', 'avg']) {
1063
1287
  const fOfT = `${f}(af)`;
1064
- test(fOfT, exprOK(fOfT));
1288
+ test(fOfT, () => {
1289
+ expect(new test_translator_1.BetaExpression(fOfT)).toTranslate();
1290
+ });
1065
1291
  if (f !== 'min' && f !== 'max') {
1066
1292
  const joinDot = `b.af.${f}()`;
1067
- test(joinDot, exprOK(joinDot));
1293
+ test(joinDot, () => {
1294
+ expect(new test_translator_1.BetaExpression(joinDot)).toTranslate();
1295
+ });
1068
1296
  const joinAgg = `b.${f}(af)`;
1069
- test(joinAgg, exprOK(joinAgg));
1297
+ test(joinAgg, () => {
1298
+ expect(new test_translator_1.BetaExpression(joinAgg)).toTranslate();
1299
+ });
1070
1300
  }
1071
1301
  }
1072
1302
  });
1073
1303
  describe('pick statements', () => {
1074
- test('full', exprOK(`
1304
+ test('full', () => {
1305
+ expect((0, test_translator_1.expr) `
1075
1306
  pick 'the answer' when ai = 42
1076
1307
  pick 'the questionable answer' when ai = 54
1077
1308
  else 'random'
1078
- `));
1079
- test('applied', exprOK(`
1309
+ `).toTranslate();
1310
+ });
1311
+ test('applied', () => {
1312
+ expect((0, test_translator_1.expr) `
1080
1313
  astr ?
1081
1314
  pick 'the answer' when = '42'
1082
1315
  pick 'the questionable answer' when = '54'
1083
1316
  else 'random'
1084
- `));
1085
- test('filtering', exprOK(`
1086
- astr ? pick 'missing value' when NULL
1087
- `));
1088
- test('null branch with else', exprType("astr ? pick null when = '42' else 3", 'number'));
1089
- test('null branch no else', exprType("astr ? pick null when = '42'", 'string'));
1090
- test('null branch no apply', exprType('pick null when 1 = 1 else 3', 'number'));
1091
- test('tiering', exprOK(`
1317
+ `).toTranslate();
1318
+ });
1319
+ test('filtering', () => {
1320
+ expect((0, test_translator_1.expr) `astr ? pick 'missing value' when NULL`).toTranslate();
1321
+ });
1322
+ test('null branch with else', () => {
1323
+ expect("astr ? pick null when = '42' else 3").toReturnType('number');
1324
+ });
1325
+ test('null branch no else', () => {
1326
+ expect("astr ? pick null when = '42'").toReturnType('string');
1327
+ });
1328
+ test('null branch no apply', () => {
1329
+ expect('pick null when 1 = 1 else 3').toReturnType('number');
1330
+ });
1331
+ test('tiering', () => {
1332
+ expect((0, test_translator_1.expr) `
1092
1333
  ai ?
1093
1334
  pick 1 when < 10
1094
1335
  pick 10 when < 100
1095
1336
  pick 100 when < 1000
1096
1337
  else 10000
1097
- `));
1098
- test('transforming', exprOK(`
1338
+ `).toTranslate();
1339
+ });
1340
+ test('transforming', () => {
1341
+ expect((0, test_translator_1.expr) `
1099
1342
  ai ?
1100
1343
  pick 'small' when < 10
1101
1344
  pick 'medium' when < 100
1102
1345
  else 'large'
1103
- `));
1104
- test('when single values', exprOK(`
1346
+ `).toTranslate();
1347
+ });
1348
+ test('when single values', () => {
1349
+ expect((0, test_translator_1.expr) `
1105
1350
  ai ?
1106
1351
  pick 'one' when 1
1107
1352
  else 'a lot'
1108
- `));
1353
+ `).toTranslate();
1354
+ });
1109
1355
  test('n-ary without else', () => {
1110
- expect((0, test_translator_1.markSource) `
1356
+ expect(`
1111
1357
  source: na is a + { dimension: d is
1112
1358
  pick 7 when true and true
1113
1359
  }
1114
- `).compileToFailWith("pick incomplete, missing 'else'", "Cannot define 'd', value has unknown type");
1360
+ `).translationToFailWith("pick incomplete, missing 'else'", "Cannot define 'd', value has unknown type");
1115
1361
  });
1116
1362
  test('n-ary with mismatch when clauses', () => {
1117
1363
  expect((0, test_translator_1.markSource) `
@@ -1120,7 +1366,7 @@ describe('expressions', () => {
1120
1366
  pick '7' when true or true
1121
1367
  else 7
1122
1368
  }
1123
- `).compileToFailWith("pick type 'string', expected 'number'", "Cannot define 'd', value has unknown type");
1369
+ `).translationToFailWith("pick type 'string', expected 'number'", "Cannot define 'd', value has unknown type");
1124
1370
  });
1125
1371
  test('n-ary with mismatched else clause', () => {
1126
1372
  expect((0, test_translator_1.markSource) `
@@ -1128,35 +1374,35 @@ describe('expressions', () => {
1128
1374
  pick 7 when true and true
1129
1375
  else '7'
1130
1376
  }
1131
- `).compileToFailWith("else type 'string', expected 'number'", "Cannot define 'd', value has unknown type");
1377
+ `).translationToFailWith("else type 'string', expected 'number'", "Cannot define 'd', value has unknown type");
1132
1378
  });
1133
1379
  test('applied else mismatch', () => {
1134
1380
  expect((0, test_translator_1.markSource) `
1135
1381
  source: na is a + { dimension: d is
1136
1382
  7 ? pick 7 when 7 else 'not seven'
1137
1383
  }
1138
- `).compileToFailWith("else type 'string', expected 'number'", "Cannot define 'd', value has unknown type");
1384
+ `).translationToFailWith("else type 'string', expected 'number'", "Cannot define 'd', value has unknown type");
1139
1385
  });
1140
1386
  test('applied default mismatch', () => {
1141
1387
  expect((0, test_translator_1.markSource) `
1142
1388
  source: na is a + { dimension: d is
1143
1389
  7 ? pick 'seven' when 7
1144
1390
  }
1145
- `).compileToFailWith("pick default type 'number', expected 'string'", "Cannot define 'd', value has unknown type");
1391
+ `).translationToFailWith("pick default type 'number', expected 'string'", "Cannot define 'd', value has unknown type");
1146
1392
  });
1147
1393
  test('applied when mismatch', () => {
1148
1394
  expect((0, test_translator_1.markSource) `
1149
1395
  source: na is a + { dimension: d is
1150
1396
  7 ? pick 'seven' when 7 pick 6 when 6
1151
1397
  }
1152
- `).compileToFailWith("pick type 'number', expected 'string'", "Cannot define 'd', value has unknown type");
1398
+ `).translationToFailWith("pick type 'number', expected 'string'", "Cannot define 'd', value has unknown type");
1153
1399
  });
1154
1400
  });
1155
1401
  test('paren and applied div', () => {
1156
1402
  var _a, _b;
1157
1403
  const modelSrc = 'query: z is a -> { group_by: x is 1+(3/4) }';
1158
1404
  const m = new test_translator_1.TestTranslator(modelSrc);
1159
- expect(m).modelCompiled();
1405
+ expect(m).toTranslate();
1160
1406
  const queryDef = (_b = (_a = m.translate()) === null || _a === void 0 ? void 0 : _a.translated) === null || _b === void 0 ? void 0 : _b.modelDef.contents['z'];
1161
1407
  expect(queryDef).toBeDefined();
1162
1408
  expect(queryDef === null || queryDef === void 0 ? void 0 : queryDef.type).toBe('query');
@@ -1211,39 +1457,39 @@ describe('expressions', () => {
1211
1457
  ['astr', 'string'],
1212
1458
  ['abool', 'boolean'],
1213
1459
  ])('Can compare field %s (type %s) to NULL', (name, _datatype) => {
1214
- expect(`${name} = NULL`).expressionCompiled();
1460
+ expect((0, test_translator_1.expr) `${name} = NULL`).toTranslate();
1215
1461
  });
1216
1462
  });
1217
1463
  describe('unspported fields in schema', () => {
1218
1464
  test('unsupported reference in result allowed', () => {
1219
1465
  const uModel = new test_translator_1.TestTranslator('query: a->{ group_by: aun }');
1220
- expect(uModel).modelCompiled();
1466
+ expect(uModel).toTranslate();
1221
1467
  });
1222
1468
  test('unsupported reference can be compared to NULL', () => {
1223
1469
  const uModel = new test_translator_1.TestTranslator('query: a->{ where: aun != NULL; project: * }');
1224
- expect(uModel).modelCompiled();
1470
+ expect(uModel).toTranslate();
1225
1471
  });
1226
1472
  test('flag unsupported equality', () => {
1227
1473
  // because we don't know if the two unsupported types are comparable
1228
1474
  const uModel = new test_translator_1.TestTranslator('query: ab->{ where: aun = b.aun project: * }');
1229
- expect(uModel).compileToFailWith('Unsupported type not allowed in expression');
1475
+ expect(uModel).translationToFailWith('Unsupported type not allowed in expression');
1230
1476
  });
1231
1477
  test('flag unsupported compare', () => {
1232
1478
  // because we don't know if the two unsupported types are comparable
1233
1479
  const uModel = new test_translator_1.TestTranslator('query: ab->{ where: aun > b.aun project: * }');
1234
- expect(uModel).compileToFailWith('Unsupported type not allowed in expression');
1480
+ expect(uModel).translationToFailWith('Unsupported type not allowed in expression');
1235
1481
  });
1236
1482
  test('allow unsupported equality when raw types match', () => {
1237
1483
  const uModel = new test_translator_1.TestTranslator('query: ab->{ where: aweird = b.aweird project: * }');
1238
- expect(uModel).modelCompiled();
1484
+ expect(uModel).toTranslate();
1239
1485
  });
1240
1486
  test('flag not applied to unsupported', () => {
1241
1487
  const uModel = new test_translator_1.TestTranslator('source: x is a { dimension: notUn is not aun }');
1242
- expect(uModel).compileToFailWith("'not' Can't use type unsupported");
1488
+ expect(uModel).translationToFailWith("'not' Can't use type unsupported");
1243
1489
  });
1244
1490
  test('allow unsupported to be cast', () => {
1245
1491
  const uModel = new test_translator_1.TestTranslator('source: x is a { dimension: notUn is aun::string }');
1246
- expect(uModel).modelCompiled();
1492
+ expect(uModel).toTranslate();
1247
1493
  });
1248
1494
  });
1249
1495
  describe('error handling', () => {
@@ -1252,7 +1498,7 @@ describe('error handling', () => {
1252
1498
  source: flights is table('malloytest.flights') {
1253
1499
  query: carrier is { group_by: carrier }
1254
1500
  }
1255
- `).compileToFailWith("Cannot redefine 'carrier'");
1501
+ `).translationToFailWith("Cannot redefine 'carrier'");
1256
1502
  });
1257
1503
  test('redefine source', () => {
1258
1504
  expect((0, test_translator_1.markSource) `
@@ -1262,55 +1508,70 @@ describe('error handling', () => {
1262
1508
  source: airports is table('malloytest.airports') + {
1263
1509
  primary_key: code
1264
1510
  }
1265
- `).compileToFailWith("Cannot redefine 'airports'");
1511
+ `).translationToFailWith("Cannot redefine 'airports'");
1266
1512
  });
1267
1513
  test('query from undefined source', () => {
1268
- expect((0, test_translator_1.markSource) `query: ${'x'}->{ project: y }`).compileToFailWith("Undefined source 'x'");
1514
+ expect((0, test_translator_1.markSource) `query: ${'x'}->{ project: y }`).translationToFailWith("Undefined source 'x'");
1269
1515
  });
1270
1516
  test('query with expression from undefined source', () => {
1271
1517
  // Regression check: Once upon a time this died with an exception even
1272
1518
  // when "query: x->{ group_by: y}" (above) generated the correct error.
1273
- expect((0, test_translator_1.markSource) `query: ${'x'}->{ project: y is z / 2 }`).compileToFailWith("Undefined source 'x'");
1519
+ expect((0, test_translator_1.markSource) `query: ${'x'}->{ project: y is z / 2 }`).translationToFailWith("Undefined source 'x'");
1274
1520
  });
1275
1521
  test('join reference before definition', () => {
1276
1522
  expect((0, test_translator_1.markSource) `
1277
1523
  source: newAB is a { join_one: newB is ${'bb'} on astring }
1278
1524
  source: newB is b
1279
- `).compileToFailWith("Undefined source 'bb'");
1280
- });
1281
- test('non-rename rename', badModel('source: na is a { rename: astr is astr }', "Can't rename field to itself"));
1282
- test('reference to field in its definition', badModel('source: na is a { dimension: ustr is UPPER(ustr) } ', "Circular reference to 'ustr' in definition"));
1283
- test('empty model', modelOK(''));
1284
- test('one line model ', modelOK('\n'));
1285
- test('query without fields', badModel('query: a -> { top: 5 }', "Can't determine query type (group_by/aggregate/nest,project,index)"));
1286
- test("refine can't change query type", badModel('query: ab -> aturtle { project: astr }', 'project: not legal in grouping query'));
1287
- test('undefined field ref in query', badModel('query: ab -> { aggregate: xyzzy }', "'xyzzy' is not defined"));
1525
+ `).translationToFailWith("Undefined source 'bb'");
1526
+ });
1527
+ test('non-rename rename', () => {
1528
+ expect('source: na is a { rename: astr is astr }').translationToFailWith("Can't rename field to itself");
1529
+ });
1530
+ test('reference to field in its definition', () => {
1531
+ expect('source: na is a { dimension: ustr is UPPER(ustr) } ').translationToFailWith("Circular reference to 'ustr' in definition");
1532
+ });
1533
+ test('empty model', () => {
1534
+ expect('').toTranslate();
1535
+ });
1536
+ test('one line model ', () => {
1537
+ expect('\n').toTranslate();
1538
+ });
1539
+ test('query without fields', () => {
1540
+ expect('query: a -> { top: 5 }').translationToFailWith("Can't determine query type (group_by/aggregate/nest,project,index)");
1541
+ });
1542
+ test("refine can't change query type", () => {
1543
+ expect('query: ab -> aturtle { project: astr }').translationToFailWith('project: not legal in grouping query');
1544
+ });
1545
+ test('undefined field ref in query', () => {
1546
+ expect('query: ab -> { aggregate: xyzzy }').translationToFailWith("'xyzzy' is not defined");
1547
+ });
1288
1548
  test('query on source with errors', () => {
1289
1549
  expect((0, test_translator_1.markSource) `
1290
1550
  source: na is a { join_one: ${'n'} on astr }
1291
- `).compileToFailWith("Undefined source 'n'");
1551
+ `).translationToFailWith("Undefined source 'n'");
1292
1552
  });
1293
1553
  test('detect duplicate output field names', () => {
1294
- expect((0, test_translator_1.markSource) `query: ab -> { group_by: astr, ${'astr'} }`).compileToFailWith("Output already has a field named 'astr'");
1554
+ expect((0, test_translator_1.markSource) `query: ab -> { group_by: astr, ${'astr'} }`).translationToFailWith("Output already has a field named 'astr'");
1295
1555
  });
1296
1556
  test('detect join tail overlap existing ref', () => {
1297
- expect((0, test_translator_1.markSource) `query: ab -> { group_by: astr, ${'b.astr'} }`).compileToFailWith("Output already has a field named 'astr'");
1557
+ expect((0, test_translator_1.markSource) `query: ab -> { group_by: astr, ${'b.astr'} }`).translationToFailWith("Output already has a field named 'astr'");
1298
1558
  });
1299
1559
  test('undefined in expression with regex compare', () => {
1300
1560
  expect(`
1301
1561
  source: c is a {
1302
1562
  dimension: d is meaning_of_life ~ r'(forty two|fifty four)'
1303
1563
  }
1304
- `).compileToFailWith("'meaning_of_life' is not defined");
1564
+ `).translationToFailWith("'meaning_of_life' is not defined");
1305
1565
  });
1306
1566
  test('detect output collision on join references', () => {
1307
1567
  expect(`
1308
1568
  query: ab -> {
1309
1569
  group_by: astr, b.astr
1310
1570
  }
1311
- `).compileToFailWith("Output already has a field named 'astr'");
1571
+ `).translationToFailWith("Output already has a field named 'astr'");
1312
1572
  });
1313
- test('rejoin a query is renamed', modelOK(`
1573
+ test('rejoin a query is renamed', () => {
1574
+ expect(`
1314
1575
  source: querySrc is from(
1315
1576
  table('malloytest.flights')->{
1316
1577
  group_by: origin
@@ -1324,13 +1585,14 @@ describe('error handling', () => {
1324
1585
  group_by: rejoin.nested.destination
1325
1586
  }
1326
1587
  }
1327
- `));
1588
+ `).toTranslate();
1589
+ });
1328
1590
  test('popping out of embedding when not embedded', () => {
1329
- expect('}%').compileToFailWith(/extraneous input '}%' expecting/);
1591
+ expect('}%').translationToFailWith(/extraneous input '}%' expecting/);
1330
1592
  });
1331
1593
  test('bad sql in sql block', () => {
1332
1594
  const badModel = new test_translator_1.TestTranslator('sql: { select: """)""" }');
1333
- expect(badModel).modelParsed();
1595
+ expect(badModel).toParse();
1334
1596
  const needSchema = badModel.translate();
1335
1597
  expect(needSchema.compileSQL).toBeDefined();
1336
1598
  if (needSchema.compileSQL) {
@@ -1342,7 +1604,7 @@ describe('error handling', () => {
1342
1604
  },
1343
1605
  });
1344
1606
  }
1345
- expect(badModel).compileToFailWith('Invalid SQL, ZZZZ');
1607
+ expect(badModel).translationToFailWith('Invalid SQL, ZZZZ');
1346
1608
  });
1347
1609
  });
1348
1610
  function getSelectOneStruct(sqlBlock) {
@@ -1371,19 +1633,19 @@ describe('source locations', () => {
1371
1633
  test('renamed source location', () => {
1372
1634
  const source = (0, test_translator_1.markSource) `source: ${'na is a'}`;
1373
1635
  const m = new test_translator_1.TestTranslator(source.code);
1374
- expect(m).modelParsed();
1636
+ expect(m).toParse();
1375
1637
  expect((0, test_translator_1.getExplore)(m.modelDef, 'na').location).toMatchObject(source.locations[0]);
1376
1638
  });
1377
1639
  test('refined source location', () => {
1378
1640
  const source = (0, test_translator_1.markSource) `source: ${'na is a {}'}`;
1379
1641
  const m = new test_translator_1.TestTranslator(source.code);
1380
- expect(m).modelParsed();
1642
+ expect(m).toParse();
1381
1643
  expect((0, test_translator_1.getExplore)(m.modelDef, 'na').location).toMatchObject(source.locations[0]);
1382
1644
  });
1383
1645
  test('location of defined dimension', () => {
1384
1646
  const source = (0, test_translator_1.markSource) `source: na is a { dimension: ${'x is 1'} }`;
1385
1647
  const m = new test_translator_1.TestTranslator(source.code);
1386
- expect(m).modelCompiled();
1648
+ expect(m).toTranslate();
1387
1649
  const na = (0, test_translator_1.getExplore)(m.modelDef, 'na');
1388
1650
  const x = (0, test_translator_1.getField)(na, 'x');
1389
1651
  expect(x.location).toMatchObject(source.locations[0]);
@@ -1391,7 +1653,7 @@ describe('source locations', () => {
1391
1653
  test('location of defined measure', () => {
1392
1654
  const source = (0, test_translator_1.markSource) `source: na is a { measure: ${'x is count()'} }`;
1393
1655
  const m = new test_translator_1.TestTranslator(source.code);
1394
- expect(m).modelCompiled();
1656
+ expect(m).toTranslate();
1395
1657
  const na = (0, test_translator_1.getExplore)(m.modelDef, 'na');
1396
1658
  const x = (0, test_translator_1.getField)(na, 'x');
1397
1659
  expect(x.location).toMatchObject(source.locations[0]);
@@ -1399,7 +1661,7 @@ describe('source locations', () => {
1399
1661
  test('location of defined query', () => {
1400
1662
  const source = (0, test_translator_1.markSource) `source: na is a { query: ${'x is { group_by: y is 1 }'} }`;
1401
1663
  const m = new test_translator_1.TestTranslator(source.code);
1402
- expect(m).modelCompiled();
1664
+ expect(m).toTranslate();
1403
1665
  const na = (0, test_translator_1.getExplore)(m.modelDef, 'na');
1404
1666
  const x = (0, test_translator_1.getField)(na, 'x');
1405
1667
  expect(x.location).toMatchObject(source.locations[0]);
@@ -1412,7 +1674,7 @@ describe('source locations', () => {
1412
1674
  }
1413
1675
  }`;
1414
1676
  const m = new test_translator_1.TestTranslator(source.code);
1415
- expect(m).modelCompiled();
1677
+ expect(m).toTranslate();
1416
1678
  const na = (0, test_translator_1.getExplore)(m.modelDef, 'na');
1417
1679
  const x = (0, test_translator_1.getQueryField)(na, 'x');
1418
1680
  const y = (0, test_translator_1.getField)(x.pipeline[0], 'y');
@@ -1427,7 +1689,7 @@ describe('source locations', () => {
1427
1689
  }
1428
1690
  }`;
1429
1691
  const m = new test_translator_1.TestTranslator(source.code);
1430
- expect(m).modelCompiled();
1692
+ expect(m).toTranslate();
1431
1693
  const na = (0, test_translator_1.getExplore)(m.modelDef, 'na');
1432
1694
  const x = (0, test_translator_1.getQueryField)(na, 'x');
1433
1695
  const z = (0, test_translator_1.getField)(x.pipeline[0], 'z');
@@ -1436,7 +1698,7 @@ describe('source locations', () => {
1436
1698
  test('location of field inherited from table', () => {
1437
1699
  const source = (0, test_translator_1.markSource) `source: na is ${"table('aTable')"}`;
1438
1700
  const m = new test_translator_1.TestTranslator(source.code);
1439
- expect(m).modelCompiled();
1701
+ expect(m).toTranslate();
1440
1702
  const na = (0, test_translator_1.getExplore)(m.modelDef, 'na');
1441
1703
  const abool = (0, test_translator_1.getField)(na, 'abool');
1442
1704
  expect(abool.location).toMatchObject(source.locations[0]);
@@ -1447,14 +1709,14 @@ describe('source locations', () => {
1447
1709
  source: na is from_sql(s)
1448
1710
  `;
1449
1711
  const m = new test_translator_1.TestTranslator(source.code);
1450
- expect(m).modelParsed();
1712
+ expect(m).toParse();
1451
1713
  const compileSql = m.translate().compileSQL;
1452
1714
  expect(compileSql).toBeDefined();
1453
1715
  if (compileSql) {
1454
1716
  m.update({
1455
1717
  compileSQL: { [compileSql.name]: getSelectOneStruct(compileSql) },
1456
1718
  });
1457
- expect(m).modelCompiled();
1719
+ expect(m).toTranslate();
1458
1720
  const na = (0, test_translator_1.getExplore)(m.modelDef, 'na');
1459
1721
  const one = (0, test_translator_1.getField)(na, 'one');
1460
1722
  expect(one.location).isLocationIn(source.locations[0], source.code);
@@ -1471,7 +1733,7 @@ describe('source locations', () => {
1471
1733
  )
1472
1734
  `;
1473
1735
  const m = new test_translator_1.TestTranslator(source.code);
1474
- expect(m).modelCompiled();
1736
+ expect(m).toTranslate();
1475
1737
  const na = (0, test_translator_1.getExplore)(m.modelDef, 'na');
1476
1738
  const abool = (0, test_translator_1.getField)(na, 'abool');
1477
1739
  expect(abool.location).toMatchObject(source.locations[0]);
@@ -1481,14 +1743,14 @@ describe('source locations', () => {
1481
1743
  test('location of named query', () => {
1482
1744
  const source = (0, test_translator_1.markSource) `query: ${'q is a -> { project: * }'}`;
1483
1745
  const m = new test_translator_1.TestTranslator(source.code);
1484
- expect(m).modelCompiled();
1746
+ expect(m).toTranslate();
1485
1747
  const q = (0, test_translator_1.getExplore)(m.modelDef, 'q');
1486
1748
  expect(q.location).toMatchObject(source.locations[0]);
1487
1749
  });
1488
1750
  test('location of field in named query', () => {
1489
1751
  const source = (0, test_translator_1.markSource) `query: q is a -> { group_by: ${'b is 1'} }`;
1490
1752
  const m = new test_translator_1.TestTranslator(source.code);
1491
- expect(m).modelCompiled();
1753
+ expect(m).toTranslate();
1492
1754
  const q = (0, test_translator_1.getModelQuery)(m.modelDef, 'q');
1493
1755
  const a = (0, test_translator_1.getField)(q.pipeline[0], 'b');
1494
1756
  expect(a.location).toMatchObject(source.locations[0]);
@@ -1496,14 +1758,14 @@ describe('source locations', () => {
1496
1758
  test('location of named SQL block', () => {
1497
1759
  const source = (0, test_translator_1.markSource) `${'sql: s is { select: """SELECT 1 as one""" }'}`;
1498
1760
  const m = new test_translator_1.TestTranslator(source.code);
1499
- expect(m).modelParsed();
1761
+ expect(m).toParse();
1500
1762
  const compileSql = m.translate().compileSQL;
1501
1763
  expect(compileSql).toBeDefined();
1502
1764
  if (compileSql) {
1503
1765
  m.update({
1504
1766
  compileSQL: { [compileSql.name]: getSelectOneStruct(compileSql) },
1505
1767
  });
1506
- expect(m).modelCompiled();
1768
+ expect(m).toTranslate();
1507
1769
  const s = m.sqlBlocks[0];
1508
1770
  expect(s.location).isLocationIn(source.locations[0], source.code);
1509
1771
  }
@@ -1515,7 +1777,7 @@ describe('source locations', () => {
1515
1777
  }
1516
1778
  `;
1517
1779
  const m = new test_translator_1.TestTranslator(source.code);
1518
- expect(m).modelCompiled();
1780
+ expect(m).toTranslate();
1519
1781
  const na = (0, test_translator_1.getExplore)(m.modelDef, 'na');
1520
1782
  const bbool = (0, test_translator_1.getField)(na, 'bbool');
1521
1783
  expect(bbool.location).toMatchObject(source.locations[0]);
@@ -1527,7 +1789,7 @@ describe('source locations', () => {
1527
1789
  }
1528
1790
  `;
1529
1791
  const m = new test_translator_1.TestTranslator(source.code);
1530
- expect(m).modelCompiled();
1792
+ expect(m).toTranslate();
1531
1793
  const na = (0, test_translator_1.getExplore)(m.modelDef, 'na');
1532
1794
  const x = (0, test_translator_1.getField)(na, 'x');
1533
1795
  expect(x.location).toMatchObject(source.locations[0]);
@@ -1539,7 +1801,7 @@ describe('source locations', () => {
1539
1801
  }
1540
1802
  `;
1541
1803
  const m = new test_translator_1.TestTranslator(source.code);
1542
- expect(m).modelCompiled();
1804
+ expect(m).toTranslate();
1543
1805
  const na = (0, test_translator_1.getExplore)(m.modelDef, 'na');
1544
1806
  const x = (0, test_translator_1.getField)(na, 'x');
1545
1807
  expect(x.location).toMatchObject(source.locations[0]);
@@ -1554,7 +1816,7 @@ describe('source locations', () => {
1554
1816
  }
1555
1817
  `;
1556
1818
  const m = new test_translator_1.TestTranslator(source.code);
1557
- expect(m).modelCompiled();
1819
+ expect(m).toTranslate();
1558
1820
  const na = (0, test_translator_1.getExplore)(m.modelDef, 'na');
1559
1821
  const x = (0, test_translator_1.getJoinField)(na, 'x');
1560
1822
  const y = (0, test_translator_1.getField)(x, 'y');
@@ -1564,15 +1826,25 @@ describe('source locations', () => {
1564
1826
  // test("multi line sql block token span is correct", () => {
1565
1827
  // const sqlSource = `sql: { select: """// line 0\n//line 1\n// line 2""" }`;
1566
1828
  // const m = new TestTranslator(sqlSource);
1567
- // expect(m).not.modelParsed();
1829
+ // expect(m).not.toParse();
1568
1830
  // const errList = m.errors().errors;
1569
1831
  // expect(errList[0].at?.range.end).toEqual({ line: 2, character: 11 });
1570
1832
  // });
1571
- test('undefined query location', badModel((0, test_translator_1.markSource) `query: ${'-> xyz'}`, "Reference to undefined query 'xyz'"));
1572
- test('undefined field reference', badModel((0, test_translator_1.markSource) `query: a -> { group_by: ${'xyz'} }`, "'xyz' is not defined"));
1573
- test('bad query', badModel((0, test_translator_1.markSource) `query: a -> { group_by: astr; ${'project: *'} }`, 'project: not legal in grouping query'));
1574
- test.skip('undefined field reference in top', badModel((0, test_translator_1.markSource) `query: a -> { group_by: one is 1; top: 1 by ${'xyz'} }`, "'xyz' is not defined"));
1575
- test.skip('undefined field reference in order_by', badModel((0, test_translator_1.markSource) `query: a -> { group_by: one is 1; order_by: ${'xyz'} }`, "'xyz' is not defined"));
1833
+ test('undefined query location', () => {
1834
+ expect((0, test_translator_1.model) `query: ${'-> xyz'}`).translationToFailWith("Reference to undefined query 'xyz'");
1835
+ });
1836
+ test('undefined field reference', () => {
1837
+ expect((0, test_translator_1.model) `query: a -> { group_by: ${'xyz'} }`).translationToFailWith("'xyz' is not defined");
1838
+ });
1839
+ test('bad query', () => {
1840
+ expect((0, test_translator_1.model) `query: a -> { group_by: astr; ${'project: *'} }`).translationToFailWith('project: not legal in grouping query');
1841
+ });
1842
+ test.skip('undefined field reference in top', () => {
1843
+ expect((0, test_translator_1.model) `query: a -> { group_by: one is 1; top: 1 by ${'xyz'} }`).translationToFailWith("'xyz' is not defined");
1844
+ });
1845
+ test.skip('undefined field reference in order_by', () => {
1846
+ expect((0, test_translator_1.model) `query: a -> { group_by: one is 1; order_by: ${'xyz'} }`).translationToFailWith("'xyz' is not defined");
1847
+ });
1576
1848
  });
1577
1849
  describe('source references', () => {
1578
1850
  test('reference to explore', () => {
@@ -1581,7 +1853,7 @@ describe('source references', () => {
1581
1853
  query: ${'na'} -> { project: * }
1582
1854
  `;
1583
1855
  const m = new test_translator_1.TestTranslator(source.code);
1584
- expect(m).modelCompiled();
1856
+ expect(m).toTranslate();
1585
1857
  expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
1586
1858
  location: source.locations[1],
1587
1859
  type: 'exploreReference',
@@ -1599,7 +1871,7 @@ describe('source references', () => {
1599
1871
  query: t -> ${'q'}
1600
1872
  `;
1601
1873
  const m = new test_translator_1.TestTranslator(source.code);
1602
- expect(m).modelCompiled();
1874
+ expect(m).toTranslate();
1603
1875
  expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
1604
1876
  location: source.locations[1],
1605
1877
  type: 'fieldReference',
@@ -1615,7 +1887,7 @@ describe('source references', () => {
1615
1887
  query: na -> ${'x'}
1616
1888
  `;
1617
1889
  const m = new test_translator_1.TestTranslator(source.code);
1618
- expect(m).modelCompiled();
1890
+ expect(m).toTranslate();
1619
1891
  expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
1620
1892
  location: source.locations[1],
1621
1893
  type: 'fieldReference',
@@ -1631,14 +1903,14 @@ describe('source references', () => {
1631
1903
  source: na is from_sql(${'s'})
1632
1904
  `;
1633
1905
  const m = new test_translator_1.TestTranslator(source.code);
1634
- expect(m).modelParsed();
1906
+ expect(m).toParse();
1635
1907
  const compileSql = m.translate().compileSQL;
1636
1908
  expect(compileSql).toBeDefined();
1637
1909
  if (compileSql) {
1638
1910
  m.update({
1639
1911
  compileSQL: { [compileSql.name]: getSelectOneStruct(compileSql) },
1640
1912
  });
1641
- expect(m).modelCompiled();
1913
+ expect(m).toTranslate();
1642
1914
  const ref = m.referenceAt(pos(source.locations[1]));
1643
1915
  expect(ref).toMatchObject({
1644
1916
  location: source.locations[1],
@@ -1657,7 +1929,7 @@ describe('source references', () => {
1657
1929
  source: na is from(-> ${'q'})
1658
1930
  `;
1659
1931
  const m = new test_translator_1.TestTranslator(source.code);
1660
- expect(m).modelCompiled();
1932
+ expect(m).toTranslate();
1661
1933
  expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
1662
1934
  location: source.locations[1],
1663
1935
  type: 'queryReference',
@@ -1673,7 +1945,7 @@ describe('source references', () => {
1673
1945
  query: q2 is -> ${'q'} -> { project: * }
1674
1946
  `;
1675
1947
  const m = new test_translator_1.TestTranslator(source.code);
1676
- expect(m).modelCompiled();
1948
+ expect(m).toTranslate();
1677
1949
  expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
1678
1950
  location: source.locations[1],
1679
1951
  type: 'queryReference',
@@ -1689,7 +1961,7 @@ describe('source references', () => {
1689
1961
  query: q2 is -> ${'q'} { limit: 10 }
1690
1962
  `;
1691
1963
  const m = new test_translator_1.TestTranslator(source.code);
1692
- expect(m).modelCompiled();
1964
+ expect(m).toTranslate();
1693
1965
  expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
1694
1966
  location: source.locations[1],
1695
1967
  type: 'queryReference',
@@ -1705,7 +1977,7 @@ describe('source references', () => {
1705
1977
  query: na -> { project: bbool is not ${'abool'} }
1706
1978
  `;
1707
1979
  const m = new test_translator_1.TestTranslator(source.code);
1708
- expect(m).modelCompiled();
1980
+ expect(m).toTranslate();
1709
1981
  expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
1710
1982
  location: source.locations[1],
1711
1983
  type: 'fieldReference',
@@ -1723,7 +1995,7 @@ describe('source references', () => {
1723
1995
  query: na -> { project: ${'`name`'} }
1724
1996
  `;
1725
1997
  const m = new test_translator_1.TestTranslator(source.code);
1726
- expect(m).modelCompiled();
1998
+ expect(m).toTranslate();
1727
1999
  expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
1728
2000
  location: source.locations[1],
1729
2001
  type: 'fieldReference',
@@ -1742,7 +2014,7 @@ describe('source references', () => {
1742
2014
  query: na -> { project: bstr is self.${'astr'} }
1743
2015
  `;
1744
2016
  const m = new test_translator_1.TestTranslator(source.code);
1745
- expect(m).modelCompiled();
2017
+ expect(m).toTranslate();
1746
2018
  expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
1747
2019
  location: source.locations[1],
1748
2020
  type: 'fieldReference',
@@ -1760,7 +2032,7 @@ describe('source references', () => {
1760
2032
  query: na -> { project: bstr is ${'self'}.astr }
1761
2033
  `;
1762
2034
  const m = new test_translator_1.TestTranslator(source.code);
1763
- expect(m).modelCompiled();
2035
+ expect(m).toTranslate();
1764
2036
  expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
1765
2037
  location: source.locations[1],
1766
2038
  type: 'joinReference',
@@ -1775,7 +2047,7 @@ describe('source references', () => {
1775
2047
  query: ${"table('aTable')"} -> { group_by: ${'abool'} }
1776
2048
  `;
1777
2049
  const m = new test_translator_1.TestTranslator(source.code);
1778
- expect(m).modelCompiled();
2050
+ expect(m).toTranslate();
1779
2051
  expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
1780
2052
  location: source.locations[1],
1781
2053
  type: 'fieldReference',
@@ -1791,7 +2063,7 @@ describe('source references', () => {
1791
2063
  query: na -> { project: ${'abool'} }
1792
2064
  `;
1793
2065
  const m = new test_translator_1.TestTranslator(source.code);
1794
- expect(m).modelCompiled();
2066
+ expect(m).toTranslate();
1795
2067
  expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
1796
2068
  location: source.locations[1],
1797
2069
  type: 'fieldReference',
@@ -1809,7 +2081,7 @@ describe('source references', () => {
1809
2081
  }
1810
2082
  `;
1811
2083
  const m = new test_translator_1.TestTranslator(source.code);
1812
- expect(m).modelCompiled();
2084
+ expect(m).toTranslate();
1813
2085
  expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
1814
2086
  location: source.locations[1],
1815
2087
  type: 'fieldReference',
@@ -1827,7 +2099,7 @@ describe('source references', () => {
1827
2099
  }
1828
2100
  `;
1829
2101
  const m = new test_translator_1.TestTranslator(source.code);
1830
- expect(m).modelCompiled();
2102
+ expect(m).toTranslate();
1831
2103
  expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
1832
2104
  location: source.locations[1],
1833
2105
  type: 'fieldReference',
@@ -1845,7 +2117,7 @@ describe('source references', () => {
1845
2117
  }
1846
2118
  `;
1847
2119
  const m = new test_translator_1.TestTranslator(source.code);
1848
- expect(m).modelCompiled();
2120
+ expect(m).toTranslate();
1849
2121
  expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
1850
2122
  location: source.locations[1],
1851
2123
  type: 'fieldReference',
@@ -1863,7 +2135,7 @@ describe('source references', () => {
1863
2135
  }
1864
2136
  `;
1865
2137
  const m = new test_translator_1.TestTranslator(source.code);
1866
- expect(m).modelCompiled();
2138
+ expect(m).toTranslate();
1867
2139
  expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
1868
2140
  location: source.locations[1],
1869
2141
  type: 'fieldReference',
@@ -1881,7 +2153,7 @@ describe('source references', () => {
1881
2153
  }
1882
2154
  `;
1883
2155
  const m = new test_translator_1.TestTranslator(source.code);
1884
- expect(m).modelCompiled();
2156
+ expect(m).toTranslate();
1885
2157
  expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
1886
2158
  location: source.locations[1],
1887
2159
  type: 'fieldReference',
@@ -1899,7 +2171,7 @@ describe('source references', () => {
1899
2171
  }
1900
2172
  `;
1901
2173
  const m = new test_translator_1.TestTranslator(source.code);
1902
- expect(m).modelCompiled();
2174
+ expect(m).toTranslate();
1903
2175
  expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
1904
2176
  location: source.locations[1],
1905
2177
  type: 'fieldReference',
@@ -1917,7 +2189,7 @@ describe('source references', () => {
1917
2189
  }
1918
2190
  `;
1919
2191
  const m = new test_translator_1.TestTranslator(source.code);
1920
- expect(m).modelCompiled();
2192
+ expect(m).toTranslate();
1921
2193
  expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
1922
2194
  location: source.locations[1],
1923
2195
  type: 'fieldReference',
@@ -1933,7 +2205,7 @@ describe('source references', () => {
1933
2205
  query: na -> { aggregate: ai_sum is ${'ai'}.sum() }
1934
2206
  `;
1935
2207
  const m = new test_translator_1.TestTranslator(source.code);
1936
- expect(m).modelCompiled();
2208
+ expect(m).toTranslate();
1937
2209
  expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
1938
2210
  location: source.locations[1],
1939
2211
  type: 'fieldReference',
@@ -1954,7 +2226,7 @@ describe('source references', () => {
1954
2226
  query: na -> { aggregate: ai_sum is ${'self'}.sum(self.ai) }
1955
2227
  `;
1956
2228
  const m = new test_translator_1.TestTranslator(source.code);
1957
- expect(m).modelCompiled();
2229
+ expect(m).toTranslate();
1958
2230
  expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
1959
2231
  location: source.locations[1],
1960
2232
  type: 'joinReference',
@@ -1972,7 +2244,7 @@ describe('source references', () => {
1972
2244
  query: na -> { aggregate: ai_sum is self.sum(${'self'}.ai) }
1973
2245
  `;
1974
2246
  const m = new test_translator_1.TestTranslator(source.code);
1975
- expect(m).modelCompiled();
2247
+ expect(m).toTranslate();
1976
2248
  expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
1977
2249
  location: source.locations[1],
1978
2250
  type: 'joinReference',
@@ -1990,7 +2262,7 @@ describe('source references', () => {
1990
2262
  }
1991
2263
  `;
1992
2264
  const m = new test_translator_1.TestTranslator(source.code);
1993
- expect(m).modelCompiled();
2265
+ expect(m).toTranslate();
1994
2266
  expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
1995
2267
  location: source.locations[1],
1996
2268
  type: 'exploreReference',
@@ -2006,7 +2278,7 @@ describe('source references', () => {
2006
2278
  query: na -> { aggregate: ai_sum is sum(${'ai'}) }
2007
2279
  `;
2008
2280
  const m = new test_translator_1.TestTranslator(source.code);
2009
- expect(m).modelCompiled();
2281
+ expect(m).toTranslate();
2010
2282
  expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
2011
2283
  location: source.locations[1],
2012
2284
  type: 'fieldReference',
@@ -2023,7 +2295,7 @@ describe('source references', () => {
2023
2295
  }
2024
2296
  `;
2025
2297
  const m = new test_translator_1.TestTranslator(source.code);
2026
- expect(m).modelCompiled();
2298
+ expect(m).toTranslate();
2027
2299
  expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
2028
2300
  location: source.locations[1],
2029
2301
  type: 'fieldReference',
@@ -2041,7 +2313,7 @@ describe('source references', () => {
2041
2313
  }
2042
2314
  `;
2043
2315
  const m = new test_translator_1.TestTranslator(source.code);
2044
- expect(m).modelCompiled();
2316
+ expect(m).toTranslate();
2045
2317
  expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
2046
2318
  location: source.locations[1],
2047
2319
  type: 'fieldReference',
@@ -2054,15 +2326,13 @@ describe('source references', () => {
2054
2326
  });
2055
2327
  describe('translation need error locations', () => {
2056
2328
  test('import error location', () => {
2057
- const source = (0, test_translator_1.markSource) `import ${'"badfile"'}`;
2058
- const m = new test_translator_1.TestTranslator(source.code);
2329
+ const source = (0, test_translator_1.model) `import ${'"badfile"'}`;
2330
+ const m = source.translator;
2059
2331
  const result = m.translate();
2060
2332
  m.update({
2061
2333
  errors: { urls: { [(result.urls || [])[0]]: 'Bad file!' } },
2062
2334
  });
2063
- expect(m).not.modelParsed();
2064
- const errList = m.errors().errors;
2065
- expect(errList[0].at).isLocationIn(source.locations[0], source.code);
2335
+ expect(source).translationToFailWith(/Bad file!/);
2066
2336
  return undefined;
2067
2337
  });
2068
2338
  test('sql struct error location', () => {
@@ -2071,35 +2341,33 @@ describe('translation need error locations', () => {
2071
2341
  query: from_sql(bad_sql) -> { project: * }
2072
2342
  `;
2073
2343
  const m = new test_translator_1.TestTranslator(source.code);
2074
- expect(m).modelParsed();
2344
+ expect(m).toParse();
2075
2345
  const req = m.translate().compileSQL;
2076
2346
  expect(req).toBeDefined();
2077
2347
  if (req) {
2078
2348
  m.update({ errors: { compileSQL: { [req.name]: 'Bad SQL!' } } });
2079
2349
  }
2080
- expect(m).not.modelCompiled();
2350
+ expect(m).not.toTranslate();
2081
2351
  const errList = m.errors().errors;
2082
2352
  expect(errList[0].at).isLocationIn(source.locations[0], source.code);
2083
2353
  });
2084
2354
  test('table struct error location', () => {
2085
- const source = (0, test_translator_1.markSource) `
2355
+ const source = (0, test_translator_1.model) `
2086
2356
  source: bad_explore is ${"table('malloy-data.bad.table')"}
2087
2357
  `;
2088
- const m = new test_translator_1.TestTranslator(source.code);
2358
+ const m = source.translator;
2089
2359
  const result = m.translate();
2090
2360
  m.update({
2091
2361
  errors: {
2092
2362
  tables: { [(result.tables || [])[0]]: 'Bad table!' },
2093
2363
  },
2094
2364
  });
2095
- expect(m).not.modelParsed();
2096
- const errList = m.errors().errors;
2097
- expect(errList[0].at).isLocationIn(source.locations[0], source.code);
2098
- return undefined;
2365
+ expect(m).translationToFailWith(/Bad table!/);
2099
2366
  });
2100
2367
  });
2101
2368
  describe('pipeline comprehension', () => {
2102
- test('second query gets namespace from first', modelOK(`
2369
+ test('second query gets namespace from first', () => {
2370
+ expect(`
2103
2371
  source: aq is a {
2104
2372
  query: t1 is {
2105
2373
  group_by: t1int is ai, t1str is astr
@@ -2107,8 +2375,10 @@ describe('pipeline comprehension', () => {
2107
2375
  project: t1str, t1int
2108
2376
  }
2109
2377
  }
2110
- `));
2111
- test("second query doesn't have access to original fields", badModel((0, test_translator_1.markSource) `
2378
+ `).toTranslate();
2379
+ });
2380
+ test("second query doesn't have access to original fields", () => {
2381
+ expect((0, test_translator_1.model) `
2112
2382
  source: aq is a {
2113
2383
  query: t1 is {
2114
2384
  group_by: t1int is ai, t1str is astr
@@ -2116,8 +2386,10 @@ describe('pipeline comprehension', () => {
2116
2386
  project: ${'ai'}
2117
2387
  }
2118
2388
  }
2119
- `, "'ai' is not defined"));
2120
- test('new query can append ops to existing query', modelOK(`
2389
+ `).translationToFailWith("'ai' is not defined");
2390
+ });
2391
+ test('new query can append ops to existing query', () => {
2392
+ expect(`
2121
2393
  source: aq is a {
2122
2394
  query: t0 is {
2123
2395
  group_by: t1int is ai, t1str is astr
@@ -2126,8 +2398,10 @@ describe('pipeline comprehension', () => {
2126
2398
  project: t1str, t1int
2127
2399
  }
2128
2400
  }
2129
- `));
2130
- test('new query can refine and append to exisiting query', modelOK(`
2401
+ `).toTranslate();
2402
+ });
2403
+ test('new query can refine and append to exisiting query', () => {
2404
+ expect(`
2131
2405
  source: aq is table('aTable') {
2132
2406
  query: by_region is { group_by: astr }
2133
2407
  query: by_region2 is by_region {
@@ -2136,20 +2410,25 @@ describe('pipeline comprehension', () => {
2136
2410
  project: astr, dateNest.ad
2137
2411
  }
2138
2412
  }
2139
- `));
2140
- test('reference to a query can include a refinement', modelOK(`
2413
+ `).toTranslate();
2414
+ });
2415
+ test('reference to a query can include a refinement', () => {
2416
+ expect(`
2141
2417
  query: ab -> {
2142
2418
  group_by: ai
2143
2419
  nest: aturtle { limit: 1 }
2144
2420
  }
2145
- `));
2146
- test('Querying an sourcebased on a query', modelOK(`
2421
+ `).toTranslate();
2422
+ });
2423
+ test('Querying an sourcebased on a query', () => {
2424
+ expect(`
2147
2425
  query: q is a -> { group_by: astr; aggregate: strsum is ai.sum() }
2148
2426
  source: aq is a {
2149
2427
  join_one: aq is from(->q) on astr = aq.astr
2150
2428
  }
2151
2429
  query: aqf is aq -> { project: * }
2152
- `));
2430
+ `).toTranslate();
2431
+ });
2153
2432
  test('new query appends to existing query', () => {
2154
2433
  const src = `
2155
2434
  query: s1 is table('malloytest.flights') -> {
@@ -2160,7 +2439,7 @@ describe('pipeline comprehension', () => {
2160
2439
  }
2161
2440
  `;
2162
2441
  const m = new test_translator_1.TestTranslator(src);
2163
- expect(m).modelCompiled();
2442
+ expect(m).toTranslate();
2164
2443
  const s2 = m.getQuery('s2');
2165
2444
  expect(s2 === null || s2 === void 0 ? void 0 : s2.pipeline.length).toBe(2);
2166
2445
  });