AITURK IDE 1.0.0-beta.17: telefon kodu yayini yeniden denenir
Uzun sure acik kalan IDE'de "Telefon ekle" ilk istegi olu bir keep-alive baglantisinda kaybedebiliyordu; bulusma kaydi yayinlanamayinca kod yolu bes dakika boyunca "Kodla baglanma su an kullanilamiyor" kaliyordu (QR calisiyordu). - publishPairOffer: en cok 4 deneme (0,5 / 1,5 / 3 sn); ag hatasi, 5xx ve 429 yeniden denenir, 422 kalicidir; yaniti kaybolan denemeden sonraki 409 kaydin yerinde oldugu anlamina gelir (iptal jetonu yoktur, kayit kendiliginden biter). - Pencerede "Yeniden dene": ayni kod ve QR ile kaydi yeniden yayinlar. - Role oturumu kaydi ve "Giris yap" baslangici icin tek ek deneme (zaman asimi yeniden denenmez). - Gunluge yalniz "pair offer published / failed after N tries" duser; kod, lookup ve jeton yazilmaz. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B8ppceaR5s6cvMnm7uGRnf
This commit is contained in:
1 parent
2ed9a6b2de
commit
58dbb4523b
13 files changed
+729
-76
No files matched your search
@@ -11,6 +11,7 @@ import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import {
|
||||
AITURK_CLI_KEY_HOLD_MS,
|
||||
AITURK_LOGIN_START_RETRY_DELAY_MS,
|
||||
type AiturkCliExit,
|
||||
AiturkLoginError,
|
||||
createAiturkCliRunner,
|
||||
@@ -38,6 +39,8 @@ const approved = { state: 'approved', key: IDE_KEY, cliKey: CLI_KEY, account: 'a
|
||||
function harness(
|
||||
options: {
|
||||
start?: { status: number; body?: unknown }
|
||||
/** Thrown by successive `device-auth/start` calls before any answer is given. */
|
||||
startErrors?: Error[]
|
||||
polls?: ({ status: number; body?: unknown } | Error)[]
|
||||
cliExits?: AiturkCliExit[]
|
||||
validate?: unknown
|
||||
@@ -47,6 +50,7 @@ function harness(
|
||||
) {
|
||||
const calls: { url: string; init: any }[] = []
|
||||
const polls = [...(options.polls ?? [])]
|
||||
const startErrors = [...(options.startErrors ?? [])]
|
||||
const cliExits = [...(options.cliExits ?? [0])]
|
||||
const clock = { now: 1_000_000 }
|
||||
const timers: { fn: () => void; ms: number; cleared: boolean }[] = []
|
||||
@@ -55,6 +59,12 @@ function harness(
|
||||
calls.push({ url, init })
|
||||
|
||||
if (url.endsWith('/start')) {
|
||||
const failure = startErrors.shift()
|
||||
|
||||
if (failure) {
|
||||
throw failure
|
||||
}
|
||||
|
||||
const r = options.start ?? { status: 201, body: startBody() }
|
||||
|
||||
return { status: r.status, json: async () => r.body }
|
||||
@@ -154,6 +164,47 @@ describe('aiturk login: start', () => {
|
||||
await expect(harness({ start: { status: 500 } }).login.start()).rejects.toThrow('aiturk-login:unavailable')
|
||||
})
|
||||
|
||||
it('tries start once more after a thrown network error (first request of a long-idle process)', async () => {
|
||||
const h = harness({ startErrors: [new TypeError('fetch failed')] })
|
||||
const pending = h.login.start()
|
||||
|
||||
await vi.waitFor(() => expect(h.timers.some(timer => timer.ms === AITURK_LOGIN_START_RETRY_DELAY_MS)).toBe(true))
|
||||
expect(AITURK_LOGIN_START_RETRY_DELAY_MS).toBe(500)
|
||||
expect(h.calls.filter(call => call.url.endsWith('/start'))).toHaveLength(1)
|
||||
expect(h.openExternal).not.toHaveBeenCalled()
|
||||
h.timers.find(timer => timer.ms === AITURK_LOGIN_START_RETRY_DELAY_MS)!.fn()
|
||||
|
||||
expect(await pending).toEqual({ userCode: 'ABCD2345', verificationUri: URI, expiresAt: 1_000_000 + 600_000 })
|
||||
expect(h.calls.filter(call => call.url.endsWith('/start'))).toHaveLength(2)
|
||||
// One code only: the approval page is opened once.
|
||||
expect(h.openExternal).toHaveBeenCalledExactlyOnceWith(URI)
|
||||
})
|
||||
|
||||
it('gives up after the second network error, and never retries an HTTP answer or a timeout', async () => {
|
||||
const twice = harness({ startErrors: [new TypeError('fetch failed'), new TypeError('fetch failed')] })
|
||||
const failing = twice.login.start().catch(e => e)
|
||||
|
||||
await vi.waitFor(() => expect(twice.timers.some(timer => timer.ms === AITURK_LOGIN_START_RETRY_DELAY_MS)).toBe(true))
|
||||
twice.timers.find(timer => timer.ms === AITURK_LOGIN_START_RETRY_DELAY_MS)!.fn()
|
||||
expect((await failing).message).toBe('aiturk-login:unavailable')
|
||||
expect(twice.calls.filter(call => call.url.endsWith('/start'))).toHaveLength(2)
|
||||
expect(twice.timers.filter(timer => timer.ms === AITURK_LOGIN_START_RETRY_DELAY_MS)).toHaveLength(1)
|
||||
|
||||
for (const status of [500, 503, 429, 422]) {
|
||||
const answered = harness({ start: { status } })
|
||||
|
||||
await expect(answered.login.start()).rejects.toThrow(/^aiturk-login:(?:unavailable|rate-limited)$/)
|
||||
expect(answered.calls.filter(call => call.url.endsWith('/start'))).toHaveLength(1)
|
||||
expect(answered.timers).toHaveLength(0)
|
||||
}
|
||||
|
||||
const timedOut = harness({ startErrors: [new DOMException('The operation was aborted due to timeout', 'TimeoutError')] })
|
||||
|
||||
await expect(timedOut.login.start()).rejects.toThrow('aiturk-login:unavailable')
|
||||
expect(timedOut.calls.filter(call => call.url.endsWith('/start'))).toHaveLength(1)
|
||||
expect(timedOut.timers).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('rejects malformed codes', async () => {
|
||||
await expect(
|
||||
harness({ start: { status: 201, body: startBody({ userCode: 'abcd2345' }) } }).login.start()
|
||||
|
||||
Reference in new issue
Block a user