import { describe, expect, it } from "vitest"; import { compareApis } from "../../src/core/compare"; import { inspectHar, inspectRawExchange } from "../../src/core/har"; import { collectOperations, operationExample, securityInventory, } from "../../src/core/operations"; import { parseApiDocument } from "../../src/core/parse"; import { createWorkspace } from "../../src/core/refs"; const SPEC = `openapi: 3.1.0 info: { title: T, version: '1' } servers: [{ url: "https://api.example.test" }] paths: /items/{id}: get: operationId: readItem parameters: - { name: id, in: path, required: true, schema: { type: string, example: "a'b" } } responses: "200": { description: ok, content: { application/json: { schema: { type: object, properties: { id: {type: string} } } } } } components: securitySchemes: { key: { type: apiKey, in: header, name: X-Key } } `; describe("operation derivation, comparisons and saved exchanges", () => { it("collects operations, schemes and safely quoted inert commands", () => { const document = parseApiDocument(SPEC, "openapi.yaml"); const operations = collectOperations(document.value); expect(operations.map((item) => item.key)).toEqual(["GET /items/{id}"]); const example = operationExample(createWorkspace(document), operations[0]!); expect(example.url).toContain("a'b"); expect(example.commands.curl).toContain("curl --request GET"); expect(example.commands.fetch).toContain("fetch("); expect(securityInventory(document.value)).toEqual([ { name: "key", type: "apiKey", detail: "header ยท X-Key" }, ]); }); it("reports a missing local reference without aborting generation", () => { const document = parseApiDocument( "openapi: 3.1.0\ninfo: {title: Missing, version: '1'}\npaths: {/x: {get: {responses: {'200': {description: ok, content: {application/json: {schema: {$ref: 'missing.yaml#/Result'}}}}}}}}", "openapi.yaml", ); const operation = collectOperations(document.value)[0]!; const example = operationExample(createWorkspace(document), operation); expect(example.response).toBeUndefined(); expect(example.notices.join(" ")).toMatch(/missing\.yaml/iu); }); it("classifies removed operations and additions", () => { const before = parseApiDocument(SPEC, "before.yaml"); const after = parseApiDocument( SPEC.replace(" get:", " post:"), "after.yaml", ); expect(compareApis(before, after)).toEqual( expect.arrayContaining([ expect.objectContaining({ level: "breaking", path: "GET /items/{id}" }), expect.objectContaining({ level: "non-breaking", path: "POST /items/{id}", }), ]), ); }); it("summarizes HAR and raw exchanges without exposing headers", () => { const har = inspectHar( '{"log":{"entries":[{"time":7,"request":{"method":"POST","url":"https://example.test/x","headers":[{"name":"Authorization","value":"secret"}],"postData":{"text":"abc"}},"response":{"status":201,"headers":[],"content":{"size":9,"mimeType":"application/json"}}}]}}', ); expect(har[0]).toMatchObject({ method: "POST", status: 201, requestHeaders: 1, requestBytes: 3, responseBytes: 9, }); expect(JSON.stringify(har)).not.toContain("secret"); expect( inspectRawExchange( "GET /x HTTP/1.1\r\nHost: example.test\r\n\r\n--- response ---\r\nHTTP/1.1 204 No Content\r\nX-Test: yes\r\n", )[0], ).toMatchObject({ method: "GET", url: "/x", status: 204 }); }); });