import { describe, expect, it } from "@effect/vitest";import { Effect, Layer } from "effect";import { HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http"; import { AuthTemplateSlug, ConnectionName, IntegrationSlug, createExecutor,} from "@executor-js/sdk";import { makeTestConfig, memoryCredentialsPlugin } from "@executor-js/sdk/testing"; import { microsoftPlugin, type MicrosoftPluginOptions } from "./plugin";import { MICROSOFT_AUTH_TEMPLATE_SLUG, MICROSOFT_CLIENT_CREDENTIALS_AUTH_TEMPLATE_SLUG, MICROSOFT_GRAPH_CLIENT_CREDENTIALS_SCOPES, MICROSOFT_GRAPH_OPENAPI_URL, MICROSOFT_GRAPH_PERMISSIONS_REFERENCE_URL,} from "./presets"; const graphFixture = `openapi: 3.0.4info: title: Microsoft Graph Fixture version: v1.0servers: - url: https://graph.microsoft.com/v1.0paths: /me: get: operationId: me.GetUser summary: Get the signed-in user profile security: - azureAdDelegated: - User.Read responses: "200": description: OK /me/messages: get: operationId: me.messages.ListMessages security: - azureAdDelegated: - Mail.ReadWrite responses: "200": description: OK /me/events: get: operationId: me.events.ListEvents security: - azureAdDelegated: - Calendars.ReadWrite responses: "200": description: OK /me/onenote/pages: get: operationId: me.onenote.pages.ListPages security: - azureAdDelegated: - Notes.ReadWrite responses: "200": description: OK /me/drive/root/children: get: operationId: me.drive.root.ListChildren responses: "200": description: OK /sites: get: operationId: sites.ListSites responses: "200": description: OKcomponents: schemas: user: type: object`; const permissionsReferenceFixture = `### User.Read | Category | Application | Delegated ||--|--|--|| Identifier | - | e1fe6dd8-ba31-4d61-89e7-88639da4683d | --- ### Mail.ReadWrite | Category | Application | Delegated ||--|--|--|| Identifier | e2a3a72e-5f79-4c64-b1b1-878b674786c9 | 024d486e-b451-40bb-833d-3e66d98c5c73 | --- ### Calendars.ReadWrite | Category | Application | Delegated ||--|--|--|| Identifier | ef54d2bf-783f-4e0f-bca1-3210c0444d99 | 1ec239c2-d7c9-4623-a91a-a9775856bb36 | --- ### Notes.ReadWrite | Category | Application | Delegated ||--|--|--|| Identifier | 085ca537-6565-41c2-aca7-db852babc212 | 615e82d5-1f7f-4f99-a456-0a0484a820d5 | --- ### AppCatalog.Read.All | Category | Application | Delegated ||--|--|--|| Identifier | e12dae10-5a57-4817-b79d-dfbec5348930 | - |`; const EMULATOR_SPEC_URL = "https://microsoft.emulators.dev/_emulate/openapi";const EMULATOR_BASE_URL = "https://microsoft.emulators.dev";const LOCAL_EMULATOR_SPEC_URL = "http://localhost:4123/_emulate/openapi";const LOCAL_EMULATOR_BASE_URL = "http://localhost:4123";const emulatorGraphFixture = `openapi: 3.0.3info: title: Microsoft Graph Emulator version: 1.0.0servers: - url: ${EMULATOR_BASE_URL}paths: /v1.0/users: get: operationId: graphUser_List security: - azureAdDelegated: - User.Read.All responses: "200": description: OK /v1.0/me: get: operationId: graphUser_GetMyProfile responses: "200": description: OKcomponents: securitySchemes: azureAdDelegated: type: oauth2 flows: authorizationCode: authorizationUrl: ${EMULATOR_BASE_URL}/oauth2/v2.0/authorize tokenUrl: ${EMULATOR_BASE_URL}/oauth2/v2.0/token scopes: User.Read: User.Read User.Read.All: User.Read.All clientCredentials: tokenUrl: ${EMULATOR_BASE_URL}/oauth2/v2.0/token scopes: https://graph.microsoft.com/.default: https://graph.microsoft.com/.default`; const localEmulatorGraphFixture = `openapi: 3.0.3info: title: Microsoft Graph Local Emulator version: 1.0.0servers: - url: ${LOCAL_EMULATOR_BASE_URL}paths: /v1.0/users: get: operationId: graphUser_List security: - azureAdDelegated: - User.Read.All responses: "200": description: OKcomponents: securitySchemes: azureAdDelegated: type: oauth2 flows: clientCredentials: tokenUrl: ${LOCAL_EMULATOR_BASE_URL}/oauth2/v2.0/token scopes: https://graph.microsoft.com/.default: https://graph.microsoft.com/.default`; interface GraphHttpClientOptions { readonly failedUrls?: ReadonlySet<string>; readonly beforeResponse?: (url: string) => Effect.Effect<void, never, never>;} const waitOneTurn: Effect.Effect<void, never, never> = Effect.promise(() => Promise.resolve()).pipe( Effect.orDie,); const GRAPH_FIXTURE_BODIES: Readonly<Record<string, string>> = { [MICROSOFT_GRAPH_OPENAPI_URL]: graphFixture, [MICROSOFT_GRAPH_PERMISSIONS_REFERENCE_URL]: permissionsReferenceFixture, [EMULATOR_SPEC_URL]: emulatorGraphFixture, [LOCAL_EMULATOR_SPEC_URL]: localEmulatorGraphFixture,}; const makeGraphHttpClientLayer = (options: GraphHttpClientOptions = {}) => Layer.succeed(HttpClient.HttpClient)( HttpClient.make((request: HttpClientRequest.HttpClientRequest) => { const body = GRAPH_FIXTURE_BODIES[request.url]; const failed = options.failedUrls?.has(request.url) === true; const response = HttpClientResponse.fromWeb( request, failed ? new Response("forced failure", { status: 503 }) : body === undefined ? new Response("not found", { status: 404 }) : new Response(body, { status: 200, headers: { "content-type": request.url === MICROSOFT_GRAPH_PERMISSIONS_REFERENCE_URL ? "text/markdown" : "application/yaml", }, }), ); return (options.beforeResponse?.(request.url) ?? Effect.void).pipe(Effect.as(response)); }), ); const graphHttpClientLayer = makeGraphHttpClientLayer(); const graphPlugins = (options?: Omit<MicrosoftPluginOptions, "httpClientLayer">) => [ microsoftPlugin({ httpClientLayer: graphHttpClientLayer, ...options }), memoryCredentialsPlugin(), ] as const; describe("Microsoft Graph provider", () => { it.effect("rejects non-Microsoft URL overrides before fetching the Graph spec", () => Effect.scoped( Effect.gen(function* () { let requests = 0; const blockedHttpClientLayer = Layer.succeed(HttpClient.HttpClient)( HttpClient.make((request: HttpClientRequest.HttpClientRequest) => Effect.sync(() => { requests += 1; return HttpClientResponse.fromWeb( request, new Response("unexpected request", { status: 500 }), ); }), ), ); const executor = yield* createExecutor( makeTestConfig({ plugins: [ microsoftPlugin({ httpClientLayer: blockedHttpClientLayer }), memoryCredentialsPlugin(), ], }), ); const result = yield* executor.microsoft.addWorkloads({ baseUrl: "https://attacker.example/v1.0", workloads: [ { custom: { customScopes: ["User.Read.All"], slug: "bad_graph", name: "Bad Graph", specUrl: "https://attacker.example/openapi.yaml", authorizationUrl: "https://attacker.example/oauth2/v2.0/authorize", tokenUrl: "https://attacker.example/oauth2/v2.0/token", clientCredentialsTokenUrl: "https://attacker.example/oauth2/v2.0/token", }, }, ], }); expect(result).toEqual({ added: [], skipped: [], failed: [ { slug: IntegrationSlug.make("bad_graph"), presetId: "custom", error: "Microsoft Graph specUrl must point to the trusted Microsoft Graph OpenAPI source", }, ], }); expect(requests).toBe(0); }), ), ); it.effect("custom workload with custom scopes stays custom-only, no default workloads", () => Effect.scoped( Effect.gen(function* () { const executor = yield* createExecutor(makeTestConfig({ plugins: graphPlugins() })); const result = yield* executor.microsoft.addWorkloads({ workloads: [ { custom: { customScopes: ["Custom.Scope"], slug: "microsoft_graph_custom", name: "Custom Microsoft Graph", }, }, ], }); expect(result).toEqual({ added: [ { slug: IntegrationSlug.make("microsoft_graph_custom"), presetId: "custom", toolCount: 1, }, ], skipped: [], failed: [], }); const config = yield* executor.microsoft.getConfig("microsoft_graph_custom"); expect(config?.microsoftGraphPresetIds).toEqual([]); expect(config?.microsoftGraphCustomScopes).toEqual(["Custom.Scope"]); expect(config?.microsoftGraphCoversFullGraph).toBe(false); expect(config?.microsoftGraphScopes).toEqual([ "offline_access", "User.Read", "Custom.Scope", ]); }), ), ); it.effect( "adds a custom Microsoft workload from the emulator spec with app-only OAuth endpoints", () => Effect.scoped( Effect.gen(function* () { const executor = yield* createExecutor( makeTestConfig({ plugins: graphPlugins({ allowUnsafeUrlOverrides: true }) }), ); yield* executor.microsoft.addWorkloads({ baseUrl: EMULATOR_BASE_URL, workloads: [ { custom: { customScopes: ["User.Read.All"], slug: "microsoft_graph_emulated", name: "Microsoft Graph Emulator", specUrl: EMULATOR_SPEC_URL, }, }, ], }); const config = yield* executor.microsoft.getConfig("microsoft_graph_emulated"); expect(config?.sourceUrl).toBe(EMULATOR_SPEC_URL); expect(config?.baseUrl).toBe(EMULATOR_BASE_URL); expect(config?.microsoftGraphAuthorizationUrl).toBe( `${EMULATOR_BASE_URL}/oauth2/v2.0/authorize`, ); expect(config?.microsoftGraphTokenUrl).toBe(`${EMULATOR_BASE_URL}/oauth2/v2.0/token`); expect(config?.microsoftGraphClientCredentialsTokenUrl).toBe( `${EMULATOR_BASE_URL}/oauth2/v2.0/token`, ); const delegated = config?.authenticationTemplate?.find( (entry) => entry.kind === "oauth2" && String(entry.slug) === MICROSOFT_AUTH_TEMPLATE_SLUG, ); const clientCredentials = config?.authenticationTemplate?.find( (entry) => entry.kind === "oauth2" && String(entry.slug) === MICROSOFT_CLIENT_CREDENTIALS_AUTH_TEMPLATE_SLUG, ); expect(delegated?.kind === "oauth2" ? delegated.tokenUrl : undefined).toBe( `${EMULATOR_BASE_URL}/oauth2/v2.0/token`, ); expect( clientCredentials?.kind === "oauth2" ? clientCredentials.scopes : undefined, ).toEqual([...MICROSOFT_GRAPH_CLIENT_CREDENTIALS_SCOPES]); yield* executor.connections.create({ owner: "org", name: ConnectionName.make("machine"), integration: IntegrationSlug.make("microsoft_graph_emulated"), template: AuthTemplateSlug.make(MICROSOFT_CLIENT_CREDENTIALS_AUTH_TEMPLATE_SLUG), value: "token-xyz", }); const toolNames = (yield* executor.tools.list()).map((tool) => String(tool.name)); expect(toolNames).toContain("users.graphUserList"); }), ), ); it.effect("accepts a loopback http emulator spec only when the override is enabled", () => Effect.scoped( Effect.gen(function* () { const executor = yield* createExecutor( makeTestConfig({ plugins: graphPlugins({ allowUnsafeUrlOverrides: true }) }), ); yield* executor.microsoft.addWorkloads({ baseUrl: LOCAL_EMULATOR_BASE_URL, workloads: [ { custom: { customScopes: ["User.Read.All"], slug: "microsoft_graph_local_emulated", name: "Microsoft Graph Local Emulator", specUrl: LOCAL_EMULATOR_SPEC_URL, }, }, ], }); const config = yield* executor.microsoft.getConfig("microsoft_graph_local_emulated"); expect(config?.sourceUrl).toBe(LOCAL_EMULATOR_SPEC_URL); expect(config?.baseUrl).toBe(LOCAL_EMULATOR_BASE_URL); }), ), ); it.effect("rejects a loopback http spec URL when the override is disabled", () => Effect.scoped( Effect.gen(function* () { const executor = yield* createExecutor(makeTestConfig({ plugins: graphPlugins() })); const result = yield* executor.microsoft.addWorkloads({ baseUrl: LOCAL_EMULATOR_BASE_URL, workloads: [ { custom: { customScopes: ["User.Read.All"], slug: "microsoft_graph_local_disabled", name: "Microsoft Graph Local Emulator", specUrl: LOCAL_EMULATOR_SPEC_URL, }, }, ], }); expect(result.failed).toEqual([ { slug: IntegrationSlug.make("microsoft_graph_local_disabled"), presetId: "custom", error: "Microsoft Graph specUrl must point to the trusted Microsoft Graph OpenAPI source", }, ]); }), ), ); it.effect("rejects a non-loopback http override even with allowUnsafeUrlOverrides", () => Effect.scoped( Effect.gen(function* () { let requests = 0; const blockedHttpClientLayer = Layer.succeed(HttpClient.HttpClient)( HttpClient.make((request: HttpClientRequest.HttpClientRequest) => Effect.sync(() => { requests += 1; return HttpClientResponse.fromWeb( request, new Response("unexpected request", { status: 500 }), ); }), ), ); const executor = yield* createExecutor( makeTestConfig({ plugins: [ microsoftPlugin({ httpClientLayer: blockedHttpClientLayer, allowUnsafeUrlOverrides: true, }), memoryCredentialsPlugin(), ], }), ); const result = yield* executor.microsoft.addWorkloads({ baseUrl: "http://example.com/v1.0", workloads: [ {Lines 1–500 of 896. Load more
import { describe, expect, it } from "@effect/vitest"; import { Effect, Layer } from "effect"; import { HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http"; import { AuthTemplateSlug, ConnectionName, IntegrationSlug, createExecutor, } from "@executor-js/sdk"; import { makeTestConfig, memoryCredentialsPlugin } from "@executor-js/sdk/testing"; import { microsoftPlugin, type MicrosoftPluginOptions } from "./plugin"; import { MICROSOFT_AUTH_TEMPLATE_SLUG, MICROSOFT_CLIENT_CREDENTIALS_AUTH_TEMPLATE_SLUG, MICROSOFT_GRAPH_CLIENT_CREDENTIALS_SCOPES, MICROSOFT_GRAPH_OPENAPI_URL, MICROSOFT_GRAPH_PERMISSIONS_REFERENCE_URL, } from "./presets"; const graphFixture = ` openapi: 3.0.4 info: title: Microsoft Graph Fixture version: v1.0 servers: - url: https://graph.microsoft.com/v1.0 paths: /me: get: operationId: me.GetUser summary: Get the signed-in user profile security: - azureAdDelegated: - User.Read responses: "200": description: OK /me/messages: get: operationId: me.messages.ListMessages security: - azureAdDelegated: - Mail.ReadWrite responses: "200": description: OK /me/events: get: operationId: me.events.ListEvents security: - azureAdDelegated: - Calendars.ReadWrite responses: "200": description: OK /me/onenote/pages: get: operationId: me.onenote.pages.ListPages security: - azureAdDelegated: - Notes.ReadWrite responses: "200": description: OK /me/drive/root/children: get: operationId: me.drive.root.ListChildren responses: "200": description: OK /sites: get: operationId: sites.ListSites responses: "200": description: OK components: schemas: user: type: object `; const permissionsReferenceFixture = ` ### User.Read | Category | Application | Delegated | |--|--|--| | Identifier | - | e1fe6dd8-ba31-4d61-89e7-88639da4683d | --- ### Mail.ReadWrite | Category | Application | Delegated | |--|--|--| | Identifier | e2a3a72e-5f79-4c64-b1b1-878b674786c9 | 024d486e-b451-40bb-833d-3e66d98c5c73 | --- ### Calendars.ReadWrite | Category | Application | Delegated | |--|--|--| | Identifier | ef54d2bf-783f-4e0f-bca1-3210c0444d99 | 1ec239c2-d7c9-4623-a91a-a9775856bb36 | --- ### Notes.ReadWrite | Category | Application | Delegated | |--|--|--| | Identifier | 085ca537-6565-41c2-aca7-db852babc212 | 615e82d5-1f7f-4f99-a456-0a0484a820d5 | --- ### AppCatalog.Read.All | Category | Application | Delegated | |--|--|--| | Identifier | e12dae10-5a57-4817-b79d-dfbec5348930 | - | `; const EMULATOR_SPEC_URL = "https://microsoft.emulators.dev/_emulate/openapi"; const EMULATOR_BASE_URL = "https://microsoft.emulators.dev"; const LOCAL_EMULATOR_SPEC_URL = "http://localhost:4123/_emulate/openapi"; const LOCAL_EMULATOR_BASE_URL = "http://localhost:4123"; const emulatorGraphFixture = ` openapi: 3.0.3 info: title: Microsoft Graph Emulator version: 1.0.0 servers: - url: ${EMULATOR_BASE_URL} paths: /v1.0/users: get: operationId: graphUser_List security: - azureAdDelegated: - User.Read.All responses: "200": description: OK /v1.0/me: get: operationId: graphUser_GetMyProfile responses: "200": description: OK components: securitySchemes: azureAdDelegated: type: oauth2 flows: authorizationCode: authorizationUrl: ${EMULATOR_BASE_URL}/oauth2/v2.0/authorize tokenUrl: ${EMULATOR_BASE_URL}/oauth2/v2.0/token scopes: User.Read: User.Read User.Read.All: User.Read.All clientCredentials: tokenUrl: ${EMULATOR_BASE_URL}/oauth2/v2.0/token scopes: https://graph.microsoft.com/.default: https://graph.microsoft.com/.default `; const localEmulatorGraphFixture = ` openapi: 3.0.3 info: title: Microsoft Graph Local Emulator version: 1.0.0 servers: - url: ${LOCAL_EMULATOR_BASE_URL} paths: /v1.0/users: get: operationId: graphUser_List security: - azureAdDelegated: - User.Read.All responses: "200": description: OK components: securitySchemes: azureAdDelegated: type: oauth2 flows: clientCredentials: tokenUrl: ${LOCAL_EMULATOR_BASE_URL}/oauth2/v2.0/token scopes: https://graph.microsoft.com/.default: https://graph.microsoft.com/.default `; interface GraphHttpClientOptions { readonly failedUrls?: ReadonlySet<string>; readonly beforeResponse?: (url: string) => Effect.Effect<void, never, never>; } const waitOneTurn: Effect.Effect<void, never, never> = Effect.promise(() => Promise.resolve()).pipe( Effect.orDie, ); const GRAPH_FIXTURE_BODIES: Readonly<Record<string, string>> = { [MICROSOFT_GRAPH_OPENAPI_URL]: graphFixture, [MICROSOFT_GRAPH_PERMISSIONS_REFERENCE_URL]: permissionsReferenceFixture, [EMULATOR_SPEC_URL]: emulatorGraphFixture, [LOCAL_EMULATOR_SPEC_URL]: localEmulatorGraphFixture, }; const makeGraphHttpClientLayer = (options: GraphHttpClientOptions = {}) => Layer.succeed(HttpClient.HttpClient)( HttpClient.make((request: HttpClientRequest.HttpClientRequest) => { const body = GRAPH_FIXTURE_BODIES[request.url]; const failed = options.failedUrls?.has(request.url) === true; const response = HttpClientResponse.fromWeb( request, failed ? new Response("forced failure", { status: 503 }) : body === undefined ? new Response("not found", { status: 404 }) : new Response(body, { status: 200, headers: { "content-type": request.url === MICROSOFT_GRAPH_PERMISSIONS_REFERENCE_URL ? "text/markdown" : "application/yaml", }, }), ); return (options.beforeResponse?.(request.url) ?? Effect.void).pipe(Effect.as(response)); }), ); const graphHttpClientLayer = makeGraphHttpClientLayer(); const graphPlugins = (options?: Omit<MicrosoftPluginOptions, "httpClientLayer">) => [ microsoftPlugin({ httpClientLayer: graphHttpClientLayer, ...options }), memoryCredentialsPlugin(), ] as const; describe("Microsoft Graph provider", () => { it.effect("rejects non-Microsoft URL overrides before fetching the Graph spec", () => Effect.scoped( Effect.gen(function* () { let requests = 0; const blockedHttpClientLayer = Layer.succeed(HttpClient.HttpClient)( HttpClient.make((request: HttpClientRequest.HttpClientRequest) => Effect.sync(() => { requests += 1; return HttpClientResponse.fromWeb( request, new Response("unexpected request", { status: 500 }), ); }), ), ); const executor = yield* createExecutor( makeTestConfig({ plugins: [ microsoftPlugin({ httpClientLayer: blockedHttpClientLayer }), memoryCredentialsPlugin(), ], }), ); const result = yield* executor.microsoft.addWorkloads({ baseUrl: "https://attacker.example/v1.0", workloads: [ { custom: { customScopes: ["User.Read.All"], slug: "bad_graph", name: "Bad Graph", specUrl: "https://attacker.example/openapi.yaml", authorizationUrl: "https://attacker.example/oauth2/v2.0/authorize", tokenUrl: "https://attacker.example/oauth2/v2.0/token", clientCredentialsTokenUrl: "https://attacker.example/oauth2/v2.0/token", }, }, ], }); expect(result).toEqual({ added: [], skipped: [], failed: [ { slug: IntegrationSlug.make("bad_graph"), presetId: "custom", error: "Microsoft Graph specUrl must point to the trusted Microsoft Graph OpenAPI source", }, ], }); expect(requests).toBe(0); }), ), ); it.effect("custom workload with custom scopes stays custom-only, no default workloads", () => Effect.scoped( Effect.gen(function* () { const executor = yield* createExecutor(makeTestConfig({ plugins: graphPlugins() })); const result = yield* executor.microsoft.addWorkloads({ workloads: [ { custom: { customScopes: ["Custom.Scope"], slug: "microsoft_graph_custom", name: "Custom Microsoft Graph", }, }, ], }); expect(result).toEqual({ added: [ { slug: IntegrationSlug.make("microsoft_graph_custom"), presetId: "custom", toolCount: 1, }, ], skipped: [], failed: [], }); const config = yield* executor.microsoft.getConfig("microsoft_graph_custom"); expect(config?.microsoftGraphPresetIds).toEqual([]); expect(config?.microsoftGraphCustomScopes).toEqual(["Custom.Scope"]); expect(config?.microsoftGraphCoversFullGraph).toBe(false); expect(config?.microsoftGraphScopes).toEqual([ "offline_access", "User.Read", "Custom.Scope", ]); }), ), ); it.effect( "adds a custom Microsoft workload from the emulator spec with app-only OAuth endpoints", () => Effect.scoped( Effect.gen(function* () { const executor = yield* createExecutor( makeTestConfig({ plugins: graphPlugins({ allowUnsafeUrlOverrides: true }) }), ); yield* executor.microsoft.addWorkloads({ baseUrl: EMULATOR_BASE_URL, workloads: [ { custom: { customScopes: ["User.Read.All"], slug: "microsoft_graph_emulated", name: "Microsoft Graph Emulator", specUrl: EMULATOR_SPEC_URL, }, }, ], }); const config = yield* executor.microsoft.getConfig("microsoft_graph_emulated"); expect(config?.sourceUrl).toBe(EMULATOR_SPEC_URL); expect(config?.baseUrl).toBe(EMULATOR_BASE_URL); expect(config?.microsoftGraphAuthorizationUrl).toBe( `${EMULATOR_BASE_URL}/oauth2/v2.0/authorize`, ); expect(config?.microsoftGraphTokenUrl).toBe(`${EMULATOR_BASE_URL}/oauth2/v2.0/token`); expect(config?.microsoftGraphClientCredentialsTokenUrl).toBe( `${EMULATOR_BASE_URL}/oauth2/v2.0/token`, ); const delegated = config?.authenticationTemplate?.find( (entry) => entry.kind === "oauth2" && String(entry.slug) === MICROSOFT_AUTH_TEMPLATE_SLUG, ); const clientCredentials = config?.authenticationTemplate?.find( (entry) => entry.kind === "oauth2" && String(entry.slug) === MICROSOFT_CLIENT_CREDENTIALS_AUTH_TEMPLATE_SLUG, ); expect(delegated?.kind === "oauth2" ? delegated.tokenUrl : undefined).toBe( `${EMULATOR_BASE_URL}/oauth2/v2.0/token`, ); expect( clientCredentials?.kind === "oauth2" ? clientCredentials.scopes : undefined, ).toEqual([...MICROSOFT_GRAPH_CLIENT_CREDENTIALS_SCOPES]); yield* executor.connections.create({ owner: "org", name: ConnectionName.make("machine"), integration: IntegrationSlug.make("microsoft_graph_emulated"), template: AuthTemplateSlug.make(MICROSOFT_CLIENT_CREDENTIALS_AUTH_TEMPLATE_SLUG), value: "token-xyz", }); const toolNames = (yield* executor.tools.list()).map((tool) => String(tool.name)); expect(toolNames).toContain("users.graphUserList"); }), ), ); it.effect("accepts a loopback http emulator spec only when the override is enabled", () => Effect.scoped( Effect.gen(function* () { const executor = yield* createExecutor( makeTestConfig({ plugins: graphPlugins({ allowUnsafeUrlOverrides: true }) }), ); yield* executor.microsoft.addWorkloads({ baseUrl: LOCAL_EMULATOR_BASE_URL, workloads: [ { custom: { customScopes: ["User.Read.All"], slug: "microsoft_graph_local_emulated", name: "Microsoft Graph Local Emulator", specUrl: LOCAL_EMULATOR_SPEC_URL, }, }, ], }); const config = yield* executor.microsoft.getConfig("microsoft_graph_local_emulated"); expect(config?.sourceUrl).toBe(LOCAL_EMULATOR_SPEC_URL); expect(config?.baseUrl).toBe(LOCAL_EMULATOR_BASE_URL); }), ), ); it.effect("rejects a loopback http spec URL when the override is disabled", () => Effect.scoped( Effect.gen(function* () { const executor = yield* createExecutor(makeTestConfig({ plugins: graphPlugins() })); const result = yield* executor.microsoft.addWorkloads({ baseUrl: LOCAL_EMULATOR_BASE_URL, workloads: [ { custom: { customScopes: ["User.Read.All"], slug: "microsoft_graph_local_disabled", name: "Microsoft Graph Local Emulator", specUrl: LOCAL_EMULATOR_SPEC_URL, }, }, ], }); expect(result.failed).toEqual([ { slug: IntegrationSlug.make("microsoft_graph_local_disabled"), presetId: "custom", error: "Microsoft Graph specUrl must point to the trusted Microsoft Graph OpenAPI source", }, ]); }), ), ); it.effect("rejects a non-loopback http override even with allowUnsafeUrlOverrides", () => Effect.scoped( Effect.gen(function* () { let requests = 0; const blockedHttpClientLayer = Layer.succeed(HttpClient.HttpClient)( HttpClient.make((request: HttpClientRequest.HttpClientRequest) => Effect.sync(() => { requests += 1; return HttpClientResponse.fromWeb( request, new Response("unexpected request", { status: 500 }), ); }), ), ); const executor = yield* createExecutor( makeTestConfig({ plugins: [ microsoftPlugin({ httpClientLayer: blockedHttpClientLayer, allowUnsafeUrlOverrides: true, }), memoryCredentialsPlugin(), ], }), ); const result = yield* executor.microsoft.addWorkloads({ baseUrl: "http://example.com/v1.0", workloads: [ {