Skip to content

Commit b9d6594

Browse files
Make parse function throw on validation errors (#19)
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
1 parent 3a26e71 commit b9d6594

File tree

3 files changed

+30
-55
lines changed

3 files changed

+30
-55
lines changed

.changeset/breezy-lies-shake.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@urlspec/language": minor
3+
---
4+
5+
fix: throw `parse()` when validation failed

packages/language/src/parser.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ export async function parse(
2020
await URLSpec.validation.DocumentValidator.validateDocument(document);
2121
// Manually set diagnostics on the document
2222
(document as any).diagnostics = diagnostics;
23+
24+
const errors = diagnostics.filter((d) => d.severity === 1);
25+
if (errors.length > 0) {
26+
const messages = errors.map((d) => d.message).join("\n");
27+
throw new Error(`URLSpec validation failed:\n${messages}`);
28+
}
29+
2330
return document;
2431
}
2532

packages/language/test/validation.test.ts

Lines changed: 18 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -110,18 +110,9 @@ describe("URLSpec Validation", () => {
110110

111111
describe("Path parameter validation", () => {
112112
it("should require path parameters to be declared in parameter block", async () => {
113-
const doc = await parseFile(
114-
fixture("validation-path-param-missing.urlspec"),
115-
);
116-
117-
// Should have validation error for missing job_id declaration
118-
expect(doc.diagnostics?.length ?? 0).toBeGreaterThan(0);
119-
const errors = doc.diagnostics?.filter((d) => d.severity === 1) ?? [];
120-
expect(errors.length).toBeGreaterThan(0);
121-
122-
// Check that the error message mentions the missing path parameter
123-
const hasJobIdError = errors.some((e) => e.message.includes("job_id"));
124-
expect(hasJobIdError).toBe(true);
113+
await expect(
114+
parseFile(fixture("validation-path-param-missing.urlspec")),
115+
).rejects.toThrow("job_id");
125116
});
126117

127118
it("should accept path parameters when declared in parameter block", async () => {
@@ -135,19 +126,9 @@ describe("URLSpec Validation", () => {
135126
});
136127

137128
it("should require all path parameters to be declared", async () => {
138-
const doc = await parseFile(
139-
fixture("validation-path-param-multiple-missing.urlspec"),
140-
);
141-
142-
// Should have validation error for missing comment_id declaration
143-
expect(doc.diagnostics?.length ?? 0).toBeGreaterThan(0);
144-
const errors = doc.diagnostics?.filter((d) => d.severity === 1) ?? [];
145-
expect(errors.length).toBeGreaterThan(0);
146-
147-
const hasCommentIdError = errors.some((e) =>
148-
e.message.includes("comment_id"),
149-
);
150-
expect(hasCommentIdError).toBe(true);
129+
await expect(
130+
parseFile(fixture("validation-path-param-multiple-missing.urlspec")),
131+
).rejects.toThrow("comment_id");
151132
});
152133

153134
it("should accept multiple path parameters when all are declared", async () => {
@@ -182,47 +163,29 @@ describe("URLSpec Validation", () => {
182163
});
183164

184165
it("should reject multiple discriminants in when clauses", async () => {
185-
const doc = await parseFile(
186-
fixture("when-clauses-multiple-discriminants.urlspec"),
187-
);
188-
189-
const errors = doc.diagnostics?.filter((d) => d.severity === 1) ?? [];
190-
expect(errors.length).toBeGreaterThan(0);
191-
expect(errors.some((e) => e.message.includes("discriminant"))).toBe(true);
166+
await expect(
167+
parseFile(fixture("when-clauses-multiple-discriminants.urlspec")),
168+
).rejects.toThrow("discriminant");
192169
});
193170

194171
it("should reject duplicate when clause values", async () => {
195-
const doc = await parseFile(
196-
fixture("when-clauses-duplicate-value.urlspec"),
197-
);
198-
199-
const errors = doc.diagnostics?.filter((d) => d.severity === 1) ?? [];
200-
expect(errors.length).toBeGreaterThan(0);
201-
expect(errors.some((e) => e.message.includes("Duplicate"))).toBe(true);
172+
await expect(
173+
parseFile(fixture("when-clauses-duplicate-value.urlspec")),
174+
).rejects.toThrow("Duplicate");
202175
});
203176

204177
it("should reject discriminant declared in parameter block", async () => {
205-
const doc = await parseFile(
206-
fixture("when-clauses-discriminant-in-params.urlspec"),
207-
);
208-
209-
const errors = doc.diagnostics?.filter((d) => d.severity === 1) ?? [];
210-
expect(errors.length).toBeGreaterThan(0);
211-
expect(errors.some((e) => e.message.includes("type"))).toBe(true);
178+
await expect(
179+
parseFile(fixture("when-clauses-discriminant-in-params.urlspec")),
180+
).rejects.toThrow("type");
212181
});
213182
});
214183

215184
describe("Combined validation scenarios", () => {
216185
it("should validate path parameter declaration", async () => {
217-
const doc = await parseFile(
218-
fixture("validation-combined-invalid.urlspec"),
219-
);
220-
221-
expect(doc.diagnostics?.length ?? 0).toBeGreaterThan(0);
222-
const errors = doc.diagnostics?.filter((d) => d.severity === 1) ?? [];
223-
224-
// Should have at least 1 error: missing job_id (parameter naming is no longer restricted)
225-
expect(errors.length).toBeGreaterThanOrEqual(1);
186+
await expect(
187+
parseFile(fixture("validation-combined-invalid.urlspec")),
188+
).rejects.toThrow("URLSpec validation failed");
226189
});
227190

228191
it("should accept valid specification with all rules followed", async () => {

0 commit comments

Comments
 (0)