Skip to content

Commit 3f5da2b

Browse files
committed
Add integration test suite for GraphQL emitter
Expands the test coverage of the component-based emitter to exercise the full surface area of TypeSpec-to-GraphQL translation. New test files: - nullability.test.ts: required vs. optional vs. T | null, input and output side semantics, list element nullability - input-output-splitting.test.ts: models used as input, output, or both; verifies correct suffixing and separation - unions.test.ts: named union types, nullable variants, wrapper models - enums.test.ts: enum declarations, enum values, enum references in model properties and operations - arrays.test.ts: array element non-null semantics and nested arrays - circular-references.test.ts: self-referential and mutually recursive model definitions - deprecation.test.ts: @deprecated decorator propagation to SDL - doc-comments.test.ts: doc-comment propagation to SDL descriptions Extends e2e.test.ts beyond the single smoke test into a full scenario suite (complete API schema, custom scalars, union output types, models used only as input, mutations, empty schema handling, operation parameters, and complex SDL validation). Extends test/main.tsp with the fixture models, operations, and helpers needed by the e2e and integration tests. No production code changes — this PR is tests only.
1 parent ce4affb commit 3f5da2b

10 files changed

Lines changed: 2450 additions & 20 deletions
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { strictEqual } from "node:assert";
2+
import { describe, it } from "vitest";
3+
import { emitSingleSchema } from "./test-host.js";
4+
5+
describe("arrays", () => {
6+
it("supports array types", async () => {
7+
const code = `
8+
@schema
9+
namespace TestNamespace {
10+
model Tag {
11+
name: string;
12+
color: string;
13+
}
14+
15+
model Article {
16+
id: string;
17+
title: string;
18+
tags: Tag[];
19+
categories: string[];
20+
}
21+
22+
@query
23+
op getArticle(id: string): Article;
24+
25+
@query
26+
op listArticles(): Article[];
27+
}
28+
`;
29+
30+
const result = await emitSingleSchema(code, {});
31+
32+
strictEqual(result.includes("tags: [Tag!]!"), true);
33+
strictEqual(result.includes("categories: [String!]!"), true);
34+
strictEqual(result.includes("listArticles: [Article!]"), true);
35+
});
36+
37+
it("emits list types for array properties", async () => {
38+
const code = `
39+
@schema
40+
namespace TestNamespace {
41+
model User {
42+
id: string;
43+
tags: string[];
44+
}
45+
46+
@query
47+
op getUser(): User;
48+
}
49+
`;
50+
51+
const result = await emitSingleSchema(code, {});
52+
strictEqual(result.includes("tags: [String!]!"), true);
53+
});
54+
55+
it("emits nullable list items for optional element types", async () => {
56+
const code = `
57+
@schema
58+
namespace TestNamespace {
59+
model User {
60+
id: string;
61+
tags: (string | null)[];
62+
}
63+
64+
@query
65+
op getUser(): User;
66+
}
67+
`;
68+
69+
const result = await emitSingleSchema(code, {});
70+
strictEqual(result.includes("tags: [String]!"), true);
71+
});
72+
});
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { strictEqual } from "node:assert";
2+
import { describe, it } from "vitest";
3+
import { emitSingleSchema } from "./test-host.js";
4+
5+
describe("circular references", () => {
6+
it("handles circular references between models", async () => {
7+
const code = `
8+
@schema
9+
namespace TestNamespace {
10+
model User {
11+
id: string;
12+
name: string;
13+
posts: Post[];
14+
}
15+
16+
model Post {
17+
id: string;
18+
title: string;
19+
author: User;
20+
comments: Comment[];
21+
}
22+
23+
model Comment {
24+
id: string;
25+
text: string;
26+
author: User;
27+
post: Post;
28+
}
29+
30+
@query
31+
op getUser(id: string): User;
32+
33+
@query
34+
op getPost(id: string): Post;
35+
}
36+
`;
37+
38+
const result = await emitSingleSchema(code, {});
39+
40+
strictEqual(result.includes("type User {"), true);
41+
strictEqual(result.includes("posts: [Post!]!"), true);
42+
strictEqual(result.includes("type Post {"), true);
43+
strictEqual(result.includes("author: User!"), true);
44+
strictEqual(result.includes("comments: [Comment!]!"), true);
45+
strictEqual(result.includes("type Comment {"), true);
46+
});
47+
});
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { strictEqual } from "node:assert";
2+
import { describe, it } from "vitest";
3+
import { emitSingleSchema } from "./test-host.js";
4+
5+
describe("deprecation", () => {
6+
it("supports @deprecated directive", async () => {
7+
const code = `
8+
@schema
9+
namespace TestNamespace {
10+
model User {
11+
id: string;
12+
name: string;
13+
#deprecated "Use email instead"
14+
username: string;
15+
}
16+
17+
@query
18+
op getUser(id: string): User;
19+
20+
#deprecated "Use getUserById instead"
21+
@query
22+
op findUser(id: string): User;
23+
}
24+
`;
25+
26+
const result = await emitSingleSchema(code, {});
27+
28+
strictEqual(result.includes('@deprecated(reason: "Use email instead")'), true);
29+
strictEqual(result.includes('@deprecated(reason: "Use getUserById instead")'), true);
30+
});
31+
});
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { strictEqual } from "node:assert";
2+
import { describe, it } from "vitest";
3+
import { emitSingleSchema } from "./test-host.js";
4+
5+
describe("doc comments", () => {
6+
it("propagates doc comments to model descriptions", async () => {
7+
const code = `
8+
@schema
9+
namespace TestNamespace {
10+
/** A user in the system */
11+
model User {
12+
id: string;
13+
name: string;
14+
}
15+
16+
@query
17+
op getUser(id: string): User;
18+
}
19+
`;
20+
21+
const result = await emitSingleSchema(code, {});
22+
strictEqual(result.includes('"""A user in the system"""'), true);
23+
});
24+
25+
it("propagates doc comments to field descriptions", async () => {
26+
const code = `
27+
@schema
28+
namespace TestNamespace {
29+
model User {
30+
/** The unique identifier */
31+
id: string;
32+
/** The user's display name */
33+
name: string;
34+
}
35+
36+
@query
37+
op getUser(id: string): User;
38+
}
39+
`;
40+
41+
const result = await emitSingleSchema(code, {});
42+
strictEqual(result.includes('"""The unique identifier"""'), true);
43+
strictEqual(result.includes('"""The user\'s display name"""'), true);
44+
});
45+
46+
it("propagates doc comments to enum descriptions", async () => {
47+
const code = `
48+
@schema
49+
namespace TestNamespace {
50+
/** The role of a user */
51+
enum Role {
52+
/** Administrator with full access */
53+
Admin,
54+
/** Regular user */
55+
User,
56+
}
57+
58+
model Person {
59+
role: Role;
60+
}
61+
62+
@query
63+
op getPerson(): Person;
64+
}
65+
`;
66+
67+
const result = await emitSingleSchema(code, {});
68+
strictEqual(result.includes('"""The role of a user"""'), true);
69+
});
70+
});

0 commit comments

Comments
 (0)