Skip to content

Commit 7e908f2

Browse files
ymc9claude
andauthored
fix(zod): use named argument lookup for @Length validation (#2434)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent b2305cd commit 7e908f2

File tree

2 files changed

+47
-4
lines changed

2 files changed

+47
-4
lines changed

packages/zod/src/utils.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@ export function addStringValidation(
3131
for (const attr of attributes) {
3232
switch (attr.name) {
3333
case '@length': {
34-
const min = getArgValue<number>(attr.args?.[0]?.value);
34+
const min = getArgValue<number>(attr.args?.find((a) => a.name === 'min')?.value);
3535
if (min !== undefined) {
3636
result = result.min(min);
3737
}
38-
const max = getArgValue<number>(attr.args?.[1]?.value);
38+
const max = getArgValue<number>(attr.args?.find((a) => a.name === 'max')?.value);
3939
if (max !== undefined) {
4040
result = result.max(max);
4141
}
@@ -248,11 +248,11 @@ export function addListValidation(
248248
let result = schema;
249249
for (const attr of attributes) {
250250
if (attr.name === '@length') {
251-
const min = getArgValue<number>(attr.args?.[0]?.value);
251+
const min = getArgValue<number>(attr.args?.find((a) => a.name === 'min')?.value);
252252
if (min !== undefined) {
253253
result = result.min(min);
254254
}
255-
const max = getArgValue<number>(attr.args?.[1]?.value);
255+
const max = getArgValue<number>(attr.args?.find((a) => a.name === 'max')?.value);
256256
if (max !== undefined) {
257257
result = result.max(max);
258258
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { createTestClient } from '@zenstackhq/testtools';
2+
import { describe, expect, it } from 'vitest';
3+
4+
// https://github.com/zenstackhq/zenstack/issues/2433
5+
describe('Regression for issue 2433', () => {
6+
it('should accept short string when only @length(max: N) is specified', async () => {
7+
const db = await createTestClient(
8+
`
9+
model Post {
10+
id String @id @default(cuid())
11+
content String @length(max: 10000)
12+
}
13+
`,
14+
);
15+
// Should succeed: short content is within max limit
16+
await expect(db.post.create({ data: { content: 'hello' } })).resolves.toMatchObject({
17+
content: 'hello',
18+
});
19+
// Should fail validation: content exceeds max
20+
await expect(
21+
db.post.create({ data: { content: 'x'.repeat(10001) } }),
22+
).toBeRejectedByValidation();
23+
});
24+
25+
it('should reject short string when only @length(min: N) is specified', async () => {
26+
const db = await createTestClient(
27+
`
28+
model Post {
29+
id String @id @default(cuid())
30+
content String @length(min: 5)
31+
}
32+
`,
33+
);
34+
// Should succeed: content meets min length
35+
await expect(db.post.create({ data: { content: 'hello world' } })).resolves.toMatchObject({
36+
content: 'hello world',
37+
});
38+
// Should fail validation: content is too short
39+
await expect(
40+
db.post.create({ data: { content: 'hi' } }),
41+
).toBeRejectedByValidation();
42+
});
43+
});

0 commit comments

Comments
 (0)