fix(downloader): clear eta after transfer completion

This commit is contained in:
saltbo
2026-06-06 18:45:41 -04:00
parent 00f42e762f
commit 4579efda67
2 changed files with 77 additions and 0 deletions
@@ -499,6 +499,41 @@ describe('Download tasks API integration', () => {
torrent: { infoHash: 'patch-info-hash', name: 'patch-progress' },
trackers: [{ url: 'udp://tracker.example/announce', status: 'working', seeds: 2 }],
})
const completedRes = await app.request(`/api/download-tasks/${task.id}`, {
method: 'PATCH',
headers: downloaderHeaders,
body: JSON.stringify({
status: 'completed',
runtime: { phase: 'completed' },
}),
})
expect(completedRes.status).toBe(200)
const completedTask = (await completedRes.json()) as DownloadTask
expect(completedTask.status.runtime).toMatchObject({
phase: 'completed',
etaSeconds: null,
})
const seedingRes = await app.request(`/api/download-tasks/${task.id}`, {
method: 'PATCH',
headers: downloaderHeaders,
body: JSON.stringify({
runtime: {
phase: 'seeding',
seeding: { active: true, uploadedBytes: 1024, uploadBytesPerSecond: 128 },
},
}),
})
expect(seedingRes.status).toBe(200)
const seedingTask = (await seedingRes.json()) as DownloadTask
expect(seedingTask.status.runtime).toMatchObject({
engine: 'aria2',
phase: 'seeding',
etaSeconds: null,
torrent: { infoHash: 'patch-info-hash', name: 'patch-progress' },
seeding: { active: true, uploadedBytes: 1024, uploadBytesPerSecond: 128 },
})
})
it('returns storage failure details when multipart upload session creation fails', async () => {
@@ -968,6 +1003,45 @@ describe('Download tasks API integration', () => {
runtime: null,
},
})
const downloadingAfterRestartRes = await app.request(`/api/download-tasks/${createdTask.id}`, {
method: 'PATCH',
headers: downloaderHeaders,
body: JSON.stringify({
...transferProgress({ downloadBytes: 1024, totalBytes, downloadBps: 256 }),
runtime: { phase: 'downloading', etaSeconds: 36 },
}),
})
expect(downloadingAfterRestartRes.status).toBe(200)
const completedAfterRestartRes = await app.request(`/api/download-tasks/${createdTask.id}`, {
method: 'PATCH',
headers: downloaderHeaders,
body: JSON.stringify({
status: 'completed',
runtime: { phase: 'completed' },
}),
})
expect(completedAfterRestartRes.status).toBe(200)
await expect(completedAfterRestartRes.json()).resolves.toMatchObject({
status: {
runtime: { phase: 'completed', etaSeconds: null },
},
})
const seedingAfterRestartRes = await app.request(`/api/download-tasks/${createdTask.id}`, {
method: 'PATCH',
headers: downloaderHeaders,
body: JSON.stringify({
runtime: { phase: 'seeding', seeding: { active: true } },
}),
})
expect(seedingAfterRestartRes.status).toBe(200)
await expect(seedingAfterRestartRes.json()).resolves.toMatchObject({
status: {
runtime: { phase: 'seeding', etaSeconds: null, seeding: { active: true } },
},
})
})
it('uses transitional states for downloading task pause and cancel actions', async () => {
+3
View File
@@ -579,6 +579,9 @@ function nextTaskRuntime(
function mergeTaskRuntimePatch(current: DownloadTaskRuntime | null, patch: DownloadTaskRuntime): DownloadTaskRuntime {
const merged: DownloadTaskRuntime = { ...(current ?? {}), ...patch }
if (patch.phase === 'completed' || patch.phase === 'seeding') {
merged.etaSeconds = null
}
if (current?.progress || patch.progress) {
merged.progress = mergeTaskProgress(current?.progress, patch.progress)
}