| @@ -20,7 +20,6 @@ import * as NodeHttpServer from "@effect/platform-node/NodeHttpServer"; · 19 unmodified lines |
| 20 | 20 | |
| 21 | 21 | import { |
| 22 | 22 | ConnectionId, |
| 23 | | − ConnectionReauthRequiredError, |
| 24 | 23 | CreateConnectionInput, |
| 25 | 24 | ScopeId, |
| 26 | 25 | SecretId, |
| @@ -41,6 +40,13 @@ import { OAuth2Auth } from "./types"; · 14 unmodified lines |
| 41 | 40 | |
| 42 | 41 | const autoApprove: InvokeOptions = { onElicitation: "accept-all" }; |
| 43 | 42 | |
| 43 | +class OpenApiOauthTestSetupError extends Schema.TaggedErrorClass<OpenApiOauthTestSetupError>()( |
| 44 | + "OpenApiOauthTestSetupError", |
| 45 | + { |
| 46 | + message: Schema.String, |
| 47 | + }, |
| 48 | +) {} |
| 49 | + |
| 44 | 50 | // --------------------------------------------------------------------------- |
| 45 | 51 | // Test API — one endpoint that echoes the Authorization header so we can |
| 46 | 52 | // prove which access token was in flight at invoke time. |
| @@ -141,11 +147,15 @@ const makeExecutor = () => · 94 unmodified lines |
| 141 | 147 | storage: () => ({}), |
| 142 | 148 | secretProviders: [memoryProvider], |
| 143 | 149 | })); |
| 144 | | − const clientLayer = FetchHttpClient.layer; |
| 145 | | − const server = yield* HttpServer.HttpServer; |
| 146 | | − const address = server.address; |
| 147 | | − if (address._tag !== "TcpAddress") return yield* Effect.die("test server must bind to TCP"); |
| 148 | | − const baseUrl = `http://127.0.0.1:${address.port}`; |
| 150 | + const clientLayer = FetchHttpClient.layer; |
| 151 | + const server = yield* HttpServer.HttpServer; |
| 152 | + const address = server.address; |
| 153 | + if (!("port" in address)) { |
| 154 | + return yield* new OpenApiOauthTestSetupError({ |
| 155 | + message: "Test server must bind to TCP", |
| 156 | + }); |
| 157 | + } |
| 158 | + const baseUrl = `http://127.0.0.1:${address.port}`; |
| 149 | 159 | const plugins = [ |
| 150 | 160 | openApiPlugin({ httpClientLayer: clientLayer }), |
| 151 | 161 | memorySecretsPlugin(), |
| @@ -277,7 +287,7 @@ layer(TestLayer)("OpenAPI oauth refresh", (it) => { · 125 unmodified lines |
| 277 | 287 | |
| 278 | 288 | yield* executor.openapi.addSpec({ |
| 279 | 289 | spec: specJson, |
| 280 | | − scope: String(scopeId), |
| 290 | + scope: scopeId, |
| 281 | 291 | namespace: "petstore", |
| 282 | 292 | baseUrl, |
| 283 | 293 | oauth2: auth, |
| @@ -334,7 +344,7 @@ layer(TestLayer)("OpenAPI oauth refresh", (it) => { · 50 unmodified lines |
| 334 | 344 | |
| 335 | 345 | yield* executor.openapi.addSpec({ |
| 336 | 346 | spec: specJson, |
| 337 | | − scope: String(scopeId), |
| 347 | + scope: scopeId, |
| 338 | 348 | namespace: "petstore", |
| 339 | 349 | baseUrl, |
| 340 | 350 | oauth2: auth, |
| @@ -392,7 +402,7 @@ layer(TestLayer)("OpenAPI oauth refresh", (it) => { · 51 unmodified lines |
| 392 | 402 | |
| 393 | 403 | yield* executor.openapi.addSpec({ |
| 394 | 404 | spec: specJson, |
| 395 | | − scope: String(scopeId), |
| 405 | + scope: scopeId, |
| 396 | 406 | namespace: "petstore", |
| 397 | 407 | baseUrl, |
| 398 | 408 | oauth2: auth, |
| @@ -402,15 +412,23 @@ layer(TestLayer)("OpenAPI oauth refresh", (it) => { · 3 unmodified lines |
| 402 | 412 | // generic Error (see openapi invokeTool), so we assert against |
| 403 | 413 | // the `accessToken` call directly too — that's the surface |
| 404 | 414 | // the UI bridges use to trigger re-auth. |
| 405 | | − const flipped = yield* executor.connections |
| 415 | + const reauthRequired = yield* executor.connections |
| 406 | 416 | .accessToken("conn-refresh-dead") |
| 407 | | − .pipe(Effect.flip); |
| 408 | | − expect(flipped._tag).toBe("ConnectionReauthRequiredError"); |
| 409 | | − expect((flipped as ConnectionReauthRequiredError).provider).toBe( |
| 417 | + .pipe( |
| 418 | + Effect.flatMap(() => |
| 419 | + Effect.fail( |
| 420 | + new OpenApiOauthTestSetupError({ |
| 421 | + message: "Expected refresh to require re-auth", |
| 422 | + }), |
| 423 | + ), |
| 424 | + ), |
| 425 | + Effect.catchTag("ConnectionReauthRequiredError", Effect.succeed), |
| 426 | + ); |
| 427 | + expect(reauthRequired.provider).toBe( |
| 410 | 428 | "openapi:oauth2", |
| 411 | 429 | ); |
| 412 | 430 | expect( |
| 413 | | − (flipped as ConnectionReauthRequiredError).message, |
| 431 | + reauthRequired.message, |
| 414 | 432 | ).toMatch(/invalid_grant|revoked/i); |
| 415 | 433 | }), |
| 416 | 434 | ); |