Skip to content

Commit 0f26f85

Browse files
committed
more swagger tests
1 parent ead4ab0 commit 0f26f85

1 file changed

Lines changed: 104 additions & 6 deletions

File tree

backend/__tests__/actions/swagger.test.ts

Lines changed: 104 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,28 +14,126 @@ afterAll(async () => {
1414
});
1515

1616
describe("swagger", () => {
17-
test("the swagger endpoint returns metadata about the server", async () => {
17+
test("the swagger endpoint returns valid OpenAPI 3.0 metadata", async () => {
1818
const res = await fetch(url + "/api/swagger");
1919
expect(res.status).toBe(200);
2020
const response = (await res.json()) as ActionResponse<Swagger>;
2121

2222
// OpenAPI 3.0 structure
2323
expect(response.openapi).toBe("3.0.0");
2424
expect(response.info.title).toBe("actionhero");
25+
expect(response.info.version).toBeDefined();
26+
expect(response.info.license).toBeDefined();
27+
expect(response.info.description).toBeDefined();
28+
2529
expect(response.servers).toBeDefined();
30+
expect(response.servers!.length).toBeGreaterThan(0);
2631
const serverUrl = response.servers![0].url;
2732
expect(serverUrl.endsWith("/api") || serverUrl.endsWith("/api/")).toBe(
2833
true,
2934
);
35+
});
36+
37+
test("swagger documents all web actions", async () => {
38+
const res = await fetch(url + "/api/swagger");
39+
const response = (await res.json()) as ActionResponse<Swagger>;
40+
41+
// Count web actions (actions with both route and method)
42+
const webActions = api.actions.actions.filter(
43+
(action) => action.web?.route && action.web?.method,
44+
);
45+
46+
// Count unique routes (since multiple methods on same route are grouped)
47+
const uniqueRoutes = new Set(webActions.map((action) => action.web!.route));
48+
expect(Object.keys(response.paths).length).toBe(uniqueRoutes.size);
49+
50+
// Verify each web action is documented
51+
for (const action of webActions) {
52+
const path = action.web!.route;
53+
const method = action.web!.method.toLowerCase();
54+
55+
expect(response.paths[path]).toBeDefined();
56+
expect(response.paths[path]![method]).toBeDefined();
57+
expect(response.paths[path]![method]!.summary).toBe(
58+
action.description || action.name,
59+
);
60+
}
61+
});
62+
63+
test("swagger documents request bodies for actions with inputs", async () => {
64+
const res = await fetch(url + "/api/swagger");
65+
const response = (await res.json()) as ActionResponse<Swagger>;
66+
67+
// Find actions with Zod inputs
68+
const actionsWithInputs = api.actions.actions.filter(
69+
(action) => action.web?.route && action.web?.method && action.inputs,
70+
);
71+
72+
for (const action of actionsWithInputs) {
73+
const path = action.web!.route;
74+
const method = action.web!.method.toLowerCase();
75+
const pathObj = response.paths[path]![method]!;
76+
77+
expect(pathObj.requestBody).toBeDefined();
78+
expect(pathObj.requestBody!.required).toBe(true);
79+
expect(pathObj.requestBody!.content["application/json"]).toBeDefined();
80+
expect(
81+
pathObj.requestBody!.content["application/json"].schema,
82+
).toBeDefined();
83+
}
84+
});
85+
86+
test("swagger documents standard response codes", async () => {
87+
const res = await fetch(url + "/api/swagger");
88+
const response = (await res.json()) as ActionResponse<Swagger>;
89+
90+
// Check a specific endpoint (swagger itself)
91+
const swaggerPath = response.paths["/swagger"]!.get!;
92+
93+
expect(swaggerPath.responses["200"]).toBeDefined();
94+
expect(swaggerPath.responses["400"]).toBeDefined();
95+
expect(swaggerPath.responses["404"]).toBeDefined();
96+
expect(swaggerPath.responses["422"]).toBeDefined();
97+
expect(swaggerPath.responses["500"]).toBeDefined();
98+
99+
// Verify response schemas
100+
expect(
101+
swaggerPath.responses["200"]!.content["application/json"].schema,
102+
).toBeDefined();
103+
expect(
104+
swaggerPath.responses["400"]!.content["application/json"].schema,
105+
).toBeDefined();
106+
});
107+
108+
test("swagger groups actions by tags", async () => {
109+
const res = await fetch(url + "/api/swagger");
110+
const response = (await res.json()) as ActionResponse<Swagger>;
111+
112+
// Check that actions are tagged by their namespace
113+
for (const action of api.actions.actions) {
114+
if (!action.web?.route || !action.web?.method) continue;
115+
116+
const path = action.web.route;
117+
const method = action.web.method.toLowerCase();
118+
const pathObj = response.paths[path]![method]!;
119+
120+
expect(pathObj.tags).toBeDefined();
121+
expect(pathObj.tags!.length).toBeGreaterThan(0);
30122

31-
expect(Object.keys(response.paths).length).toBeGreaterThan(2);
123+
const expectedTag = action.name.split(":")[0];
124+
expect(pathObj.tags).toContain(expectedTag);
125+
}
126+
});
127+
128+
test("swagger endpoint itself is documented", async () => {
129+
const res = await fetch(url + "/api/swagger");
130+
const response = (await res.json()) as ActionResponse<Swagger>;
32131

33-
// Check that the swagger endpoint itself is documented
34132
expect(response.paths["/swagger"]).toBeDefined();
35-
expect(response.paths["/swagger"]?.get).toBeDefined();
36-
expect(response.paths["/swagger"]?.get?.summary).toBe(
133+
expect(response.paths["/swagger"]!.get).toBeDefined();
134+
expect(response.paths["/swagger"]!.get!.summary).toBe(
37135
"Return API documentation in the OpenAPI specification",
38136
);
39-
expect(response.paths["/swagger"]?.get?.responses["200"]).toBeDefined();
137+
expect(response.paths["/swagger"]!.get!.responses["200"]).toBeDefined();
40138
});
41139
});

0 commit comments

Comments
 (0)