Skip to content

Commit 0e0b163

Browse files
committed
test(contract-psl): assert from-to to: qualifier behaviour without member-access grammar
The `to: User.id` case is RED on a fresh tml-2940 build: the member-access value grammar that makes `Foo.bar` parse as a qualified argument value arrives in a later slice and is absent here. Without it, `parseIdentifierExpr` reads only the head identifier `User`, the resolver looks for a column `User` on the target model `User`, finds none, and rejects the relation with PSL_INVALID_ATTRIBUTE_ARGUMENT. Assert that real behaviour (failure + the unknown-field diagnostic) instead of the inherited 'qualifier dropped, @id inferred' assertion, which only held against a stale dist still carrying the grammar. The case stays present as the anchor a later slice rewrites once the dotted value is supported. Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
1 parent f26c32e commit 0e0b163

2 files changed

Lines changed: 22 additions & 32 deletions

File tree

packages/2-sql/2-authoring/contract-psl/test/fixtures.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ import { type EnumTypeHandle, enumType } from '@prisma-next/sql-contract-ts/cont
3030
import { blindCast } from '@prisma-next/utils/casts';
3131
import { createTestSqlNamespace } from '../../../1-core/contract/test/test-support';
3232

33+
export { createTestSqlNamespace } from '../../../1-core/contract/test/test-support';
34+
3335
function testEnumFactory(
3436
block: PslExtensionBlock,
3537
ctx: AuthoringEntityContext,

packages/2-sql/2-authoring/contract-psl/test/interpreter.relations.from-to.test.ts

Lines changed: 20 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { describe, expect, it } from 'vitest';
22
import { interpretPslDocumentToSqlContract } from '../src/interpreter';
33
import {
44
createBuiltinLikeControlMutationDefaults,
5+
createTestSqlNamespace,
56
modelsOf,
67
postgresScalarTypeDescriptors,
78
postgresTarget,
@@ -12,6 +13,7 @@ const baseInput = {
1213
target: postgresTarget,
1314
scalarTypeDescriptors: postgresScalarTypeDescriptors,
1415
composedExtensionContracts: new Map(),
16+
createNamespace: createTestSqlNamespace,
1517
} as const;
1618

1719
const builtinControlMutationDefaults = createBuiltinLikeControlMutationDefaults();
@@ -223,15 +225,14 @@ model Post {
223225
expect(bare.value).toEqual(bracketed.value);
224226
});
225227

226-
// The PSL expression grammar does not carry a member-access argument value:
227-
// `parseIdentifierExpr` consumes only the head identifier, so the trailing
228-
// `.field` of `to: User.id` never reaches the resolver. The named-argument
229-
// lookup then sees no plain `to:` value, treats `to:` as omitted, and infers
230-
// the referenced columns from the target's `@id` — lowering identically to
231-
// the bare unqualified spelling. This pins the present grammar boundary as a
232-
// regression anchor for a future slice that carries the dotted value.
233-
it('infers the target @id for a member-access to: value (qualifier dropped at the grammar layer)', () => {
234-
const qualified = interpret(`model User {
228+
// The PSL expression grammar carries no member-access argument value:
229+
// `parseIdentifierExpr` consumes only the head identifier, so `to: User.id`
230+
// reaches the resolver as the bare field name `User`. The resolver looks for
231+
// a column named `User` on the target model `User`, finds none, and rejects
232+
// the relation. A later slice teaches the grammar the dotted value and turns
233+
// this into the supported `to: Model.column` qualifier.
234+
it('rejects a member-access to: value (grammar reads only the head identifier)', () => {
235+
const result = interpret(`model User {
235236
id Int @id
236237
posts Post[]
237238
}
@@ -242,30 +243,17 @@ model Post {
242243
user User @relation(from: userId, to: User.id)
243244
}
244245
`);
245-
const inferred = interpret(`model User {
246-
id Int @id
247-
posts Post[]
248-
}
249-
250-
model Post {
251-
id Int @id
252-
userId Int
253-
user User @relation(from: userId)
254-
}
255-
`);
256-
257-
expect(qualified.ok).toBe(true);
258-
expect(inferred.ok).toBe(true);
259-
if (!qualified.ok || !inferred.ok) return;
260-
expect(qualified.value).toEqual(inferred.value);
261246

262-
const models = modelsOf(qualified.value) as RelationModels;
263-
expect(models['Post']?.relations).toMatchObject({
264-
user: {
265-
cardinality: 'N:1',
266-
on: { localFields: ['userId'], targetFields: ['id'] },
267-
},
268-
});
247+
expect(result.ok).toBe(false);
248+
if (result.ok) return;
249+
expect(result.failure.diagnostics).toEqual(
250+
expect.arrayContaining([
251+
expect.objectContaining({
252+
code: 'PSL_INVALID_ATTRIBUTE_ARGUMENT',
253+
message: expect.stringContaining('unknown field "User.User"'),
254+
}),
255+
]),
256+
);
269257
});
270258
});
271259

0 commit comments

Comments
 (0)