fix: 修复整合包失败后的完整重试流程
Some checks failed
Axolotl desktop CI / guardrails (push) Has been cancelled
Axolotl desktop CI / desktop (macos-latest) (push) Has been cancelled
Axolotl desktop CI / desktop (ubuntu-latest) (push) Has been cancelled
Axolotl desktop CI / desktop (windows-latest) (push) Has been cancelled
Axolotl desktop CI / website (push) Has been cancelled
Repository checks / typos (push) Has been cancelled
Repository checks / tombi (push) Has been cancelled
Rust checks / shear (push) Has been cancelled
Sync source to CNB / Sync Git ref (push) Has been cancelled
Sync LobeHub models / sync (push) Has been cancelled
Some checks failed
Axolotl desktop CI / guardrails (push) Has been cancelled
Axolotl desktop CI / desktop (macos-latest) (push) Has been cancelled
Axolotl desktop CI / desktop (ubuntu-latest) (push) Has been cancelled
Axolotl desktop CI / desktop (windows-latest) (push) Has been cancelled
Axolotl desktop CI / website (push) Has been cancelled
Repository checks / typos (push) Has been cancelled
Repository checks / tombi (push) Has been cancelled
Rust checks / shear (push) Has been cancelled
Sync source to CNB / Sync Git ref (push) Has been cancelled
Sync LobeHub models / sync (push) Has been cancelled
This commit is contained in:
124
apps/app-frontend/src/helpers/hosted-install-retry.test.ts
Normal file
124
apps/app-frontend/src/helpers/hosted-install-retry.test.ts
Normal file
@ -0,0 +1,124 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import test from 'node:test'
|
||||
|
||||
import {
|
||||
hostedRetryRoute,
|
||||
hostedRuntimeRetryInstanceId,
|
||||
retryInstallJob,
|
||||
} from './hosted-install-retry.ts'
|
||||
import type { InstallJobSnapshot } from './install.ts'
|
||||
|
||||
function job(overrides: Partial<InstallJobSnapshot> = {}): InstallJobSnapshot {
|
||||
return {
|
||||
job_id: 'job',
|
||||
instance_id: 'local:instance',
|
||||
source_instance_id: null,
|
||||
instance_deleted: false,
|
||||
kind: 'install_existing_instance',
|
||||
status: 'failed',
|
||||
execution_mode: 'normal',
|
||||
provider: 'minecraft',
|
||||
target: { type: 'existing_instance', instance_id: 'local:instance' },
|
||||
phase: 'downloading_minecraft',
|
||||
progress: null,
|
||||
details: { type: 'empty' },
|
||||
parallel: null,
|
||||
display: null,
|
||||
error: null,
|
||||
rollback_error: null,
|
||||
pause_reason: null,
|
||||
upgrade_result: null,
|
||||
created: '2026-09-18T00:00:00Z',
|
||||
modified: '2026-09-18T00:00:00Z',
|
||||
finished: '2026-09-18T00:00:01Z',
|
||||
summary: {
|
||||
files_completed: 0,
|
||||
files_total: null,
|
||||
bytes_downloaded: 0,
|
||||
bytes_total: null,
|
||||
speed_bytes_per_second: null,
|
||||
eta_seconds: null,
|
||||
source: null,
|
||||
fallback_count: 0,
|
||||
},
|
||||
items: [],
|
||||
...overrides,
|
||||
}
|
||||
}
|
||||
|
||||
test('StarLight runtime retries keep the existing instance identity', () => {
|
||||
assert.equal(hostedRuntimeRetryInstanceId(job()), 'local:instance')
|
||||
assert.equal(
|
||||
hostedRuntimeRetryInstanceId(
|
||||
job({ instance_id: null, target: { type: 'existing_instance', instance_id: 'target' } }),
|
||||
),
|
||||
'target',
|
||||
)
|
||||
})
|
||||
|
||||
test('deleted and unrelated install jobs do not enter the StarLight runtime retry path', () => {
|
||||
assert.equal(hostedRuntimeRetryInstanceId(job({ instance_deleted: true })), null)
|
||||
assert.equal(hostedRuntimeRetryInstanceId(job({ kind: 'create_instance' })), null)
|
||||
})
|
||||
|
||||
test('the hosted retry link targets the current instance content route', () => {
|
||||
assert.equal(
|
||||
hostedRetryRoute('local:instance/with space'),
|
||||
'/instance/local%3Ainstance%2Fwith%20space',
|
||||
)
|
||||
assert.doesNotMatch(hostedRetryRoute('local:instance'), /\/mods$/)
|
||||
})
|
||||
|
||||
test('a StarLight runtime failure retries the complete hosted transaction only', async () => {
|
||||
const calls: string[] = []
|
||||
const result = await retryInstallJob(job(), {
|
||||
getInstanceMode: async (instanceId) => {
|
||||
calls.push(`mode:${instanceId}`)
|
||||
return 'starlight'
|
||||
},
|
||||
retryHosted: async (instanceId, sourceJobId) => {
|
||||
calls.push(`hosted:${instanceId}:${sourceJobId}`)
|
||||
},
|
||||
retryGeneric: async (jobId) => {
|
||||
calls.push(`generic:${jobId}`)
|
||||
return job()
|
||||
},
|
||||
})
|
||||
assert.equal(result, null)
|
||||
assert.deepEqual(calls, ['mode:local:instance', 'hosted:local:instance:job'])
|
||||
})
|
||||
|
||||
test('a Local runtime failure keeps the generic retry path', async () => {
|
||||
const replacement = job({ job_id: 'replacement', status: 'queued' })
|
||||
const calls: string[] = []
|
||||
const result = await retryInstallJob(job(), {
|
||||
getInstanceMode: async () => 'local',
|
||||
retryHosted: async () => {
|
||||
calls.push('hosted')
|
||||
},
|
||||
retryGeneric: async (jobId) => {
|
||||
calls.push(`generic:${jobId}`)
|
||||
return replacement
|
||||
},
|
||||
})
|
||||
assert.equal(result, replacement)
|
||||
assert.deepEqual(calls, ['generic:job'])
|
||||
})
|
||||
|
||||
test('mode lookup failures never fall back to an incomplete generic retry', async () => {
|
||||
let genericCalls = 0
|
||||
await assert.rejects(
|
||||
retryInstallJob(job(), {
|
||||
getInstanceMode: async () => {
|
||||
throw new Error('instance unavailable')
|
||||
},
|
||||
retryHosted: async () => {},
|
||||
retryGeneric: async () => {
|
||||
genericCalls++
|
||||
return job()
|
||||
},
|
||||
}),
|
||||
/instance unavailable/,
|
||||
)
|
||||
assert.equal(genericCalls, 0)
|
||||
})
|
||||
Reference in New Issue
Block a user