| ... | ... | @@ -1,8 +1,11 @@ |
| 1 | 1 | import { assert, describe, it } from "@effect/vitest"; |
| 2 | import * as Duration from "effect/Duration"; |
| 2 | 3 | import * as Effect from "effect/Effect"; |
| 4 | import * as Fiber from "effect/Fiber"; |
| 3 | 5 | import * as Layer from "effect/Layer"; |
| 4 | 6 | import * as Ref from "effect/Ref"; |
| 5 | 7 | import * as HttpClient from "effect/http/HttpClient"; |
| 8 | import * as HttpClientRequest from "effect/http/HttpClientRequest"; |
| 6 | 9 | import * as HttpClientResponse from "effect/http/HttpClientResponse"; |
| 7 | 10 | import { PRIMARY_LOCAL_ENVIRONMENT_ID } from "@t3tools/contracts"; |
| 8 | 11 | import { |
| ... | ... | @@ -144,4 +147,91 @@ describe("DesktopLocalEnvironmentAuth", () => { |
| 144 | 147 | ); |
| 145 | 148 | }).pipe(Effect.provide(TestClock.layer())), |
| 146 | 149 | ); |
| 150 | |
| 151 | const tokenResponse = (request: HttpClientRequest.HttpClientRequest) => |
| 152 | HttpClientResponse.fromWeb( |
| 153 | request, |
| 154 | new Response( |
| 155 | JSON.stringify({ |
| 156 | access_token: "desktop-bearer-token", |
| 157 | issued_token_type: "urn:ietf:params:oauth:token-type:access_token", |
| 158 | token_type: "Bearer", |
| 159 | expires_in: 3600, |
| 160 | scope: "orchestration:read", |
| 161 | }), |
| 162 | { status: 200, headers: { "content-type": "application/json" } }, |
| 163 | ), |
| 164 | ); |
| 165 | const layerPool = Layer.succeed(DesktopBackendPool.DesktopBackendPool, { |
| 166 | list: Effect.succeed([ |
| 167 | { |
| 168 | id: PRIMARY_LOCAL_ENVIRONMENT_ID, |
| 169 | label: Effect.succeed("Windows"), |
| 170 | currentConfig: Effect.succeedSome(config), |
| 171 | }, |
| 172 | ]), |
| 173 | } as unknown as DesktopBackendPool.DesktopBackendPool["Service"]); |
| 174 | // Answers the first `failures` exchanges with `failure`, then with a token. |
| 175 | const makeExchange = (failures: number, failure: () => Response) => |
| 176 | Effect.gen(function* () { |
| 177 | const requestCount = yield* Ref.make(0); |
| 178 | const layerHttpClient = Layer.succeed( |
| 179 | HttpClient.HttpClient, |
| 180 | HttpClient.make((request) => |
| 181 | Ref.updateAndGet(requestCount, (count) => count + 1).pipe( |
| 182 | Effect.map((count) => |
| 183 | count <= failures |
| 184 | ? HttpClientResponse.fromWeb(request, failure()) |
| 185 | : tokenResponse(request), |
| 186 | ), |
| 187 | ), |
| 188 | ), |
| 189 | ); |
| 190 | const auth = yield* DesktopLocalEnvironmentAuth.DesktopLocalEnvironmentAuth.pipe( |
| 191 | Effect.provide( |
| 192 | DesktopLocalEnvironmentAuth.layer.pipe( |
| 193 | Layer.provide(Layer.mergeAll(layerPool, layerHttpClient)), |
| 194 | ), |
| 195 | ), |
| 196 | ); |
| 197 | return { auth, requestCount }; |
| 198 | }); |
| 199 | |
| 200 | it.effect("retries a backend that is still starting", () => |
| 201 | Effect.gen(function* () { |
| 202 | const { auth, requestCount } = yield* makeExchange( |
| 203 | 2, |
| 204 | () => new Response("", { status: 503 }), |
| 205 | ); |
| 206 | |
| 207 | const fiber = yield* auth.getBearerToken.pipe(Effect.forkChild); |
| 208 | yield* TestClock.adjust(Duration.seconds(1)); |
| 209 | |
| 210 | assert.strictEqual(yield* Fiber.join(fiber), "desktop-bearer-token"); |
| 211 | assert.strictEqual(yield* Ref.get(requestCount), 3); |
| 212 | }).pipe(Effect.provide(TestClock.layer())), |
| 213 | ); |
| 214 | |
| 215 | it.effect("does not retry a rejected bootstrap credential", () => |
| 216 | Effect.gen(function* () { |
| 217 | const { auth, requestCount } = yield* makeExchange(1, () => |
| 218 | Response.json( |
| 219 | { |
| 220 | _tag: "EnvironmentAuthInvalidError", |
| 221 | code: "auth_invalid", |
| 222 | reason: "invalid_credential", |
| 223 | traceId: "trace-1", |
| 224 | }, |
| 225 | { status: 401 }, |
| 226 | ), |
| 227 | ); |
| 228 | |
| 229 | const fiber = yield* auth.getBearerToken.pipe(Effect.flip, Effect.forkChild); |
| 230 | yield* TestClock.adjust(Duration.seconds(1)); |
| 231 | const error = yield* Fiber.join(fiber); |
| 232 | |
| 233 | assert.strictEqual(error._tag, "DesktopLocalEnvironmentAuthSessionBootstrapError"); |
| 234 | assert.strictEqual(yield* Ref.get(requestCount), 1); |
| 235 | }).pipe(Effect.provide(TestClock.layer())), |
| 236 | ); |
| 147 | 237 | }); |