Skip to content

Commit 06377cd

Browse files
Add error message tests
1 parent 72abc89 commit 06377cd

2 files changed

Lines changed: 88 additions & 1 deletion

File tree

crates/bindings-typescript/src/lib/query.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -711,7 +711,7 @@ type BooleanExprData<Table extends TypedTableDef> = (
711711
};
712712

713713
type AndOrMixedTableScopeError = {
714-
readonly __queryBuilderAndOrError: 'Cannot combine predicates from different table scopes with and/or. In semijoin on(...), keep only the join equality and move extra predicates to .where(...).';
714+
readonly 'Cannot combine predicates from different table scopes with and/or. In semijoin on(...), keep only the join equality and move extra predicates to .where(...).': never;
715715
};
716716

717717
type RequireSameAndOrTable<
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
2+
import { tmpdir } from 'node:os';
3+
import path from 'node:path';
4+
import { fileURLToPath } from 'node:url';
5+
import * as ts from 'typescript';
6+
import { describe, expect, it } from 'vitest';
7+
8+
const bindingsRoot = path.resolve(
9+
path.dirname(fileURLToPath(import.meta.url)),
10+
'..'
11+
);
12+
13+
function runTypecheck(semijoinPredicateExpr: string) {
14+
const tmpDir = mkdtempSync(path.join(tmpdir(), 'stdb-query-diag-'));
15+
const reproPath = path.join(tmpDir, 'repro.ts');
16+
17+
const imports = {
18+
query: path.join(bindingsRoot, 'src/lib/query.ts'),
19+
moduleBindings: path.join(
20+
bindingsRoot,
21+
'test-app/src/module_bindings/index.ts'
22+
),
23+
sys: path.join(bindingsRoot, 'src/server/sys.d.ts'),
24+
};
25+
26+
const source = `
27+
import { and } from ${JSON.stringify(imports.query)};
28+
import { tables } from ${JSON.stringify(imports.moduleBindings)};
29+
30+
tables.player
31+
.leftSemijoin(tables.unindexed_player, (l, r) => ${semijoinPredicateExpr})
32+
.build();
33+
`;
34+
35+
writeFileSync(reproPath, source);
36+
37+
try {
38+
const options: ts.CompilerOptions = {
39+
target: ts.ScriptTarget.ESNext,
40+
module: ts.ModuleKind.ESNext,
41+
strict: true,
42+
noEmit: true,
43+
skipLibCheck: true,
44+
forceConsistentCasingInFileNames: true,
45+
allowImportingTsExtensions: true,
46+
noImplicitAny: true,
47+
moduleResolution: ts.ModuleResolutionKind.Bundler,
48+
useDefineForClassFields: true,
49+
verbatimModuleSyntax: true,
50+
isolatedModules: true,
51+
};
52+
53+
const host = ts.createCompilerHost(options);
54+
const program = ts.createProgram([reproPath, imports.sys], options, host);
55+
const diagnostics = ts.getPreEmitDiagnostics(program);
56+
const output = diagnostics
57+
.map(d => ts.flattenDiagnosticMessageText(d.messageText, '\n'))
58+
.join('\n');
59+
60+
return {
61+
status: diagnostics.length === 0 ? 0 : 1,
62+
output,
63+
};
64+
} finally {
65+
rmSync(tmpDir, { recursive: true, force: true });
66+
}
67+
}
68+
69+
describe('query builder diagnostics', () => {
70+
const messageStart =
71+
'Cannot combine predicates from different table scopes with and/or.';
72+
const messageHint = 'move extra predicates to .where(...)';
73+
74+
it('reports a clear message for free-floating and(...) in semijoin predicates', () => {
75+
const { status, output } = runTypecheck('and(l.id.eq(r.id), r.id.eq(5))');
76+
expect(status).not.toBe(0);
77+
expect(output).toContain(messageStart);
78+
expect(output).toContain(messageHint);
79+
});
80+
81+
it('reports a clear message for method-style .and(...) in semijoin predicates', () => {
82+
const { status, output } = runTypecheck('l.id.eq(r.id).and(r.id.eq(5))');
83+
expect(status).not.toBe(0);
84+
expect(output).toContain(messageStart);
85+
expect(output).toContain(messageHint);
86+
});
87+
});

0 commit comments

Comments
 (0)