@malloydata/malloy 0.0.190 → 0.0.191-dev240925232958
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lang/ast/expressions/constant-expression.js +4 -1
- package/dist/lang/ast/expressions/expr-aggregate-function.js +25 -14
- package/dist/lang/ast/expressions/expr-alternation-tree.js +1 -3
- package/dist/lang/ast/expressions/expr-cast.js +1 -1
- package/dist/lang/ast/expressions/expr-coalesce.js +1 -1
- package/dist/lang/ast/expressions/expr-func.js +20 -31
- package/dist/lang/ast/expressions/expr-granular-time.js +1 -1
- package/dist/lang/ast/expressions/expr-id-reference.js +1 -3
- package/dist/lang/ast/expressions/expr-props.js +8 -8
- package/dist/lang/ast/expressions/expr-time-extract.js +7 -13
- package/dist/lang/ast/expressions/expr-ungroup.js +4 -8
- package/dist/lang/ast/expressions/for-range.js +2 -3
- package/dist/lang/ast/expressions/function-ordering.js +6 -6
- package/dist/lang/ast/expressions/partial-compare.js +1 -3
- package/dist/lang/ast/expressions/pick-when.js +30 -20
- package/dist/lang/ast/expressions/range.js +1 -1
- package/dist/lang/ast/expressions/top-by.js +1 -1
- package/dist/lang/ast/field-space/dynamic-space.js +1 -1
- package/dist/lang/ast/field-space/index-field-space.js +6 -6
- package/dist/lang/ast/field-space/parameter-space.js +14 -3
- package/dist/lang/ast/field-space/project-field-space.js +1 -1
- package/dist/lang/ast/field-space/query-spaces.d.ts +2 -1
- package/dist/lang/ast/field-space/query-spaces.js +7 -6
- package/dist/lang/ast/field-space/reference-field.js +1 -1
- package/dist/lang/ast/field-space/refined-space.js +3 -3
- package/dist/lang/ast/field-space/static-space.js +11 -2
- package/dist/lang/ast/parameters/has-parameter.js +4 -4
- package/dist/lang/ast/query-builders/index-builder.js +4 -4
- package/dist/lang/ast/query-builders/project-builder.js +2 -2
- package/dist/lang/ast/query-builders/reduce-builder.js +5 -5
- package/dist/lang/ast/query-elements/anonymous-query.js +1 -1
- package/dist/lang/ast/query-elements/query-reference.js +2 -2
- package/dist/lang/ast/query-items/field-declaration.js +7 -4
- package/dist/lang/ast/query-items/field-references.js +1 -1
- package/dist/lang/ast/query-items/typecheck_utils.js +31 -11
- package/dist/lang/ast/query-properties/filters.js +4 -4
- package/dist/lang/ast/query-properties/ordering.js +3 -3
- package/dist/lang/ast/query-properties/qop-desc.js +2 -2
- package/dist/lang/ast/query-properties/top.js +5 -5
- package/dist/lang/ast/query-utils.js +1 -1
- package/dist/lang/ast/source-elements/named-source.d.ts +2 -2
- package/dist/lang/ast/source-elements/named-source.js +12 -13
- package/dist/lang/ast/source-elements/refined-source.js +9 -7
- package/dist/lang/ast/source-elements/sql-source.js +5 -5
- package/dist/lang/ast/source-elements/table-source.js +2 -2
- package/dist/lang/ast/source-properties/joins.js +6 -6
- package/dist/lang/ast/source-properties/renames.js +3 -3
- package/dist/lang/ast/source-query-elements/source-query-element.d.ts +2 -1
- package/dist/lang/ast/source-query-elements/source-query-element.js +3 -2
- package/dist/lang/ast/source-query-elements/sq-arrow.js +2 -2
- package/dist/lang/ast/source-query-elements/sq-extend.js +1 -1
- package/dist/lang/ast/source-query-elements/sq-reference.js +5 -5
- package/dist/lang/ast/source-query-elements/sq-refine.js +2 -2
- package/dist/lang/ast/source-query-elements/sq-source.js +1 -1
- package/dist/lang/ast/sql-elements/sql-string.js +2 -2
- package/dist/lang/ast/statements/define-query.js +2 -2
- package/dist/lang/ast/statements/define-source.js +3 -3
- package/dist/lang/ast/statements/import-statement.js +7 -7
- package/dist/lang/ast/struct-utils.js +1 -1
- package/dist/lang/ast/types/expression-def.js +21 -22
- package/dist/lang/ast/types/lookup-result.d.ts +5 -1
- package/dist/lang/ast/types/malloy-element.d.ts +8 -5
- package/dist/lang/ast/types/malloy-element.js +33 -27
- package/dist/lang/ast/view-elements/qop-desc-view.js +3 -3
- package/dist/lang/ast/view-elements/reference-view.js +6 -6
- package/dist/lang/ast/view-elements/refine-utils.js +6 -6
- package/dist/lang/ast/view-elements/view-arrow.js +1 -1
- package/dist/lang/ast/view-elements/view-refine.js +1 -1
- package/dist/lang/malloy-to-ast.d.ts +7 -7
- package/dist/lang/malloy-to-ast.js +56 -55
- package/dist/lang/parse-log.d.ts +323 -6
- package/dist/lang/parse-log.js +62 -16
- package/dist/lang/parse-malloy.d.ts +5 -3
- package/dist/lang/parse-malloy.js +22 -31
- package/dist/lang/test/annotation.spec.js +4 -4
- package/dist/lang/test/expressions.spec.js +93 -74
- package/dist/lang/test/imports.spec.js +5 -5
- package/dist/lang/test/lenses.spec.js +18 -18
- package/dist/lang/test/literals.spec.js +1 -1
- package/dist/lang/test/locations.spec.js +5 -5
- package/dist/lang/test/parameters.spec.js +34 -34
- package/dist/lang/test/parse-expects.d.ts +10 -28
- package/dist/lang/test/parse-expects.js +38 -33
- package/dist/lang/test/parse.spec.js +49 -49
- package/dist/lang/test/query.spec.js +82 -82
- package/dist/lang/test/source.spec.js +6 -6
- package/dist/lang/test/test-translator.d.ts +19 -0
- package/dist/lang/test/test-translator.js +20 -1
- package/dist/tags.js +4 -2
- package/package.json +1 -1
|
@@ -73,7 +73,7 @@ describe('lenses', () => {
|
|
|
73
73
|
view: d is { group_by: n }
|
|
74
74
|
}
|
|
75
75
|
run: x -> d + d
|
|
76
|
-
`).
|
|
76
|
+
`).toLog((0, test_translator_1.errorMessage)('overlapping fields in refinement: n'));
|
|
77
77
|
});
|
|
78
78
|
test('cannot override limit', () => {
|
|
79
79
|
expect((0, test_translator_1.markSource) `
|
|
@@ -82,7 +82,7 @@ describe('lenses', () => {
|
|
|
82
82
|
view: d2 is { group_by: n2 is 2; limit: 20 }
|
|
83
83
|
}
|
|
84
84
|
run: x -> d1 + d2
|
|
85
|
-
`).
|
|
85
|
+
`).toLog((0, test_translator_1.errorMessage)('refinement cannot override existing limit'));
|
|
86
86
|
});
|
|
87
87
|
test('cannot override ordering', () => {
|
|
88
88
|
expect((0, test_translator_1.markSource) `
|
|
@@ -91,7 +91,7 @@ describe('lenses', () => {
|
|
|
91
91
|
view: d2 is { group_by: n2 is 1; order_by: n2 }
|
|
92
92
|
}
|
|
93
93
|
run: x -> d1 + d2
|
|
94
|
-
`).
|
|
94
|
+
`).toLog((0, test_translator_1.errorMessage)('refinement cannot override existing ordering'));
|
|
95
95
|
});
|
|
96
96
|
test('weird issue with order by constant group by', () => {
|
|
97
97
|
expect((0, test_translator_1.markSource) `
|
|
@@ -122,7 +122,7 @@ describe('lenses', () => {
|
|
|
122
122
|
run: x -> proj + grp
|
|
123
123
|
run: x -> idx + grp
|
|
124
124
|
run: x -> idx + proj
|
|
125
|
-
`).
|
|
125
|
+
`).toLog((0, test_translator_1.errorMessage)('cannot refine reduce view with project view'), (0, test_translator_1.errorMessage)('cannot refine reduce view with index view'), (0, test_translator_1.errorMessage)('cannot refine project view with index view'), (0, test_translator_1.errorMessage)('cannot refine project view with reduce view'), (0, test_translator_1.errorMessage)('cannot refine index view with reduce view'), (0, test_translator_1.errorMessage)('cannot refine index view with project view'));
|
|
126
126
|
});
|
|
127
127
|
test('can reference dimension at head of query when experiment is enabled', () => {
|
|
128
128
|
expect((0, test_translator_1.markSource) `
|
|
@@ -158,7 +158,7 @@ describe('lenses', () => {
|
|
|
158
158
|
view: two_stage is { group_by: a is 1 } -> { group_by: a }
|
|
159
159
|
}
|
|
160
160
|
run: x -> one_stage + two_stage
|
|
161
|
-
`).
|
|
161
|
+
`).toLog((0, test_translator_1.errorMessage)('named refinement `two_stage` must have exactly one stage'));
|
|
162
162
|
});
|
|
163
163
|
test('cannot refine with literal multi-stage', () => {
|
|
164
164
|
expect((0, test_translator_1.markSource) `
|
|
@@ -167,7 +167,7 @@ describe('lenses', () => {
|
|
|
167
167
|
view: two_stage is { group_by: a is 1 } -> { group_by: a }
|
|
168
168
|
}
|
|
169
169
|
run: x -> one_stage + ({ group_by: a is 1 } -> { group_by: a })
|
|
170
|
-
`).
|
|
170
|
+
`).toLog((0, test_translator_1.errorMessage)('A multi-segment view cannot be used as a refinement'));
|
|
171
171
|
});
|
|
172
172
|
test('can reference dimension in refinement when experiment is enabled', () => {
|
|
173
173
|
expect((0, test_translator_1.markSource) `
|
|
@@ -217,7 +217,7 @@ describe('lenses', () => {
|
|
|
217
217
|
join_one: y is a on true
|
|
218
218
|
view: m is { aggregate: c is count() }
|
|
219
219
|
}
|
|
220
|
-
run: x -> m + y + { limit: 1 }`).
|
|
220
|
+
run: x -> m + y + { limit: 1 }`).toLog((0, test_translator_1.errorMessage)('named refinement `y` must be a view, found a struct'));
|
|
221
221
|
});
|
|
222
222
|
test('cannot use view from join as whole pipeline', () => {
|
|
223
223
|
expect((0, test_translator_1.markSource) `
|
|
@@ -227,7 +227,7 @@ describe('lenses', () => {
|
|
|
227
227
|
} on true
|
|
228
228
|
}
|
|
229
229
|
run: x -> y.z
|
|
230
|
-
`).
|
|
230
|
+
`).toLog((0, test_translator_1.errorMessage)('Cannot use view from join'));
|
|
231
231
|
});
|
|
232
232
|
test('cannot use view from join in nest', () => {
|
|
233
233
|
expect((0, test_translator_1.markSource) `
|
|
@@ -237,7 +237,7 @@ describe('lenses', () => {
|
|
|
237
237
|
} on true
|
|
238
238
|
}
|
|
239
239
|
run: x -> { nest: y.z }
|
|
240
|
-
`).
|
|
240
|
+
`).toLog((0, test_translator_1.errorMessage)('Cannot use view from join'));
|
|
241
241
|
});
|
|
242
242
|
test('cannot use view from join as nest view head', () => {
|
|
243
243
|
expect((0, test_translator_1.markSource) `
|
|
@@ -247,7 +247,7 @@ describe('lenses', () => {
|
|
|
247
247
|
} on true
|
|
248
248
|
}
|
|
249
249
|
run: x -> { nest: y.z + { limit: 1 } }
|
|
250
|
-
`).
|
|
250
|
+
`).toLog((0, test_translator_1.errorMessage)('Cannot use view from join'));
|
|
251
251
|
});
|
|
252
252
|
test('cannot use view from join as lens in query', () => {
|
|
253
253
|
expect((0, test_translator_1.markSource) `
|
|
@@ -257,7 +257,7 @@ describe('lenses', () => {
|
|
|
257
257
|
} on true
|
|
258
258
|
}
|
|
259
259
|
run: x -> ai + y.z
|
|
260
|
-
`).
|
|
260
|
+
`).toLog((0, test_translator_1.errorMessage)('Cannot use view from join as refinement'));
|
|
261
261
|
});
|
|
262
262
|
test('cannot use view from join as lens in nest', () => {
|
|
263
263
|
expect((0, test_translator_1.markSource) `
|
|
@@ -267,7 +267,7 @@ describe('lenses', () => {
|
|
|
267
267
|
} on true
|
|
268
268
|
}
|
|
269
269
|
run: x -> { nest: ai + y.z }
|
|
270
|
-
`).
|
|
270
|
+
`).toLog((0, test_translator_1.errorMessage)('Cannot use view from join as refinement'));
|
|
271
271
|
});
|
|
272
272
|
test('can nest dimension with refinement when experiment is enabled', () => {
|
|
273
273
|
expect((0, test_translator_1.markSource) `
|
|
@@ -286,7 +286,7 @@ describe('lenses', () => {
|
|
|
286
286
|
view: d is { group_by: n }
|
|
287
287
|
}
|
|
288
288
|
run: x -> d + b
|
|
289
|
-
`).
|
|
289
|
+
`).toLog((0, test_translator_1.errorMessage)('named refinement `b` must be a view, found a struct'));
|
|
290
290
|
});
|
|
291
291
|
test('cannot reference field in LHS of refinement in group_by', () => {
|
|
292
292
|
expect((0, test_translator_1.markSource) `
|
|
@@ -294,7 +294,7 @@ describe('lenses', () => {
|
|
|
294
294
|
view: v is { group_by: i is 1 }
|
|
295
295
|
}
|
|
296
296
|
run: x -> v + { group_by: j is ${'i'} }
|
|
297
|
-
`).
|
|
297
|
+
`).toLog((0, test_translator_1.errorMessage)("'i' is not defined"));
|
|
298
298
|
});
|
|
299
299
|
test('cannot named-refine multi-stage query', () => {
|
|
300
300
|
expect((0, test_translator_1.markSource) `
|
|
@@ -306,7 +306,7 @@ describe('lenses', () => {
|
|
|
306
306
|
}
|
|
307
307
|
run: x -> multi + d
|
|
308
308
|
run: x -> d + multi
|
|
309
|
-
`).
|
|
309
|
+
`).toLog((0, test_translator_1.errorMessage)('Named refinements of multi-stage views are not supported'), (0, test_translator_1.errorMessage)('named refinement `multi` must have exactly one stage'));
|
|
310
310
|
});
|
|
311
311
|
});
|
|
312
312
|
describe('partial views', () => {
|
|
@@ -342,7 +342,7 @@ describe('partial views', () => {
|
|
|
342
342
|
run: a -> {
|
|
343
343
|
nest: { group_by: astr }
|
|
344
344
|
}
|
|
345
|
-
`).
|
|
345
|
+
`).toLog((0, test_translator_1.errorMessage)('`nest:` view requires a name (add `nest_name is ...`)'));
|
|
346
346
|
});
|
|
347
347
|
test.skip('partial with index', () => {
|
|
348
348
|
expect((0, test_translator_1.markSource) `
|
|
@@ -358,7 +358,7 @@ describe('partial views', () => {
|
|
|
358
358
|
view: bad1 is { where: true }
|
|
359
359
|
view: bad2 is { where: true } + { where: false }
|
|
360
360
|
}
|
|
361
|
-
`).
|
|
361
|
+
`).toLog((0, test_translator_1.error)('ambiguous-view-type'), (0, test_translator_1.error)('ambiguous-view-type'));
|
|
362
362
|
});
|
|
363
363
|
test('disallow chains that have no fields in multi-stage', () => {
|
|
364
364
|
expect((0, test_translator_1.markSource) `
|
|
@@ -369,7 +369,7 @@ describe('partial views', () => {
|
|
|
369
369
|
}
|
|
370
370
|
run: x -> v -> { where: true }
|
|
371
371
|
run: x -> { where: true } -> { group_by: undef }
|
|
372
|
-
`).
|
|
372
|
+
`).toLog((0, test_translator_1.error)('ambiguous-view-type'), (0, test_translator_1.errorMessage)("'undef' is not defined"), (0, test_translator_1.error)('ambiguous-view-type'), (0, test_translator_1.error)('ambiguous-view-type'), (0, test_translator_1.errorMessage)("'undef' is not defined"), (0, test_translator_1.error)('ambiguous-view-type'));
|
|
373
373
|
});
|
|
374
374
|
test('copy of view with refinement should work', () => {
|
|
375
375
|
expect((0, test_translator_1.markSource) `
|
|
@@ -219,7 +219,7 @@ describe('literals', () => {
|
|
|
219
219
|
expect(m).toParse();
|
|
220
220
|
});
|
|
221
221
|
test('timezone with illegal query', () => {
|
|
222
|
-
expect(`run: a->{timezone: """${tz}%{ab->aturtle}"""; select: *}`).
|
|
222
|
+
expect(`run: a->{timezone: """${tz}%{ab->aturtle}"""; select: *}`).toLog((0, test_translator_1.errorMessage)('%{ query } illegal in this string'));
|
|
223
223
|
});
|
|
224
224
|
test('table single quote', () => {
|
|
225
225
|
const m = new test_translator_1.TestTranslator("source: n is bigquery.table('n')");
|
|
@@ -210,19 +210,19 @@ describe('source locations', () => {
|
|
|
210
210
|
expect(y.location).toMatchObject(source.locations[0]);
|
|
211
211
|
});
|
|
212
212
|
test('undefined query location', () => {
|
|
213
|
-
expect((0, test_translator_1.model) `run: ${'xyz'}`).
|
|
213
|
+
expect((0, test_translator_1.model) `run: ${'xyz'}`).toLog((0, test_translator_1.errorMessage)("Reference to undefined object 'xyz'"));
|
|
214
214
|
});
|
|
215
215
|
test('undefined field reference', () => {
|
|
216
|
-
expect((0, test_translator_1.model) `run: a -> { group_by: ${'xyz'} }`).
|
|
216
|
+
expect((0, test_translator_1.model) `run: a -> { group_by: ${'xyz'} }`).toLog((0, test_translator_1.errorMessage)("'xyz' is not defined"));
|
|
217
217
|
});
|
|
218
218
|
test('bad query', () => {
|
|
219
|
-
expect((0, test_translator_1.model) `run: a -> { group_by: astr; ${'select: *'} }`).
|
|
219
|
+
expect((0, test_translator_1.model) `run: a -> { group_by: astr; ${'select: *'} }`).toLog((0, test_translator_1.errorMessage)(/Not legal in grouping query/));
|
|
220
220
|
});
|
|
221
221
|
test.skip('undefined field reference in top', () => {
|
|
222
|
-
expect((0, test_translator_1.model) `run: a -> { group_by: one is 1; top: 1 by ${'xyz'} }`).
|
|
222
|
+
expect((0, test_translator_1.model) `run: a -> { group_by: one is 1; top: 1 by ${'xyz'} }`).toLog((0, test_translator_1.errorMessage)("'xyz' is not defined"));
|
|
223
223
|
});
|
|
224
224
|
test.skip('undefined field reference in order_by', () => {
|
|
225
|
-
expect((0, test_translator_1.model) `run: a -> { group_by: one is 1; order_by: ${'xyz'} }`).
|
|
225
|
+
expect((0, test_translator_1.model) `run: a -> { group_by: one is 1; order_by: ${'xyz'} }`).toLog((0, test_translator_1.errorMessage)("'xyz' is not defined"));
|
|
226
226
|
});
|
|
227
227
|
});
|
|
228
228
|
describe('source references', () => {
|
|
@@ -31,19 +31,19 @@ describe('parameters', () => {
|
|
|
31
31
|
expect((0, test_translator_1.markSource) `
|
|
32
32
|
##! experimental.parameters
|
|
33
33
|
source: ab_new(param::number is ${'"hello"'}) is ab
|
|
34
|
-
`).
|
|
34
|
+
`).toLog((0, test_translator_1.errorMessage)('Default value for parameter does not match declared type `number`'));
|
|
35
35
|
});
|
|
36
36
|
test('error if paramter has no type or value', () => {
|
|
37
37
|
expect((0, test_translator_1.markSource) `
|
|
38
38
|
##! experimental.parameters
|
|
39
39
|
source: ab_new(param) is ab
|
|
40
|
-
`).
|
|
40
|
+
`).toLog((0, test_translator_1.errorMessage)('Parameter must have default value or declared type'));
|
|
41
41
|
});
|
|
42
42
|
test('error if paramter type is null', () => {
|
|
43
43
|
expect((0, test_translator_1.markSource) `
|
|
44
44
|
##! experimental.parameters
|
|
45
45
|
source: ab_new(param is null) is ab
|
|
46
|
-
`).
|
|
46
|
+
`).toLog((0, test_translator_1.errorMessage)('Default value cannot have type `null` unless parameter type is also specified'));
|
|
47
47
|
});
|
|
48
48
|
test('allowed to write null::string', () => {
|
|
49
49
|
expect(`
|
|
@@ -70,13 +70,13 @@ describe('parameters', () => {
|
|
|
70
70
|
expect((0, test_translator_1.markSource) `
|
|
71
71
|
##! experimental.parameters
|
|
72
72
|
source: ab_new(param is 10 to 20) is ab
|
|
73
|
-
`).
|
|
73
|
+
`).toLog((0, test_translator_1.errorMessage)('A Range is not a value'));
|
|
74
74
|
});
|
|
75
75
|
test('no additional error if default value type is error', () => {
|
|
76
76
|
expect((0, test_translator_1.markSource) `
|
|
77
77
|
##! experimental.parameters
|
|
78
78
|
source: ab_new(param::number is 1 + ${'"foo"'}) is ab
|
|
79
|
-
`).
|
|
79
|
+
`).toLog((0, test_translator_1.errorMessage)("The '+' operator requires a number, not a 'string'"));
|
|
80
80
|
});
|
|
81
81
|
test('can declare parameter with inferred type', () => {
|
|
82
82
|
expect(`
|
|
@@ -242,21 +242,21 @@ describe('parameters', () => {
|
|
|
242
242
|
view: all_fields is { select: * }
|
|
243
243
|
}
|
|
244
244
|
run: ab_new(param is 1) -> all_fields -> { select: ${'param'} }
|
|
245
|
-
`).
|
|
245
|
+
`).toLog((0, test_translator_1.errorMessage)("'param' is not defined"));
|
|
246
246
|
});
|
|
247
247
|
test('cannot reference renamed param in query against source', () => {
|
|
248
248
|
expect((0, test_translator_1.markSource) `
|
|
249
249
|
##! experimental.parameters
|
|
250
250
|
source: ab_new(param::number) is ab
|
|
251
251
|
run: ab_new(param is 1) -> { select: p is ${'param'} }
|
|
252
|
-
`).
|
|
252
|
+
`).toLog((0, test_translator_1.errorMessage)("'param' is not defined"));
|
|
253
253
|
});
|
|
254
254
|
test('cannot reference param in query against source', () => {
|
|
255
255
|
expect((0, test_translator_1.markSource) `
|
|
256
256
|
##! experimental.parameters
|
|
257
257
|
source: ab_new(param::number) is ab
|
|
258
258
|
run: ab_new(param is 1) -> { select: ${'param'} }
|
|
259
|
-
`).
|
|
259
|
+
`).toLog((0, test_translator_1.errorMessage)("'param' is not defined"));
|
|
260
260
|
});
|
|
261
261
|
test('cannot reference param in source extension', () => {
|
|
262
262
|
expect((0, test_translator_1.markSource) `
|
|
@@ -265,7 +265,7 @@ describe('parameters', () => {
|
|
|
265
265
|
source: x is ab_new(param is 1) extend {
|
|
266
266
|
dimension: param_copy is ${'param'}
|
|
267
267
|
}
|
|
268
|
-
`).
|
|
268
|
+
`).toLog((0, test_translator_1.errorMessage)("'param' is not defined"));
|
|
269
269
|
});
|
|
270
270
|
test('cannot reference param in in-query source extension', () => {
|
|
271
271
|
expect((0, test_translator_1.markSource) `
|
|
@@ -277,14 +277,14 @@ describe('parameters', () => {
|
|
|
277
277
|
}
|
|
278
278
|
group_by: param_copy
|
|
279
279
|
}
|
|
280
|
-
`).
|
|
280
|
+
`).toLog((0, test_translator_1.errorMessage)("'param' is not defined"));
|
|
281
281
|
});
|
|
282
282
|
test('can reference field in source in argument', () => {
|
|
283
283
|
expect((0, test_translator_1.markSource) `
|
|
284
284
|
##! experimental.parameters
|
|
285
285
|
source: ab_new(param::number) is ab
|
|
286
286
|
run: ab_new(param is ${'ai'}) -> { select: * }
|
|
287
|
-
`).
|
|
287
|
+
`).toLog((0, test_translator_1.errorMessage)('`ai` is not defined'));
|
|
288
288
|
});
|
|
289
289
|
test('can pass through parameter to joined source (shorthand)', () => {
|
|
290
290
|
expect(`
|
|
@@ -384,7 +384,7 @@ describe('parameters', () => {
|
|
|
384
384
|
##! experimental.parameters
|
|
385
385
|
source: ab_new(param::number) is ab
|
|
386
386
|
run: ab_new(param is 1) -> { select: ${'param'} }
|
|
387
|
-
`).
|
|
387
|
+
`).toLog((0, test_translator_1.errorMessage)("'param' is not defined"));
|
|
388
388
|
});
|
|
389
389
|
test('can reference param in view in source', () => {
|
|
390
390
|
expect(`
|
|
@@ -407,13 +407,13 @@ describe('parameters', () => {
|
|
|
407
407
|
##! experimental.parameters
|
|
408
408
|
source: ab_new(param::number) is ab
|
|
409
409
|
run: ab_new(param is 1) -> { select: p is ${'param'} }
|
|
410
|
-
`).
|
|
410
|
+
`).toLog((0, test_translator_1.errorMessage)("'param' is not defined"));
|
|
411
411
|
});
|
|
412
412
|
test('error when declaring parameter twice', () => {
|
|
413
413
|
expect((0, test_translator_1.markSource) `
|
|
414
414
|
##! experimental.parameters
|
|
415
415
|
source: ab_new(param::number, ${'param::number'}) is ab
|
|
416
|
-
`).
|
|
416
|
+
`).toLog((0, test_translator_1.errorMessage)('Cannot redefine parameter `param`'));
|
|
417
417
|
});
|
|
418
418
|
// This behavior will likely change in the future; but in the meantime, this
|
|
419
419
|
// safeguards against some confusion about parameter scoping
|
|
@@ -423,7 +423,7 @@ describe('parameters', () => {
|
|
|
423
423
|
source: ab_new(ai::string) is ab extend {
|
|
424
424
|
dimension: foo is upper(ai)
|
|
425
425
|
}
|
|
426
|
-
`).
|
|
426
|
+
`).toLog((0, test_translator_1.errorMessage)('No matching overload for function upper(number)'), (0, test_translator_1.errorMessage)('Illegal shadowing of field `ai` by parameter with the same name'));
|
|
427
427
|
});
|
|
428
428
|
test('can shadow field that is excepted', () => {
|
|
429
429
|
expect(`
|
|
@@ -438,7 +438,7 @@ describe('parameters', () => {
|
|
|
438
438
|
expect((0, test_translator_1.markSource) `
|
|
439
439
|
##! experimental.parameters
|
|
440
440
|
source: ab_new(${'ai::string'}) is ab
|
|
441
|
-
`).
|
|
441
|
+
`).toLog((0, test_translator_1.errorMessage)('Illegal shadowing of field `ai` by parameter with the same name'));
|
|
442
442
|
});
|
|
443
443
|
test('do not inherit parameters from base source', () => {
|
|
444
444
|
expect((0, test_translator_1.markSource) `
|
|
@@ -446,7 +446,7 @@ describe('parameters', () => {
|
|
|
446
446
|
source: ab_new(param::number) is ab
|
|
447
447
|
source: ab_new_new is ab_new(param is 1)
|
|
448
448
|
run: ab_new_new(${'param'} is 2) -> { select: * }
|
|
449
|
-
`).
|
|
449
|
+
`).toLog((0, test_translator_1.errorMessage)('`ab_new_new` has no declared parameter named `param`'));
|
|
450
450
|
});
|
|
451
451
|
test('error when declaring field with same name as parameter', () => {
|
|
452
452
|
expect((0, test_translator_1.markSource) `
|
|
@@ -454,12 +454,12 @@ describe('parameters', () => {
|
|
|
454
454
|
source: ab_new(param::number) is ab extend {
|
|
455
455
|
dimension: param is 1
|
|
456
456
|
}
|
|
457
|
-
`).
|
|
457
|
+
`).toLog((0, test_translator_1.errorMessage)("Cannot redefine 'param'"));
|
|
458
458
|
});
|
|
459
459
|
test('error when declaring parameter without experiment enabled', () => {
|
|
460
460
|
expect((0, test_translator_1.markSource) `
|
|
461
461
|
source: ab_new(param::number) is ab
|
|
462
|
-
`).
|
|
462
|
+
`).toLog((0, test_translator_1.error)('experiment-not-enabled', { experimentId: 'parameters' }));
|
|
463
463
|
});
|
|
464
464
|
test('cannot except parameter from extended source', () => {
|
|
465
465
|
expect((0, test_translator_1.markSource) `
|
|
@@ -468,7 +468,7 @@ describe('parameters', () => {
|
|
|
468
468
|
source: ab_new_new(param_b::number) is ab_new(param_a is 1) extend {
|
|
469
469
|
except: param_a
|
|
470
470
|
}
|
|
471
|
-
`).
|
|
471
|
+
`).toLog((0, test_translator_1.errorMessage)('`param_a` is not defined'));
|
|
472
472
|
});
|
|
473
473
|
test('cannot except parameter in direct extend', () => {
|
|
474
474
|
expect((0, test_translator_1.markSource) `
|
|
@@ -476,7 +476,7 @@ describe('parameters', () => {
|
|
|
476
476
|
source: ab_new(param::number) is ab extend {
|
|
477
477
|
except: param
|
|
478
478
|
}
|
|
479
|
-
`).
|
|
479
|
+
`).toLog((0, test_translator_1.errorMessage)('Illegal `except:` of parameter'));
|
|
480
480
|
});
|
|
481
481
|
test('cannot accept parameter', () => {
|
|
482
482
|
expect((0, test_translator_1.markSource) `
|
|
@@ -484,19 +484,19 @@ describe('parameters', () => {
|
|
|
484
484
|
source: ab_new(param::number) is ab extend {
|
|
485
485
|
accept: param
|
|
486
486
|
}
|
|
487
|
-
`).
|
|
487
|
+
`).toLog((0, test_translator_1.errorMessage)('Illegal `accept:` of parameter'));
|
|
488
488
|
});
|
|
489
489
|
test('error when using parameter without experiment enabled', () => {
|
|
490
490
|
expect((0, test_translator_1.markSource) `
|
|
491
491
|
run: ab_new${'(param is param)'} -> { select: * }
|
|
492
|
-
`).
|
|
492
|
+
`).toLog((0, test_translator_1.error)('experiment-not-enabled', { experimentId: 'parameters' }));
|
|
493
493
|
});
|
|
494
494
|
test('parameters cannot reference themselves', () => {
|
|
495
495
|
expect((0, test_translator_1.markSource) `
|
|
496
496
|
##! experimental.parameters
|
|
497
497
|
source: ab_new(param::number) is ab
|
|
498
498
|
run: ab_new(param is ${'param'}) -> { select: * }
|
|
499
|
-
`).
|
|
499
|
+
`).toLog((0, test_translator_1.errorMessage)('`param` is not defined'));
|
|
500
500
|
});
|
|
501
501
|
// This just looks like circular referencing--in reality, you cannot reference other
|
|
502
502
|
// parameters in parameter arguments, hence just "xxx is not defined"
|
|
@@ -505,42 +505,42 @@ describe('parameters', () => {
|
|
|
505
505
|
##! experimental.parameters
|
|
506
506
|
source: ab_new(p_a::number, p_b::number) is ab
|
|
507
507
|
run: ab_new(p_a is ${'p_b'}, p_b is ${'p_a'}) -> { select: * }
|
|
508
|
-
`).
|
|
508
|
+
`).toLog((0, test_translator_1.errorMessage)('`p_b` is not defined'), (0, test_translator_1.errorMessage)('`p_a` is not defined'));
|
|
509
509
|
});
|
|
510
510
|
test('error when passing param with no name', () => {
|
|
511
511
|
expect((0, test_translator_1.markSource) `
|
|
512
512
|
##! experimental.parameters
|
|
513
513
|
source: ab_new(param::number) is ab
|
|
514
514
|
run: ab_new(${'1'}) -> { select: * }
|
|
515
|
-
`).
|
|
515
|
+
`).toLog((0, test_translator_1.errorMessage)('Parameterized source arguments must be named with `parameter_name is`'), (0, test_translator_1.errorMessage)('Argument not provided for required parameter `param`'));
|
|
516
516
|
});
|
|
517
517
|
test('error when passing param with incorrect name', () => {
|
|
518
518
|
expect((0, test_translator_1.markSource) `
|
|
519
519
|
##! experimental.parameters
|
|
520
520
|
source: ab_new(param::number) is ab
|
|
521
521
|
run: ab_new(${'wrong_name'} is 1, param is 2) -> { select: * }
|
|
522
|
-
`).
|
|
522
|
+
`).toLog((0, test_translator_1.errorMessage)('`ab_new` has no declared parameter named `wrong_name`'));
|
|
523
523
|
});
|
|
524
524
|
test('error when passing param multiple times', () => {
|
|
525
525
|
expect((0, test_translator_1.markSource) `
|
|
526
526
|
##! experimental.parameters
|
|
527
527
|
source: ab_new(param::number) is ab
|
|
528
528
|
run: ab_new(param is 1, ${'param is 2'}) -> { select: * }
|
|
529
|
-
`).
|
|
529
|
+
`).toLog((0, test_translator_1.errorMessage)('Cannot pass argument for `param` more than once'));
|
|
530
530
|
});
|
|
531
531
|
test('error when not specifying argument for param with parentheses', () => {
|
|
532
532
|
expect((0, test_translator_1.markSource) `
|
|
533
533
|
##! experimental.parameters
|
|
534
534
|
source: ab_new(param::number) is ab
|
|
535
535
|
run: ${'ab_new'}() -> { select: * }
|
|
536
|
-
`).
|
|
536
|
+
`).toLog((0, test_translator_1.errorMessage)('Argument not provided for required parameter `param`'));
|
|
537
537
|
});
|
|
538
538
|
test('error when not specifying argument for param without parentheses', () => {
|
|
539
539
|
expect((0, test_translator_1.markSource) `
|
|
540
540
|
##! experimental.parameters
|
|
541
541
|
source: ab_new(param::number) is ab
|
|
542
542
|
run: ${'ab_new'} -> { select: * }
|
|
543
|
-
`).
|
|
543
|
+
`).toLog((0, test_translator_1.errorMessage)('Argument not provided for required parameter `param`'));
|
|
544
544
|
});
|
|
545
545
|
test('error when not specifying argument for param second time', () => {
|
|
546
546
|
expect((0, test_translator_1.markSource) `
|
|
@@ -548,7 +548,7 @@ describe('parameters', () => {
|
|
|
548
548
|
source: ab_new(param::number) is ab
|
|
549
549
|
run: ab_new(param is 1) -> { select: * }
|
|
550
550
|
run: ${'ab_new'} -> { select: * }
|
|
551
|
-
`).
|
|
551
|
+
`).toLog((0, test_translator_1.errorMessage)('Argument not provided for required parameter `param`'));
|
|
552
552
|
});
|
|
553
553
|
test('error when referencing parameter that does not exist in join definition', () => {
|
|
554
554
|
expect((0, test_translator_1.markSource) `
|
|
@@ -557,13 +557,13 @@ describe('parameters', () => {
|
|
|
557
557
|
source: ab_new_2(param_2::number) is ab extend {
|
|
558
558
|
join_one: ab_join is ab_new_1(param_1 is ${'param_3'})
|
|
559
559
|
}
|
|
560
|
-
`).
|
|
560
|
+
`).toLog((0, test_translator_1.errorMessage)('`param_3` is not defined'));
|
|
561
561
|
});
|
|
562
562
|
test('error when referencing identifier in default param value', () => {
|
|
563
563
|
expect((0, test_translator_1.markSource) `
|
|
564
564
|
##! experimental.parameters
|
|
565
565
|
source: ab_new_1(param_1 is ${'ident'}) is ab
|
|
566
|
-
`).
|
|
566
|
+
`).toLog((0, test_translator_1.errorMessage)('Only constants allowed in parameter default values'));
|
|
567
567
|
});
|
|
568
568
|
test.skip('can use param in multi-stage query', () => {
|
|
569
569
|
expect(`
|
|
@@ -582,7 +582,7 @@ describe('parameters', () => {
|
|
|
582
582
|
##! experimental.parameters
|
|
583
583
|
source: ab_new(param::number) is ab
|
|
584
584
|
source: ab_new_new(param::number) is ab_new(${'param'}) -> { select: * }
|
|
585
|
-
`).
|
|
585
|
+
`).toLog((0, test_translator_1.errorMessage)('`param` is not defined'));
|
|
586
586
|
});
|
|
587
587
|
test.skip('can add an annotation to a param', () => {
|
|
588
588
|
expect(`
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { DocumentLocation } from '../../model';
|
|
2
2
|
import { LogSeverity } from '../parse-log';
|
|
3
|
-
type
|
|
4
|
-
type ComplexProblemSpec = {
|
|
3
|
+
type MessageProblemSpec = {
|
|
5
4
|
severity: LogSeverity;
|
|
6
|
-
message:
|
|
5
|
+
message: string | RegExp;
|
|
6
|
+
data?: any;
|
|
7
7
|
};
|
|
8
|
-
type
|
|
8
|
+
type CodeProblemSpec = {
|
|
9
|
+
severity: LogSeverity;
|
|
10
|
+
code: string;
|
|
11
|
+
data?: any;
|
|
12
|
+
};
|
|
13
|
+
type ProblemSpec = CodeProblemSpec | MessageProblemSpec;
|
|
9
14
|
declare global {
|
|
10
15
|
namespace jest {
|
|
11
16
|
interface Matchers<R> {
|
|
@@ -26,31 +31,8 @@ declare global {
|
|
|
26
31
|
* X can be a MarkedSource, a string, or a model.
|
|
27
32
|
*/
|
|
28
33
|
toTranslate(): R;
|
|
29
|
-
/**
|
|
30
|
-
* expect(X).toTranslateWithWarnings(expectedWarnings)
|
|
31
|
-
*
|
|
32
|
-
* Passes if the source compiles to code which could be used to
|
|
33
|
-
* generate SQL, and the specified warnings appear. If X is a marked
|
|
34
|
-
* source, the warnings which are found must match the locations of
|
|
35
|
-
* the markings.
|
|
36
|
-
*
|
|
37
|
-
* X can be a MarkedSource, a string, or a model. If it is a marked
|
|
38
|
-
* source, the errors which are found must match the locations of
|
|
39
|
-
* the markings.
|
|
40
|
-
*/
|
|
41
|
-
toTranslateWithWarnings(...expectedWarnings: SimpleProblemSpec[]): R;
|
|
42
34
|
toReturnType(tp: string): R;
|
|
43
|
-
|
|
44
|
-
* expect(X).translateToFailWith(expectedErrors)
|
|
45
|
-
*
|
|
46
|
-
* X can be a MarkedSource, a string, or a model. If it is a marked
|
|
47
|
-
* source, the errors which are found must match the locations of
|
|
48
|
-
* the markings.
|
|
49
|
-
*
|
|
50
|
-
* @param expectedErrors varargs list of strings which must match
|
|
51
|
-
* exactly, or regular expressions.
|
|
52
|
-
*/
|
|
53
|
-
translationToFailWith(...expectedErrors: ProblemSpec[]): R;
|
|
35
|
+
toLog(...expectedErrors: ProblemSpec[]): R;
|
|
54
36
|
isLocationIn(at: DocumentLocation, txt: string): R;
|
|
55
37
|
/**
|
|
56
38
|
* expect(X).compilesTo('expression-string')
|
|
@@ -102,19 +102,6 @@ function highlightError(dl, txt) {
|
|
|
102
102
|
}
|
|
103
103
|
return output.join('\n');
|
|
104
104
|
}
|
|
105
|
-
function normalizeProblemSpec(defaultSeverity) {
|
|
106
|
-
return function (spec) {
|
|
107
|
-
if (typeof spec === 'string') {
|
|
108
|
-
return { severity: defaultSeverity, message: spec };
|
|
109
|
-
}
|
|
110
|
-
else if (spec instanceof RegExp) {
|
|
111
|
-
return { severity: defaultSeverity, message: spec };
|
|
112
|
-
}
|
|
113
|
-
else {
|
|
114
|
-
return spec;
|
|
115
|
-
}
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
105
|
function isMarkedSource(ts) {
|
|
119
106
|
return typeof ts !== 'string' && !(ts instanceof test_translator_1.TestTranslator);
|
|
120
107
|
}
|
|
@@ -207,11 +194,9 @@ expect.extend({
|
|
|
207
194
|
const msg = `Expression type ${d.dataType} ${pass ? '=' : '!='} $[returnType`;
|
|
208
195
|
return { pass, message: () => msg };
|
|
209
196
|
},
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
toTranslateWithWarnings: function (s, ...msgs) {
|
|
214
|
-
return checkForProblems(this, true, s, 'warn', ...msgs);
|
|
197
|
+
toLog: function (s, ...msgs) {
|
|
198
|
+
const expectCompiles = !msgs.some(m => m.severity === 'error');
|
|
199
|
+
return checkForProblems(this, expectCompiles, s, ...msgs);
|
|
215
200
|
},
|
|
216
201
|
isLocationIn: function (checkAt, at, text) {
|
|
217
202
|
if (this.equals(at, checkAt)) {
|
|
@@ -263,13 +248,14 @@ expect.extend({
|
|
|
263
248
|
return { pass, message: () => `${msg}` };
|
|
264
249
|
},
|
|
265
250
|
});
|
|
266
|
-
function
|
|
251
|
+
function problemSpecSummary(s) {
|
|
252
|
+
return `${s.severity} '${'message' in s ? s.message : s.code}' ${s.data !== undefined ? (0, test_translator_1.pretty)(s.data) : ''}`;
|
|
253
|
+
}
|
|
254
|
+
function checkForProblems(context, expectCompiles, s, ...msgs) {
|
|
267
255
|
var _a;
|
|
268
256
|
let emsg = `Expected ${expectCompiles ? 'to' : 'to not'} compile with: `;
|
|
269
257
|
const mSrc = isMarkedSource(s) ? s : undefined;
|
|
270
|
-
const
|
|
271
|
-
const normMsgs = msgs.map(normalize);
|
|
272
|
-
const qmsgs = normMsgs.map(s => `${s.severity} '${s.message}'`);
|
|
258
|
+
const qmsgs = msgs.map(problemSpecSummary);
|
|
273
259
|
if (msgs.length === 1) {
|
|
274
260
|
emsg += ` ${qmsgs[0]}`;
|
|
275
261
|
}
|
|
@@ -297,25 +283,44 @@ function checkForProblems(context, expectCompiles, s, defaultSeverity, ...msgs)
|
|
|
297
283
|
const explain = [];
|
|
298
284
|
const errList = m.problemResponse().problems;
|
|
299
285
|
let i;
|
|
300
|
-
for (i = 0; i <
|
|
301
|
-
const msg =
|
|
286
|
+
for (i = 0; i < msgs.length && errList[i]; i += 1) {
|
|
287
|
+
const msg = msgs[i];
|
|
302
288
|
const err = errList[i];
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
289
|
+
let matched = true;
|
|
290
|
+
if ('message' in msg) {
|
|
291
|
+
if (typeof msg.message === 'string') {
|
|
292
|
+
if (msg.message !== err.message) {
|
|
293
|
+
explain.push(`Expected: ${msg.message}\nGot: ${err.message}`);
|
|
294
|
+
matched = false;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
if (!err.message.match(msg.message)) {
|
|
299
|
+
explain.push(`Expected: ${msg.message}\nGot: ${err.message}`);
|
|
300
|
+
matched = false;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
if (msg.code !== err.code) {
|
|
306
|
+
matched = false;
|
|
307
|
+
explain.push(`Expected: ${msg.code}\nGot: ${err.code}`);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
306
310
|
if (err.severity !== msg.severity) {
|
|
307
311
|
explain.push(`Expected ${msg.severity}, got ${err.severity} ${err.message}`);
|
|
308
312
|
}
|
|
309
|
-
if (
|
|
310
|
-
explain.push(`Expected: ${msg.message}\nGot: ${err.message}`);
|
|
311
|
-
}
|
|
312
|
-
else {
|
|
313
|
+
if (matched) {
|
|
313
314
|
if (mSrc === null || mSrc === void 0 ? void 0 : mSrc.locations[i]) {
|
|
314
315
|
const have = (_a = err.at) === null || _a === void 0 ? void 0 : _a.range;
|
|
315
316
|
const want = mSrc.locations[i].range;
|
|
316
317
|
if (!context.equals(have, want)) {
|
|
317
|
-
explain.push(`Expected
|
|
318
|
-
|
|
318
|
+
explain.push(`Expected ${err.code} (${err.message}) at location: ${rangeToStr(want)}\n` + `Actual location: ${rangeToStr(have)}`);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
if (msg.data !== undefined) {
|
|
322
|
+
if (JSON.stringify(msg.data) !== JSON.stringify(err.data)) {
|
|
323
|
+
explain.push(`Expected log data ${(0, test_translator_1.pretty)(msg.data)}\nGot ${(0, test_translator_1.pretty)(err.data)}`);
|
|
319
324
|
}
|
|
320
325
|
}
|
|
321
326
|
}
|