mirror of
https://github.com/anomalyco/opencode.git
synced 2026-05-04 11:46:38 +00:00
fix(account): coalesce concurrent console token refreshes (#20503)
This commit is contained in:
@@ -148,6 +148,70 @@ it.live("token refresh persists the new token", () =>
|
||||
}),
|
||||
)
|
||||
|
||||
it.live("concurrent config and token requests coalesce token refresh", () =>
|
||||
Effect.gen(function* () {
|
||||
const id = AccountID.make("user-1")
|
||||
|
||||
yield* AccountRepo.use((r) =>
|
||||
r.persistAccount({
|
||||
id,
|
||||
email: "user@example.com",
|
||||
url: "https://one.example.com",
|
||||
accessToken: AccessToken.make("at_old"),
|
||||
refreshToken: RefreshToken.make("rt_old"),
|
||||
expiry: Date.now() - 1_000,
|
||||
orgID: Option.some(OrgID.make("org-9")),
|
||||
}),
|
||||
)
|
||||
|
||||
let refreshCalls = 0
|
||||
const client = HttpClient.make((req) =>
|
||||
Effect.promise(async () => {
|
||||
if (req.url === "https://one.example.com/auth/device/token") {
|
||||
refreshCalls += 1
|
||||
|
||||
if (refreshCalls === 1) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 25))
|
||||
return json(req, {
|
||||
access_token: "at_new",
|
||||
refresh_token: "rt_new",
|
||||
expires_in: 60,
|
||||
})
|
||||
}
|
||||
|
||||
return json(
|
||||
req,
|
||||
{
|
||||
error: "invalid_grant",
|
||||
error_description: "refresh token already used",
|
||||
},
|
||||
400,
|
||||
)
|
||||
}
|
||||
|
||||
if (req.url === "https://one.example.com/api/config") {
|
||||
return json(req, { config: { theme: "light", seats: 5 } })
|
||||
}
|
||||
|
||||
return json(req, {}, 404)
|
||||
}),
|
||||
)
|
||||
|
||||
const [cfg, token] = yield* Account.Service.use((s) =>
|
||||
Effect.all([s.config(id, OrgID.make("org-9")), s.token(id)], { concurrency: 2 }),
|
||||
).pipe(Effect.provide(live(client)))
|
||||
|
||||
expect(Option.getOrThrow(cfg)).toEqual({ theme: "light", seats: 5 })
|
||||
expect(String(Option.getOrThrow(token))).toBe("at_new")
|
||||
expect(refreshCalls).toBe(1)
|
||||
|
||||
const row = yield* AccountRepo.use((r) => r.getRow(id))
|
||||
const value = Option.getOrThrow(row)
|
||||
expect(value.access_token).toBe(AccessToken.make("at_new"))
|
||||
expect(value.refresh_token).toBe(RefreshToken.make("rt_new"))
|
||||
}),
|
||||
)
|
||||
|
||||
it.live("config sends the selected org header", () =>
|
||||
Effect.gen(function* () {
|
||||
const id = AccountID.make("user-1")
|
||||
|
||||
Reference in New Issue
Block a user