183 lines · 5.5 KB · TypeScript
RawDownload
import { describe, expect, it } from "@effect/vitest";import { Effect } from "effect"; import { OpenApiExtractionError } from "./errors";import { previewSpecText, previewSpecTextStreaming } from "./preview";import type { SpecPreview } from "./preview"; // Streamable block-YAML fixture: parameter/schema `$ref`s, a path-level// parameter, a deprecated operation, top-level security, and a transitive// schema chain (WidgetList → Widget → Owner) for the response-field walk.const fixture = `openapi: 3.0.4info:  title: Streamed Fixture  version: 1.2.3  description: Fixture for streaming preview parityservers:  - url: https://api.example.com/v1security:  - appAuth: []paths:  /widgets:    get:      operationId: widgets.list      summary: List widgets      tags:        - widgets      parameters:        - $ref: '#/components/parameters/PageSize'      responses:        "200":          description: OK          content:            application/json:              schema:                $ref: '#/components/schemas/WidgetList'    post:      operationId: widgets.create      summary: Create a widget      tags:        - widgets      requestBody:        content:          application/json:            schema:              $ref: '#/components/schemas/Widget'      responses:        "201":          description: Created          content:            application/json:              schema:                $ref: '#/components/schemas/Widget'  /widgets/{widget-id}:    parameters:      - name: widget-id        in: path        required: true        schema:          type: string    get:      operationId: widgets.get      deprecated: true      tags:        - widgets        - detail      responses:        "200":          description: OK          content:            application/json:              schema:                $ref: '#/components/schemas/Widget'components:  parameters:    PageSize:      name: pageSize      in: query      required: false      description: Page size      schema:        type: integer  securitySchemes:    appAuth:      type: oauth2      flows:        authorizationCode:          authorizationUrl: https://auth.example.com/authorize          tokenUrl: https://auth.example.com/token          scopes:            widgets.read: Read widgets  schemas:    WidgetList:      type: object      properties:        items:          type: array          items:            $ref: '#/components/schemas/Widget'    Widget:      type: object      properties:        id:          type: string        owner:          $ref: '#/components/schemas/Owner'    Owner:      type: object      properties:        name:          type: string`; const sortedOperations = (preview: SpecPreview) =>  [...preview.operations].sort((a, b) =>    `${a.operationId}:${a.method}`.localeCompare(`${b.operationId}:${b.method}`),  ); describe("previewSpecTextStreaming", () => {  it.effect("matches the whole-document preview on a streamable spec", () =>    Effect.gen(function* () {      const whole = yield* previewSpecText(fixture);      const streamed = yield* previewSpecTextStreaming(fixture);       expect(streamed.title).toEqual(whole.title);      expect(streamed.description).toEqual(whole.description);      expect(streamed.version).toEqual(whole.version);      expect(streamed.servers).toEqual(whole.servers);      expect(streamed.operationCount).toBe(whole.operationCount);      expect(streamed.tags).toEqual(whole.tags);      expect(streamed.securitySchemes).toEqual(whole.securitySchemes);      expect(streamed.authStrategies).toEqual(whole.authStrategies);      expect(streamed.headerPresets).toEqual(whole.headerPresets);      expect(streamed.oauth2Presets).toEqual(whole.oauth2Presets);      expect(streamed.healthCheckCandidates).toEqual(whole.healthCheckCandidates);      expect(sortedOperations(streamed)).toEqual(sortedOperations(whole));    }),  );   it.effect("projects response fields through the transitive schema closure", () =>    Effect.gen(function* () {      const streamed = yield* previewSpecTextStreaming(fixture);       const listCandidate = streamed.healthCheckCandidates.find(        (candidate) => candidate.method === "get" && candidate.operation.endsWith("list"),      );      expect(listCandidate).toBeDefined();      expect(listCandidate!.responseFields ?? []).not.toHaveLength(0);    }),  );   it.effect("applies the keep filter to counts, tags, and candidates", () =>    Effect.gen(function* () {      const streamed = yield* previewSpecTextStreaming(fixture, (path, pathItem) =>        path === "/widgets/{widget-id}" ? null : pathItem,      );       expect(streamed.operationCount).toBe(2);      expect(streamed.tags).toEqual(["widgets"]);      expect(        streamed.operations.every((operation) => operation.operationId !== "widgets.get"),      ).toBe(true);      expect(        streamed.healthCheckCandidates.every((candidate) => candidate.method !== "delete"),      ).toBe(true);      expect(streamed.healthCheckCandidates).toHaveLength(2);    }),  );   it.effect("fails cleanly on a spec outside the streamable profile", () =>    Effect.gen(function* () {      const error = yield* previewSpecTextStreaming(        JSON.stringify({          openapi: "3.1.0",          info: { title: "Inline", version: "1.0.0" },          paths: {},        }),      ).pipe(Effect.flip);       expect(error).toBeInstanceOf(OpenApiExtractionError);      expect(error).toHaveProperty("message", expect.stringMatching(/streamable/));    }),  );});