@prisma-next/sql-runtime 0.5.0-dev.7 → 0.5.0-dev.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -21
- package/dist/{exports-BQZSVXXt.mjs → exports-BOHa3Emo.mjs} +481 -128
- package/dist/exports-BOHa3Emo.mjs.map +1 -0
- package/dist/{index-yb51L_1h.d.mts → index-CZmC2kD3.d.mts} +53 -16
- package/dist/index-CZmC2kD3.d.mts.map +1 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +1 -1
- package/dist/test/utils.d.mts +6 -5
- package/dist/test/utils.d.mts.map +1 -1
- package/dist/test/utils.mjs +7 -2
- package/dist/test/utils.mjs.map +1 -1
- package/package.json +12 -14
- package/src/codecs/decoding.ts +172 -116
- package/src/codecs/encoding.ts +59 -21
- package/src/exports/index.ts +10 -7
- package/src/fingerprint.ts +22 -0
- package/src/guardrails/raw.ts +214 -0
- package/src/lower-sql-plan.ts +3 -3
- package/src/marker.ts +82 -0
- package/src/middleware/before-compile-chain.ts +32 -1
- package/src/middleware/budgets.ts +14 -11
- package/src/middleware/lints.ts +3 -3
- package/src/middleware/sql-middleware.ts +6 -5
- package/src/runtime-spi.ts +43 -0
- package/src/sql-family-adapter.ts +3 -2
- package/src/sql-marker.ts +1 -1
- package/src/sql-runtime.ts +272 -110
- package/dist/exports-BQZSVXXt.mjs.map +0 -1
- package/dist/index-yb51L_1h.d.mts.map +0 -1
- package/test/async-iterable-result.test.ts +0 -141
- package/test/before-compile-chain.test.ts +0 -223
- package/test/budgets.test.ts +0 -431
- package/test/context.types.test-d.ts +0 -68
- package/test/execution-stack.test.ts +0 -161
- package/test/json-schema-validation.test.ts +0 -571
- package/test/lints.test.ts +0 -160
- package/test/mutation-default-generators.test.ts +0 -254
- package/test/parameterized-types.test.ts +0 -529
- package/test/sql-context.test.ts +0 -384
- package/test/sql-family-adapter.test.ts +0 -103
- package/test/sql-runtime.test.ts +0 -792
- package/test/utils.ts +0 -297
package/test/sql-context.test.ts
DELETED
|
@@ -1,384 +0,0 @@
|
|
|
1
|
-
import { type Contract, coreHash, executionHash, profileHash } from '@prisma-next/contract/types';
|
|
2
|
-
import type { SqlStorage } from '@prisma-next/sql-contract/types';
|
|
3
|
-
import type { SqlOperationDescriptor } from '@prisma-next/sql-operations';
|
|
4
|
-
import { codec, createCodecRegistry } from '@prisma-next/sql-relational-core/ast';
|
|
5
|
-
import { describe, expect, it } from 'vitest';
|
|
6
|
-
import {
|
|
7
|
-
createExecutionContext,
|
|
8
|
-
type SqlExecutionStack,
|
|
9
|
-
type SqlRuntimeExtensionDescriptor,
|
|
10
|
-
type SqlRuntimeTargetDescriptor,
|
|
11
|
-
} from '../src/sql-context';
|
|
12
|
-
import {
|
|
13
|
-
createStubAdapter,
|
|
14
|
-
createTestAdapterDescriptor,
|
|
15
|
-
createTestTargetDescriptor,
|
|
16
|
-
} from './utils';
|
|
17
|
-
|
|
18
|
-
const testContract: Contract<SqlStorage> = {
|
|
19
|
-
targetFamily: 'sql',
|
|
20
|
-
target: 'postgres',
|
|
21
|
-
profileHash: profileHash('sha256:test'),
|
|
22
|
-
models: {},
|
|
23
|
-
roots: {},
|
|
24
|
-
storage: { storageHash: coreHash('sha256:test'), tables: {} },
|
|
25
|
-
extensionPacks: {},
|
|
26
|
-
capabilities: {},
|
|
27
|
-
meta: {},
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
function createTestExtensionDescriptor(options?: {
|
|
31
|
-
hasCodecs?: boolean;
|
|
32
|
-
hasOperations?: boolean;
|
|
33
|
-
}): SqlRuntimeExtensionDescriptor<'postgres'> {
|
|
34
|
-
const { hasCodecs = false, hasOperations = false } = options ?? {};
|
|
35
|
-
|
|
36
|
-
const codecRegistry = hasCodecs
|
|
37
|
-
? (() => {
|
|
38
|
-
const registry = createCodecRegistry();
|
|
39
|
-
registry.register(
|
|
40
|
-
codec({
|
|
41
|
-
typeId: 'test/ext@1',
|
|
42
|
-
targetTypes: ['ext'],
|
|
43
|
-
encode: (v: string) => v,
|
|
44
|
-
decode: (w: string) => w,
|
|
45
|
-
}),
|
|
46
|
-
);
|
|
47
|
-
return registry;
|
|
48
|
-
})()
|
|
49
|
-
: createCodecRegistry();
|
|
50
|
-
|
|
51
|
-
const operationsArray: ReadonlyArray<SqlOperationDescriptor> = hasOperations
|
|
52
|
-
? [
|
|
53
|
-
{
|
|
54
|
-
method: 'testOp',
|
|
55
|
-
args: [{ codecId: 'test/ext@1', nullable: false }],
|
|
56
|
-
returns: { codecId: 'test/ext@1', nullable: false },
|
|
57
|
-
lowering: {
|
|
58
|
-
targetFamily: 'sql' as const,
|
|
59
|
-
strategy: 'function' as const,
|
|
60
|
-
template: 'test()',
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
]
|
|
64
|
-
: [];
|
|
65
|
-
|
|
66
|
-
return {
|
|
67
|
-
kind: 'extension' as const,
|
|
68
|
-
id: 'test-extension',
|
|
69
|
-
version: '0.0.1',
|
|
70
|
-
familyId: 'sql' as const,
|
|
71
|
-
targetId: 'postgres' as const,
|
|
72
|
-
codecs: () => codecRegistry,
|
|
73
|
-
queryOperations: () => operationsArray,
|
|
74
|
-
parameterizedCodecs: () => [],
|
|
75
|
-
create() {
|
|
76
|
-
return {
|
|
77
|
-
familyId: 'sql' as const,
|
|
78
|
-
targetId: 'postgres' as const,
|
|
79
|
-
};
|
|
80
|
-
},
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function createStack(options?: {
|
|
85
|
-
extensionPacks?: ReadonlyArray<SqlRuntimeExtensionDescriptor<'postgres'>>;
|
|
86
|
-
}): SqlExecutionStack<'postgres'> {
|
|
87
|
-
return {
|
|
88
|
-
target: createTestTargetDescriptor(),
|
|
89
|
-
adapter: createTestAdapterDescriptor(createStubAdapter()),
|
|
90
|
-
extensionPacks: options?.extensionPacks ?? [],
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
describe('createExecutionContext', () => {
|
|
95
|
-
it('creates context with adapter codecs from descriptor', () => {
|
|
96
|
-
const context = createExecutionContext({
|
|
97
|
-
contract: testContract,
|
|
98
|
-
stack: createStack(),
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
expect(context.contract).toBe(testContract);
|
|
102
|
-
expect(context.codecs.has('pg/int4@1')).toBe(true);
|
|
103
|
-
expect(context.queryOperations).toBeDefined();
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it('creates context with empty extension packs', () => {
|
|
107
|
-
const context = createExecutionContext({
|
|
108
|
-
contract: testContract,
|
|
109
|
-
stack: createStack({ extensionPacks: [] }),
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
expect(context.codecs.has('pg/int4@1')).toBe(true);
|
|
113
|
-
expect(context.codecs.has('test/ext@1')).toBe(false);
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it('registers extension codecs from descriptors', () => {
|
|
117
|
-
const context = createExecutionContext({
|
|
118
|
-
contract: testContract,
|
|
119
|
-
stack: createStack({
|
|
120
|
-
extensionPacks: [createTestExtensionDescriptor({ hasCodecs: true })],
|
|
121
|
-
}),
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
expect(context.codecs.has('pg/int4@1')).toBe(true);
|
|
125
|
-
expect(context.codecs.has('test/ext@1')).toBe(true);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it('registers extension operations from descriptors', () => {
|
|
129
|
-
const context = createExecutionContext({
|
|
130
|
-
contract: testContract,
|
|
131
|
-
stack: createStack({
|
|
132
|
-
extensionPacks: [createTestExtensionDescriptor({ hasOperations: true })],
|
|
133
|
-
}),
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
const entries = context.queryOperations.entries();
|
|
137
|
-
expect(entries['testOp']).toBeDefined();
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
it('handles extension with no contributions', () => {
|
|
141
|
-
const context = createExecutionContext({
|
|
142
|
-
contract: testContract,
|
|
143
|
-
stack: createStack({
|
|
144
|
-
extensionPacks: [createTestExtensionDescriptor({ hasCodecs: false, hasOperations: false })],
|
|
145
|
-
}),
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
expect(context.codecs.has('pg/int4@1')).toBe(true);
|
|
149
|
-
expect(context.codecs.has('test/ext@1')).toBe(false);
|
|
150
|
-
});
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
describe('comprehensive descriptor-based derivation', () => {
|
|
154
|
-
it('includes all expected codec IDs and operations from target, adapter, and extensions', () => {
|
|
155
|
-
const targetCodecRegistry = createCodecRegistry();
|
|
156
|
-
targetCodecRegistry.register(
|
|
157
|
-
codec({
|
|
158
|
-
typeId: 'target/special@1',
|
|
159
|
-
targetTypes: ['special'],
|
|
160
|
-
encode: (v: string) => v,
|
|
161
|
-
decode: (w: string) => w,
|
|
162
|
-
}),
|
|
163
|
-
);
|
|
164
|
-
|
|
165
|
-
const targetOps: SqlOperationDescriptor[] = [
|
|
166
|
-
{
|
|
167
|
-
method: 'targetOp',
|
|
168
|
-
args: [{ codecId: 'target/special@1', nullable: false }],
|
|
169
|
-
returns: { codecId: 'target/special@1', nullable: false },
|
|
170
|
-
lowering: {
|
|
171
|
-
targetFamily: 'sql' as const,
|
|
172
|
-
strategy: 'function' as const,
|
|
173
|
-
template: 'target_fn()',
|
|
174
|
-
},
|
|
175
|
-
},
|
|
176
|
-
];
|
|
177
|
-
|
|
178
|
-
const target: SqlRuntimeTargetDescriptor<'postgres'> = {
|
|
179
|
-
kind: 'target' as const,
|
|
180
|
-
id: 'postgres',
|
|
181
|
-
version: '0.0.1',
|
|
182
|
-
familyId: 'sql' as const,
|
|
183
|
-
targetId: 'postgres' as const,
|
|
184
|
-
codecs: () => targetCodecRegistry,
|
|
185
|
-
queryOperations: () => targetOps,
|
|
186
|
-
parameterizedCodecs: () => [],
|
|
187
|
-
create() {
|
|
188
|
-
return { familyId: 'sql' as const, targetId: 'postgres' as const };
|
|
189
|
-
},
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
const stack: SqlExecutionStack<'postgres'> = {
|
|
193
|
-
target,
|
|
194
|
-
adapter: createTestAdapterDescriptor(createStubAdapter()),
|
|
195
|
-
extensionPacks: [createTestExtensionDescriptor({ hasCodecs: true, hasOperations: true })],
|
|
196
|
-
};
|
|
197
|
-
|
|
198
|
-
const context = createExecutionContext({ contract: testContract, stack });
|
|
199
|
-
|
|
200
|
-
expect(context.codecs.has('target/special@1')).toBe(true);
|
|
201
|
-
expect(context.codecs.has('pg/int4@1')).toBe(true);
|
|
202
|
-
expect(context.codecs.has('test/ext@1')).toBe(true);
|
|
203
|
-
|
|
204
|
-
const entries = context.queryOperations.entries();
|
|
205
|
-
expect(entries['targetOp']).toBeDefined();
|
|
206
|
-
expect(entries['testOp']).toBeDefined();
|
|
207
|
-
});
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
describe('context.types presence', () => {
|
|
211
|
-
it('exists as empty object when no parameterized codecs are registered', () => {
|
|
212
|
-
const context = createExecutionContext({
|
|
213
|
-
contract: testContract,
|
|
214
|
-
stack: createStack(),
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
expect(context.types).toBeDefined();
|
|
218
|
-
expect(context.types).toEqual({});
|
|
219
|
-
});
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
describe('contract/stack validation errors', () => {
|
|
223
|
-
it('throws RUNTIME.CONTRACT_FAMILY_MISMATCH when contract targetFamily differs from stack', () => {
|
|
224
|
-
const mismatchedFamilyContract = {
|
|
225
|
-
...testContract,
|
|
226
|
-
targetFamily: 'document',
|
|
227
|
-
} as unknown as Contract<SqlStorage>;
|
|
228
|
-
|
|
229
|
-
expect(() =>
|
|
230
|
-
createExecutionContext({ contract: mismatchedFamilyContract, stack: createStack() }),
|
|
231
|
-
).toThrow(
|
|
232
|
-
expect.objectContaining({
|
|
233
|
-
code: 'RUNTIME.CONTRACT_FAMILY_MISMATCH',
|
|
234
|
-
category: 'RUNTIME',
|
|
235
|
-
severity: 'error',
|
|
236
|
-
details: {
|
|
237
|
-
actual: 'document',
|
|
238
|
-
expected: 'sql',
|
|
239
|
-
},
|
|
240
|
-
}),
|
|
241
|
-
);
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
it('throws RUNTIME.CONTRACT_TARGET_MISMATCH when contract target differs from stack', () => {
|
|
245
|
-
const mismatchedContract: Contract<SqlStorage> = {
|
|
246
|
-
...testContract,
|
|
247
|
-
target: 'mysql',
|
|
248
|
-
};
|
|
249
|
-
|
|
250
|
-
expect(() =>
|
|
251
|
-
createExecutionContext({ contract: mismatchedContract, stack: createStack() }),
|
|
252
|
-
).toThrow(
|
|
253
|
-
expect.objectContaining({
|
|
254
|
-
code: 'RUNTIME.CONTRACT_TARGET_MISMATCH',
|
|
255
|
-
category: 'RUNTIME',
|
|
256
|
-
severity: 'error',
|
|
257
|
-
details: {
|
|
258
|
-
actual: 'mysql',
|
|
259
|
-
expected: 'postgres',
|
|
260
|
-
},
|
|
261
|
-
}),
|
|
262
|
-
);
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
it('throws RUNTIME.MISSING_EXTENSION_PACK when contract requires extension not in stack', () => {
|
|
266
|
-
const contractWithExtension: Contract<SqlStorage> = {
|
|
267
|
-
...testContract,
|
|
268
|
-
extensionPacks: {
|
|
269
|
-
'required-extension': { id: 'required-extension', version: '1.0.0', capabilities: {} },
|
|
270
|
-
},
|
|
271
|
-
};
|
|
272
|
-
|
|
273
|
-
expect(() =>
|
|
274
|
-
createExecutionContext({ contract: contractWithExtension, stack: createStack() }),
|
|
275
|
-
).toThrow(
|
|
276
|
-
expect.objectContaining({
|
|
277
|
-
code: 'RUNTIME.MISSING_EXTENSION_PACK',
|
|
278
|
-
category: 'RUNTIME',
|
|
279
|
-
severity: 'error',
|
|
280
|
-
details: {
|
|
281
|
-
packIds: ['required-extension'],
|
|
282
|
-
},
|
|
283
|
-
}),
|
|
284
|
-
);
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
it('lists all missing extension packs in a single error', () => {
|
|
288
|
-
const contractWithExtensions: Contract<SqlStorage> = {
|
|
289
|
-
...testContract,
|
|
290
|
-
extensionPacks: {
|
|
291
|
-
'ext-a': { id: 'ext-a', version: '1.0.0', capabilities: {} },
|
|
292
|
-
'ext-b': { id: 'ext-b', version: '1.0.0', capabilities: {} },
|
|
293
|
-
},
|
|
294
|
-
};
|
|
295
|
-
|
|
296
|
-
expect(() =>
|
|
297
|
-
createExecutionContext({ contract: contractWithExtensions, stack: createStack() }),
|
|
298
|
-
).toThrow(
|
|
299
|
-
expect.objectContaining({
|
|
300
|
-
code: 'RUNTIME.MISSING_EXTENSION_PACK',
|
|
301
|
-
details: {
|
|
302
|
-
packIds: expect.arrayContaining(['ext-a', 'ext-b']),
|
|
303
|
-
},
|
|
304
|
-
}),
|
|
305
|
-
);
|
|
306
|
-
});
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
describe('applyMutationDefaults', () => {
|
|
310
|
-
const contractWithDefaults: Contract<SqlStorage> = {
|
|
311
|
-
...testContract,
|
|
312
|
-
storage: {
|
|
313
|
-
storageHash: coreHash('sha256:test'),
|
|
314
|
-
tables: {
|
|
315
|
-
user: {
|
|
316
|
-
columns: {
|
|
317
|
-
id: { nativeType: 'text', codecId: 'pg/text@1', nullable: false },
|
|
318
|
-
slug: { nativeType: 'text', codecId: 'pg/text@1', nullable: false },
|
|
319
|
-
},
|
|
320
|
-
uniques: [],
|
|
321
|
-
indexes: [],
|
|
322
|
-
foreignKeys: [],
|
|
323
|
-
},
|
|
324
|
-
},
|
|
325
|
-
},
|
|
326
|
-
execution: {
|
|
327
|
-
executionHash: executionHash('sha256:test'),
|
|
328
|
-
mutations: {
|
|
329
|
-
defaults: [
|
|
330
|
-
{
|
|
331
|
-
ref: { table: 'user', column: 'id' },
|
|
332
|
-
onCreate: { kind: 'generator', id: 'nanoid', params: { size: 8 } },
|
|
333
|
-
},
|
|
334
|
-
{
|
|
335
|
-
ref: { table: 'user', column: 'slug' },
|
|
336
|
-
onUpdate: { kind: 'generator', id: 'nanoid', params: { size: 6 } },
|
|
337
|
-
},
|
|
338
|
-
],
|
|
339
|
-
},
|
|
340
|
-
},
|
|
341
|
-
};
|
|
342
|
-
|
|
343
|
-
it('applies create defaults with generator params', () => {
|
|
344
|
-
const context = createExecutionContext({
|
|
345
|
-
contract: contractWithDefaults,
|
|
346
|
-
stack: createStack(),
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
const applied = context.applyMutationDefaults({
|
|
350
|
-
op: 'create',
|
|
351
|
-
table: 'user',
|
|
352
|
-
values: {},
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
expect(applied).toEqual([
|
|
356
|
-
{
|
|
357
|
-
column: 'id',
|
|
358
|
-
value: expect.any(String),
|
|
359
|
-
},
|
|
360
|
-
]);
|
|
361
|
-
expect((applied[0]?.value as string).length).toBe(8);
|
|
362
|
-
});
|
|
363
|
-
|
|
364
|
-
it('applies update defaults from onUpdate', () => {
|
|
365
|
-
const context = createExecutionContext({
|
|
366
|
-
contract: contractWithDefaults,
|
|
367
|
-
stack: createStack(),
|
|
368
|
-
});
|
|
369
|
-
|
|
370
|
-
const applied = context.applyMutationDefaults({
|
|
371
|
-
op: 'update',
|
|
372
|
-
table: 'user',
|
|
373
|
-
values: {},
|
|
374
|
-
});
|
|
375
|
-
|
|
376
|
-
expect(applied).toEqual([
|
|
377
|
-
{
|
|
378
|
-
column: 'slug',
|
|
379
|
-
value: expect.any(String),
|
|
380
|
-
},
|
|
381
|
-
]);
|
|
382
|
-
expect((applied[0]?.value as string).length).toBe(6);
|
|
383
|
-
});
|
|
384
|
-
});
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import type { Contract, ExecutionPlan } from '@prisma-next/contract/types';
|
|
2
|
-
import { coreHash, profileHash } from '@prisma-next/contract/types';
|
|
3
|
-
import type { SqlStorage } from '@prisma-next/sql-contract/types';
|
|
4
|
-
import type { AdapterProfile } from '@prisma-next/sql-relational-core/ast';
|
|
5
|
-
import { describe, expect, it } from 'vitest';
|
|
6
|
-
import { SqlFamilyAdapter } from '../src/sql-family-adapter';
|
|
7
|
-
|
|
8
|
-
// Minimal test contract
|
|
9
|
-
const testContract: Contract<SqlStorage> = {
|
|
10
|
-
targetFamily: 'sql',
|
|
11
|
-
target: 'postgres',
|
|
12
|
-
profileHash: profileHash('sha256:test-hash'),
|
|
13
|
-
models: {},
|
|
14
|
-
roots: {},
|
|
15
|
-
storage: { storageHash: coreHash('sha256:test-hash'), tables: {} },
|
|
16
|
-
extensionPacks: {},
|
|
17
|
-
capabilities: {},
|
|
18
|
-
meta: {},
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
const testProfile: AdapterProfile = {
|
|
22
|
-
id: 'test/default@1',
|
|
23
|
-
target: 'postgres',
|
|
24
|
-
capabilities: {},
|
|
25
|
-
codecs: () => {
|
|
26
|
-
throw new Error('not needed in test');
|
|
27
|
-
},
|
|
28
|
-
readMarkerStatement: () => ({
|
|
29
|
-
sql: 'SELECT core_hash, profile_hash FROM prisma_contract.marker WHERE id = $1',
|
|
30
|
-
params: [1],
|
|
31
|
-
}),
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
describe('SqlFamilyAdapter', () => {
|
|
35
|
-
it('creates adapter with contract and marker reader', () => {
|
|
36
|
-
const adapter = new SqlFamilyAdapter(testContract, testProfile);
|
|
37
|
-
|
|
38
|
-
expect(adapter.contract).toBe(testContract);
|
|
39
|
-
expect(adapter.markerReader).toBeDefined();
|
|
40
|
-
expect(adapter.markerReader.readMarkerStatement).toBeDefined();
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('delegates readMarkerStatement to adapter profile', () => {
|
|
44
|
-
const adapter = new SqlFamilyAdapter(testContract, testProfile);
|
|
45
|
-
const stmt = adapter.markerReader.readMarkerStatement();
|
|
46
|
-
|
|
47
|
-
expect(stmt.sql).toContain('prisma_contract.marker');
|
|
48
|
-
expect(stmt.params).toEqual([1]);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('validates plan with matching target and hash', () => {
|
|
52
|
-
const adapter = new SqlFamilyAdapter(testContract, testProfile);
|
|
53
|
-
const plan: ExecutionPlan = {
|
|
54
|
-
meta: {
|
|
55
|
-
target: 'postgres',
|
|
56
|
-
storageHash: 'sha256:test-hash',
|
|
57
|
-
lane: 'sql',
|
|
58
|
-
paramDescriptors: [],
|
|
59
|
-
},
|
|
60
|
-
sql: 'SELECT 1',
|
|
61
|
-
params: [],
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
// Should not throw
|
|
65
|
-
expect(() => adapter.validatePlan(plan, testContract)).not.toThrow();
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('throws on plan target mismatch', () => {
|
|
69
|
-
const adapter = new SqlFamilyAdapter(testContract, testProfile);
|
|
70
|
-
const plan: ExecutionPlan = {
|
|
71
|
-
meta: {
|
|
72
|
-
target: 'mysql', // Wrong target
|
|
73
|
-
storageHash: 'sha256:test-hash',
|
|
74
|
-
lane: 'sql',
|
|
75
|
-
paramDescriptors: [],
|
|
76
|
-
},
|
|
77
|
-
sql: 'SELECT 1',
|
|
78
|
-
params: [],
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
expect(() => adapter.validatePlan(plan, testContract)).toThrow(
|
|
82
|
-
'Plan target does not match runtime target',
|
|
83
|
-
);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it('throws on plan storageHash mismatch', () => {
|
|
87
|
-
const adapter = new SqlFamilyAdapter(testContract, testProfile);
|
|
88
|
-
const plan: ExecutionPlan = {
|
|
89
|
-
meta: {
|
|
90
|
-
target: 'postgres',
|
|
91
|
-
storageHash: 'sha256:different-hash', // Wrong hash
|
|
92
|
-
lane: 'sql',
|
|
93
|
-
paramDescriptors: [],
|
|
94
|
-
},
|
|
95
|
-
sql: 'SELECT 1',
|
|
96
|
-
params: [],
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
expect(() => adapter.validatePlan(plan, testContract)).toThrow(
|
|
100
|
-
'Plan storage hash does not match runtime contract',
|
|
101
|
-
);
|
|
102
|
-
});
|
|
103
|
-
});
|