484 lines · 14.0 KB · TypeScript
RawDownload
import { expect, it } from "@effect/vitest";import { Effect, Option } from "effect"; import {  parseEntry,  streamOperationBindingsFromStructure,  structuralSplit,} from "@executor-js/plugin-openapi"; import { microsoftGraphKeepPathItem } from "./graph";import {  microsoftGraphExactPathsForPresetIds,  microsoftGraphPathPrefixesForPresetIds,  microsoftGraphTagPrefixesForPresetIds,} from "./presets"; // Mirrors the verbatim shape of the real Microsoft Graph v1.0 spec: every// success response uses the OpenAPI wildcard status key `2XX` (the real spec// has zero numeric 200/201 keys), drive content GET is already declared as a// binary octet-stream, PUT declares a binary octet-stream requestBody, error// responses are $refs, and path-level shared parameters carry// `x-ms-docs-key-type`.const driveContentFixture = `openapi: 3.0.4info:  title: Microsoft Graph Fixture  version: v1.0servers:  - url: https://graph.microsoft.com/v1.0paths:  /drives/{drive-id}/items/{driveItem-id}/content:    get:      tags:        - drives.driveItem      summary: Get content for the navigation property items from drives      operationId: drives.GetItemsContent      parameters:        - name: $format          in: query          description: Format of the content          style: form          explode: false          schema:            type: string      responses:        2XX:          description: Retrieved media content          content:            application/octet-stream:              schema:                type: string                format: binary        4XX:          $ref: '#/components/responses/error'        5XX:          $ref: '#/components/responses/error'    put:      tags:        - drives.driveItem      summary: Update content for the navigation property items in drives      operationId: drives.UpdateItemsContent      requestBody:        description: New media content.        content:          application/octet-stream:            schema:              type: string              format: binary        required: true      responses:        2XX:          description: Success          content:            application/json:              schema:                $ref: '#/components/schemas/microsoft.graph.driveItem'        4XX:          $ref: '#/components/responses/error'        5XX:          $ref: '#/components/responses/error'    delete:      tags:        - drives.driveItem      summary: Delete content for the navigation property items in drives      operationId: drives.DeleteItemsContent      responses:        '204':          description: Success        4XX:          $ref: '#/components/responses/error'        5XX:          $ref: '#/components/responses/error'    parameters:      - name: drive-id        in: path        description: The unique identifier of drive        required: true        schema:          type: string        x-ms-docs-key-type: drive      - name: driveItem-id        in: path        description: The unique identifier of driveItem        required: true        schema:          type: string        x-ms-docs-key-type: driveItemcomponents:  schemas:    microsoft.graph.driveItem:      type: object      properties:        id:          type: string    microsoft.graph.ODataErrors.ODataError:      type: object      properties:        error:          type: object  responses:    error:      description: error      content:        application/json:          schema:            $ref: '#/components/schemas/microsoft.graph.ODataErrors.ODataError'`; // Report-style Graph endpoints declare an octet-stream success response with an// object schema (a `value` wrapper) instead of a binary string.const reportContentFixture = `openapi: 3.0.4info:  title: Microsoft Graph Fixture  version: v1.0servers:  - url: https://graph.microsoft.com/v1.0paths:  /reports/getEmailActivityCounts(period={period}):    get:      tags:        - reports.Functions      summary: Invoke function getEmailActivityCounts      operationId: reports.getEmailActivityCounts      parameters:        - name: period          in: path          required: true          schema:            type: string      responses:        2XX:          description: Success          content:            application/octet-stream:              schema:                type: object                properties:                  value:                    type: string                    format: base64url        4XX:          $ref: '#/components/responses/error'        5XX:          $ref: '#/components/responses/error'components:  schemas:    microsoft.graph.ODataErrors.ODataError:      type: object      properties:        error:          type: object  responses:    error:      description: error      content:        application/json:          schema:            $ref: '#/components/schemas/microsoft.graph.ODataErrors.ODataError'`; const identityPathFixture = `openapi: 3.0.4info:  title: Microsoft Graph Fixture  version: v1.0servers:  - url: https://graph.microsoft.com/v1.0paths:  /me:    get:      operationId: me.GetUser      security:        - azureAdDelegated:            - User.Read      responses:        "200":          description: OK    patch:      operationId: me.UpdateUser      security:        - azureAdDelegated:            - User.ReadWrite      responses:        "200":          description: OK  /me/photo:    get:      operationId: me.photo.GetProfilePhoto      security:        - azureAdDelegated:            - User.Read      responses:        "200":          description: OK  /me/messages:    get:      operationId: me.messages.ListMessages      security:        - azureAdDelegated:            - Mail.ReadWrite      responses:        "200":          description: OKcomponents: {}`; const driveAndWorkbookFixture = `openapi: 3.0.4info:  title: Microsoft Graph Fixture  version: v1.0servers:  - url: https://graph.microsoft.com/v1.0paths:  /drives/{drive-id}/items/{driveItem-id}:    get:      operationId: drives.GetItems      responses:        "200":          description: OK  /drives/{drive-id}/items/{driveItem-id}/workbook/worksheets:    get:      operationId: drives.GetItemsWorkbookWorksheets      responses:        "200":          description: OKcomponents: {}`; const fullGraphSelection = {  coversFullGraph: true,  presetIds: [],  customScopes: [],  exactPaths: [],  pathPrefixes: [],  tagPrefixes: [],} as const; const noWorkloadSelection = {  coversFullGraph: false,  presetIds: [],  customScopes: [],  exactPaths: [],  pathPrefixes: [],  tagPrefixes: [],} as const; const profileSelection = {  coversFullGraph: false,  presetIds: ["profile"],  customScopes: [],  exactPaths: ["/me", "/me/photo", "/me/photo/$value"],  pathPrefixes: [],  tagPrefixes: [],} as const; type MicrosoftGraphKeepPathItemSelection = Parameters<typeof microsoftGraphKeepPathItem>[0]; const keptIdentityFixturePaths = (  selection: MicrosoftGraphKeepPathItemSelection,): Map<string, Record<string, unknown>> => {  const structure = structuralSplit(identityPathFixture);  expect(structure).not.toBeNull();  const keepPathItem = microsoftGraphKeepPathItem(selection);  const kept = new Map<string, Record<string, unknown>>();   for (const range of structure!.pathItems) {    const entry = parseEntry(structure!.text, range, 2);    expect(entry).not.toBeNull();    const [path, rawPathItem] = entry!;    const pathItem = keepPathItem(path, rawPathItem as Record<string, unknown>);    if (pathItem) kept.set(path, pathItem as Record<string, unknown>);  }   return kept;}; const presetSelection = (presetIds: readonly string[]): MicrosoftGraphKeepPathItemSelection => ({  coversFullGraph: false,  presetIds,  customScopes: [],  exactPaths: microsoftGraphExactPathsForPresetIds(presetIds),  pathPrefixes: microsoftGraphPathPrefixesForPresetIds(presetIds),  tagPrefixes: microsoftGraphTagPrefixesForPresetIds(presetIds),}); const keptOperationIds = (  fixture: string,  selection: MicrosoftGraphKeepPathItemSelection,): readonly string[] => {  const structure = structuralSplit(fixture);  expect(structure).not.toBeNull();  const keepPathItem = microsoftGraphKeepPathItem(selection);   return structure!.pathItems.flatMap((range) => {    const entry = parseEntry(structure!.text, range, 2);    expect(entry).not.toBeNull();    const [path, rawPathItem] = entry!;    const pathItem = keepPathItem(path, rawPathItem as Record<string, unknown>);    if (!pathItem) return [];    return Object.values(pathItem as Record<string, unknown>).flatMap((operation) => {      const operationId = (operation as { readonly operationId?: unknown }).operationId;      return typeof operationId === "string" ? [operationId] : [];    });  });}; const keptPathItem = (fixture: string): Record<string, unknown> => {  const structure = structuralSplit(fixture);  expect(structure).not.toBeNull();  const entry = parseEntry(structure!.text, structure!.pathItems[0]!, 2);  expect(entry).not.toBeNull();  const [path, rawPathItem] = entry!;  const pathItem = microsoftGraphKeepPathItem(fullGraphSelection)(    path,    rawPathItem as Record<string, unknown>,  );  expect(pathItem).not.toBeNull();  return pathItem as Record<string, unknown>;}; type StreamedBinding = {  readonly binding: {    readonly method: string;    readonly pathTemplate: string;    readonly responseBody: Option.Option<{      readonly fileHint: Option.Option<{        readonly kind: "binaryResponse" | "byteField";      }>;    }>;  };}; const streamBindings = (fixture: string) =>  Effect.gen(function* () {    const structure = structuralSplit(fixture);    expect(structure).not.toBeNull();    const chunks: StreamedBinding[] = [];    yield* streamOperationBindingsFromStructure(      structure!,      { chunkSize: 10, keepPathItem: microsoftGraphKeepPathItem(fullGraphSelection) },      (chunk) =>        Effect.sync(() => {          chunks.push(...chunk);        }),    );    return chunks;  }); const responseFileHintKind = (  chunks: readonly StreamedBinding[],  method: string,  pathTemplate: string,): string | undefined => {  const match = chunks.find(    (chunk) => chunk.binding.method === method && chunk.binding.pathTemplate === pathTemplate,  );  expect(match).toBeDefined();  const hint = Option.flatMap(match!.binding.responseBody, (body) => body.fileHint);  return Option.getOrUndefined(hint)?.kind;}; it("keeps only GET /me for identity health checks without selected workloads", () => {  const kept = keptIdentityFixturePaths(noWorkloadSelection);   expect([...kept.keys()]).toEqual(["/me"]);  expect(kept.get("/me")?.get).toMatchObject({ operationId: "me.GetUser" });  expect(kept.get("/me")?.patch).toBeUndefined();}); it("keeps all /me operations when profile selects the path", () => {  const kept = keptIdentityFixturePaths(profileSelection);   expect(kept.get("/me")?.get).toMatchObject({ operationId: "me.GetUser" });  expect(kept.get("/me")?.patch).toMatchObject({ operationId: "me.UpdateUser" });}); it("matches fresh-add ownership for overlapping files and excel drive paths", () => {  expect(keptOperationIds(driveAndWorkbookFixture, presetSelection(["files"]))).toEqual([    "drives.GetItems",    "drives.GetItemsWorkbookWorksheets",  ]);  expect(keptOperationIds(driveAndWorkbookFixture, presetSelection(["excel"]))).toEqual([    "drives.GetItemsWorkbookWorksheets",  ]);}); it("keeps already-binary drive content responses untouched", () => {  const pathItem = keptPathItem(driveContentFixture);   const get = pathItem.get as Record<string, unknown>;  const getResponses = get.responses as Record<string, unknown>;  expect(getResponses["2XX"]).toEqual({    description: "Retrieved media content",    content: {      "application/octet-stream": {        schema: { type: "string", format: "binary" },      },    },  });  expect(getResponses["4XX"]).toEqual({ $ref: "#/components/responses/error" });  expect(getResponses["5XX"]).toEqual({ $ref: "#/components/responses/error" });   // The real spec already declares the PUT requestBody as binary; the  // normalization must not touch request bodies.  const put = pathItem.put as Record<string, unknown>;  expect(put.requestBody).toEqual({    description: "New media content.",    content: {      "application/octet-stream": {        schema: { type: "string", format: "binary" },      },    },    required: true,  });  const putResponses = put.responses as Record<string, unknown>;  expect(putResponses["2XX"]).toMatchObject({    content: {      "application/json": {        schema: { $ref: "#/components/schemas/microsoft.graph.driveItem" },      },    },  });   // Path-level shared parameters survive the filter.  expect(pathItem.parameters).toMatchObject([    { name: "drive-id", "x-ms-docs-key-type": "drive" },    { name: "driveItem-id", "x-ms-docs-key-type": "driveItem" },  ]);}); it("normalizes report-style octet-stream object schemas to binary strings", () => {  const pathItem = keptPathItem(reportContentFixture);   const get = pathItem.get as Record<string, unknown>;  const responses = get.responses as Record<string, unknown>;  const success = responses["2XX"] as Record<string, unknown>;  expect(success.description).toBe("Success");  expect(success.content).toEqual({    "application/octet-stream": {      schema: { type: "string", format: "binary" },    },  });  expect(responses["4XX"]).toEqual({ $ref: "#/components/responses/error" });}); it.effect("streams drive content download bindings with a binaryResponse file hint", () =>  Effect.gen(function* () {    const chunks = yield* streamBindings(driveContentFixture);    expect(      responseFileHintKind(chunks, "get", "/drives/{drive-id}/items/{driveItem-id}/content"),    ).toBe("binaryResponse");  }),); it.effect("streams report-style download bindings with a binaryResponse file hint", () =>  Effect.gen(function* () {    const chunks = yield* streamBindings(reportContentFixture);    expect(      responseFileHintKind(chunks, "get", "/reports/getEmailActivityCounts(period={period})"),    ).toBe("binaryResponse");  }),);