package store import ( "context" "io/fs" "path/filepath" "testing" "github.com/jmoiron/sqlx" "github.com/pressly/goose/v3" ) // Миграция 00005 переводит существующие `parsed` в `pending`. Статус, который // поставил код, не различавший частичного разбора, ничего не доказывает: под // ним лежат и полностью разобранные доставки, и доставки из одних тренировок с // нулём точек (48 из 99 на живом архиве). Ретеншен, ради которого признак и // заводится, поверил бы им и срезал тела — а для stateOfMind это необратимо. func TestMigrationПрежниеParsedСтановятсяPending(t *testing.T) { db, err := sqlx.Connect("sqlite", dsn(filepath.Join(t.TempDir(), "healthlog.db"))) if err != nil { t.Fatalf("открытие базы: %v", err) } t.Cleanup(func() { _ = db.Close() }) sub, err := fs.Sub(migrationsFS, "migrations") if err != nil { t.Fatalf("миграции: %v", err) } p, err := goose.NewProvider(goose.DialectSQLite3, db.DB, sub) if err != nil { t.Fatalf("провайдер: %v", err) } ctx := context.Background() // Состояние ДО этой миграции: схема четвёртой версии. if _, err := p.UpTo(ctx, 4); err != nil { t.Fatalf("миграция до 4: %v", err) } const insert = ` INSERT INTO delivery (id, received_at, automation_name, automation_id, aggregation, period, session_id, bytes, sha256, raw_path, parse_status, points) VALUES (?, '2025-06-05T10:00:00Z', '', '', '', '', '', 0, '-', '-', ?, ?)` for _, c := range []struct { id string status string points int }{ {"d-parsed-points", "parsed", 12}, {"d-parsed-empty", "parsed", 0}, {"d-failed", "failed", 0}, {"d-pending", "pending", 0}, } { if _, err := db.ExecContext(ctx, insert, c.id, c.status, c.points); err != nil { t.Fatalf("вставка %s: %v", c.id, err) } } if _, err := p.Up(ctx); err != nil { t.Fatalf("миграция до последней: %v", err) } want := map[string]string{ // Переводятся ОБА варианта parsed, а не только пустой: наблюдение // «секции не смешиваются» собрано за двое суток потока, и ставить на // него необратимое удаление тел значит повторять ошибку, ради которой // задача и заведена. "d-parsed-points": "pending", "d-parsed-empty": "pending", "d-failed": "failed", "d-pending": "pending", } for id, wantStatus := range want { var status, sections string err := db.QueryRowContext(ctx, `SELECT parse_status, uncovered_sections FROM delivery WHERE id = ?`, id). Scan(&status, §ions) if err != nil { t.Fatalf("чтение %s: %v", id, err) } if status != wantStatus { t.Errorf("%s: статус %q, ожидался %q", id, status, wantStatus) } if sections != "[]" { t.Errorf("%s: список %q, ожидался `[]`", id, sections) } } }