|
1 | 1 | // @ts-check |
2 | 2 |
|
3 | | -import { describe, it, expect } from "vitest"; |
4 | | -import { testGitHubRESTAPI, testGitHubGraphQLAPI, testCopilotCLI, testAnthropicAPI, testOpenAIAPI, testBraveSearchAPI, testNotionAPI, generateMarkdownReport, isForkRepository, statusEmoji } from "./validate_secrets.cjs"; |
| 3 | +import { describe, it, expect, vi, afterEach } from "vitest"; |
| 4 | +import https from "https"; |
| 5 | +import { EventEmitter } from "events"; |
| 6 | +import { |
| 7 | + makePostRequest, |
| 8 | + testGitHubRESTAPI, |
| 9 | + testGitHubGraphQLAPI, |
| 10 | + testCopilotCLI, |
| 11 | + testAnthropicAPI, |
| 12 | + testOpenAIAPI, |
| 13 | + testBraveSearchAPI, |
| 14 | + testNotionAPI, |
| 15 | + generateMarkdownReport, |
| 16 | + isForkRepository, |
| 17 | + statusEmoji, |
| 18 | + Status, |
| 19 | +} from "./validate_secrets.cjs"; |
5 | 20 |
|
6 | 21 | describe("validate_secrets", () => { |
7 | 22 | describe("testGitHubRESTAPI", () => { |
@@ -72,6 +87,69 @@ describe("validate_secrets", () => { |
72 | 87 | }); |
73 | 88 | }); |
74 | 89 |
|
| 90 | + describe("makePostRequest", () => { |
| 91 | + /** @type {EventEmitter & {setTimeout: any, destroy: any, write: any, end: any}} */ |
| 92 | + let mockRequest; |
| 93 | + /** @type {EventEmitter & {statusCode: number}} */ |
| 94 | + let mockResponse; |
| 95 | + |
| 96 | + afterEach(() => { |
| 97 | + vi.restoreAllMocks(); |
| 98 | + }); |
| 99 | + |
| 100 | + function setupHttpsMock(onEnd) { |
| 101 | + mockResponse = Object.assign(new EventEmitter(), { statusCode: 200 }); |
| 102 | + mockRequest = Object.assign(new EventEmitter(), { |
| 103 | + setTimeout: vi.fn().mockImplementation((ms, cb) => { |
| 104 | + mockRequest._timeoutCb = cb; |
| 105 | + }), |
| 106 | + destroy: vi.fn(), |
| 107 | + write: vi.fn(), |
| 108 | + end: vi.fn().mockImplementation(() => { |
| 109 | + if (onEnd) onEnd(); |
| 110 | + }), |
| 111 | + }); |
| 112 | + vi.spyOn(https, "request").mockImplementation((_options, callback) => { |
| 113 | + process.nextTick(() => callback && callback(/** @type {any} */ mockResponse)); |
| 114 | + return /** @type {any} */ mockRequest; |
| 115 | + }); |
| 116 | + } |
| 117 | + |
| 118 | + it("resolves with statusCode and data on success", async () => { |
| 119 | + setupHttpsMock(() => { |
| 120 | + process.nextTick(() => { |
| 121 | + mockResponse.emit("data", '{"ok":true}'); |
| 122 | + mockResponse.emit("end"); |
| 123 | + }); |
| 124 | + }); |
| 125 | + |
| 126 | + const result = await makePostRequest("api.example.com", "/v1/test", { "Content-Type": "application/json" }, '{"query":"test"}'); |
| 127 | + expect(result.statusCode).toBe(200); |
| 128 | + expect(result.data).toBe('{"ok":true}'); |
| 129 | + }); |
| 130 | + |
| 131 | + it("rejects on request error", async () => { |
| 132 | + setupHttpsMock(null); |
| 133 | + const networkError = new Error("connection refused"); |
| 134 | + |
| 135 | + const promise = makePostRequest("api.example.com", "/v1/test", {}, "{}"); |
| 136 | + process.nextTick(() => mockRequest.emit("error", networkError)); |
| 137 | + |
| 138 | + await expect(promise).rejects.toThrow("connection refused"); |
| 139 | + }); |
| 140 | + |
| 141 | + it("rejects with timeout error after 10 s", async () => { |
| 142 | + setupHttpsMock(null); |
| 143 | + |
| 144 | + const promise = makePostRequest("api.example.com", "/v1/test", {}, "{}"); |
| 145 | + // Trigger the timeout callback registered via req.setTimeout |
| 146 | + process.nextTick(() => mockRequest._timeoutCb && mockRequest._timeoutCb()); |
| 147 | + |
| 148 | + await expect(promise).rejects.toThrow("Request timeout"); |
| 149 | + expect(mockRequest.destroy).toHaveBeenCalled(); |
| 150 | + }); |
| 151 | + }); |
| 152 | + |
75 | 153 | describe("generateMarkdownReport", () => { |
76 | 154 | it("should generate a report with summary and detailed results", () => { |
77 | 155 | const results = [ |
@@ -210,6 +288,21 @@ describe("validate_secrets", () => { |
210 | 288 | expect(report).toContain("GitHub REST API"); |
211 | 289 | expect(report).toContain("GitHub GraphQL API"); |
212 | 290 | }); |
| 291 | + |
| 292 | + it("generateMarkdownReport matches snapshot", () => { |
| 293 | + vi.useFakeTimers(); |
| 294 | + vi.setSystemTime(new Date("2024-01-15T12:00:00.000Z")); |
| 295 | + try { |
| 296 | + const results = [ |
| 297 | + { secret: "GH_AW_GITHUB_TOKEN", test: "REST API", status: "failure", message: "401" }, |
| 298 | + { secret: "ANTHROPIC_API_KEY", test: "Anthropic", status: "not_set", message: "not set" }, |
| 299 | + { secret: "BRAVE_API_KEY", test: "Brave", status: "success", message: "OK" }, |
| 300 | + ]; |
| 301 | + expect(generateMarkdownReport(results)).toMatchSnapshot(); |
| 302 | + } finally { |
| 303 | + vi.useRealTimers(); |
| 304 | + } |
| 305 | + }); |
213 | 306 | }); |
214 | 307 |
|
215 | 308 | describe("isForkRepository", () => { |
@@ -244,19 +337,19 @@ describe("validate_secrets", () => { |
244 | 337 |
|
245 | 338 | describe("statusEmoji", () => { |
246 | 339 | it("should return ✅ for success", () => { |
247 | | - expect(statusEmoji("success")).toBe("✅"); |
| 340 | + expect(statusEmoji(Status.SUCCESS)).toBe("✅"); |
248 | 341 | }); |
249 | 342 |
|
250 | 343 | it("should return ❌ for failure", () => { |
251 | | - expect(statusEmoji("failure")).toBe("❌"); |
| 344 | + expect(statusEmoji(Status.FAILURE)).toBe("❌"); |
252 | 345 | }); |
253 | 346 |
|
254 | 347 | it("should return ⚪ for not_set", () => { |
255 | | - expect(statusEmoji("not_set")).toBe("⚪"); |
| 348 | + expect(statusEmoji(Status.NOT_SET)).toBe("⚪"); |
256 | 349 | }); |
257 | 350 |
|
258 | 351 | it("should return ⏭️ for skipped", () => { |
259 | | - expect(statusEmoji("skipped")).toBe("⏭️"); |
| 352 | + expect(statusEmoji(Status.SKIPPED)).toBe("⏭️"); |
260 | 353 | }); |
261 | 354 |
|
262 | 355 | it("should return ❓ for unknown status", () => { |
|
0 commit comments