Жизненный цикл: claim-токен распознавания, индексация хешей, retry сломанного торрента
Три мелких фикса из docs/backlog/review-lifecycle-minor.md (ревью Fable 2026-07-08). MINOR-9 (I/O под глобальным w.mu) осознанно waive для one-user home-сервера — не трогаем. MINOR-8: claim-токен распознавания. recognizeOne фиксирует updated_at на момент claim (перечитывая запись после перехода в recognizing), а finishRecognition коммитит результат, только если токен совпал. Иначе за время LLM-вызова задачу увели из recognizing и вернули обратно (cancel → relink revive) — это уже другой эпизод, устаревший результат отбрасываем, задача остаётся в recognizing для перезапуска поллингом. NIT-11: lookup-мапы (byHash/live/torrentByInfohash) больше не индексируют усечённый 40-hex t.Hash v2-only торрентов. Новый хелпер torrentIndexHashes зеркалит выбор torrentHashes: t.Hash берём только при отсутствии обоих infohash_v1/v2. Убирает теоретический ложный матч по коллизии длины. NIT-12: retry живого, но сломанного торрента (error/missingFiles) теперь отклоняется с подсказкой починить раздачу (recheck) в qBittorrent, вместо бессмысленной переотдачи источника (сверка тут же вернула бы задачу в failed). Повторный Add — только когда раздачи в qBittorrent нет. Меняет спеку state-reconciliation → дельта openspec/changes/2026-07-17-retry-reject-broken-torrent (не архивировал). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -9,15 +9,16 @@ import (
|
||||
)
|
||||
|
||||
// TestPollBuildsLiveSnapshot: после Poll снимок несёт телеметрию качающейся и
|
||||
// сидирующей задач (с верным Seeding), доступную по любому из трёх хэшей;
|
||||
// сидирующей задач (с верным Seeding), доступную по любому из infohash;
|
||||
// неизвестный/пустой infohash → ok=false.
|
||||
func TestPollBuildsLiveSnapshot(t *testing.T) {
|
||||
qb := &fakeQbt{torrents: []qbt.Torrent{
|
||||
// Качается (Category пуст → discover не усыновляет, store не мешает).
|
||||
{Hash: "aaa", State: "downloading", Progress: 0.5, Dlspeed: 1000, Eta: 120, TotalSize: 4096},
|
||||
// Сидирует, торрент v2 (три ключа).
|
||||
// Сидирует, гибридный торрент: t.Hash дублирует v1 (как отдаёт qBittorrent),
|
||||
// индексируем по infohash_v1/v2 (усечённый t.Hash не индексируем — NIT-11).
|
||||
{
|
||||
Hash: "bbb", InfohashV1: "bbb1", InfohashV2: "BBB2",
|
||||
Hash: "bbb1", InfohashV1: "bbb1", InfohashV2: "BBB2",
|
||||
State: "uploading", Progress: 1.0, TotalSize: 8192,
|
||||
Ratio: 2.5, NumSeeds: 3, NumLeechs: 1, Uploaded: 999, Upspeed: 50,
|
||||
},
|
||||
@@ -42,8 +43,8 @@ func TestPollBuildsLiveSnapshot(t *testing.T) {
|
||||
t.Errorf("полный размер качающейся задачи: got %d, want 4096", dl.TotalSize)
|
||||
}
|
||||
|
||||
// Сидирующая задача находится по любому из трёх хэшей (lowercase).
|
||||
for _, h := range []string{"bbb", "bbb1", "BBB2", "bbb2"} {
|
||||
// Сидирующая задача находится по любому из своих infohash (lowercase).
|
||||
for _, h := range []string{"bbb1", "BBB2", "bbb2"} {
|
||||
sd, ok := w.Live(h)
|
||||
if !ok {
|
||||
t.Fatalf("нет телеметрии раздачи по ключу %q", h)
|
||||
|
||||
Reference in New Issue
Block a user