refactor: isolate playback and upload use cases
ci / module boundaries / boundaries (push) Successful in 6s
ci / api / api (push) Successful in 47s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 1m2s
ci / db / db (push) Successful in 1m4s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 1m30s
ci / ts / ts (packages + web) (push) Successful in 2m54s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m14s

This commit is contained in:
mrleo1nid committed 2026-10-22 13:52:16 +03:00
1 parent c84f90e2cc
commit e158558da3
56 files changed
+3337 -2196

No files matched your search

@@ -12,6 +12,7 @@ import (
"os"
"path/filepath"
"strings"
"sync"
"testing"
"time"
@@ -227,12 +228,77 @@ func TestLibraryAndUploads(t *testing.T) {
assert.Empty(t, stale, "only uploading files expire")
up.Audit(ctx, &author, "upload.test", fileID, map[string]any{"ok": true})
// Импорт: связь с файлом библиотеки, место освобождено.
require.NoError(t, up.Transition(ctx, fileID, []uploads.Status{uploads.StatusPendingReview}, uploads.StatusApproved, nil))
require.NoError(t, up.Transition(ctx, fileID, []uploads.Status{uploads.StatusApproved}, uploads.StatusImporting, nil))
require.NoError(t, up.Transition(ctx, fileID, []uploads.Status{uploads.StatusImporting}, uploads.StatusImported,
func(tx pgx.Tx) error { return uploads.MarkImported(ctx, tx, fileID, fileIDs[1]) }))
// A failure writing reviewer metadata must roll back both status and quota.
require.Error(t, up.Decide(ctx, fileID, uuid.New(), false, "invalid reviewer"))
f, err = up.File(ctx, fileID)
require.NoError(t, err)
assert.Equal(t, uploads.StatusPendingReview, f.Status)
assert.Nil(t, f.ReviewedBy)
used, _, err = up.Usage(ctx, author)
require.NoError(t, err)
assert.EqualValues(t, 400, used)
// Import operations retain the status/reason/link and quota invariants.
require.NoError(t, up.Decide(ctx, fileID, author, true, "discard on approval"))
f, err = up.File(ctx, fileID)
require.NoError(t, err)
require.NotNil(t, f.ReviewedBy)
assert.Equal(t, author, *f.ReviewedBy)
assert.Nil(t, f.StatusReason)
require.NoError(t, up.ChangeStatus(ctx, fileID, []uploads.Status{uploads.StatusApproved}, uploads.StatusImporting))
require.NoError(t, up.FailImport(ctx, fileID, "retryable failure"))
f, err = up.File(ctx, fileID)
require.NoError(t, err)
assert.Equal(t, uploads.StatusImportFailed, f.Status)
require.NotNil(t, f.StatusReason)
assert.Equal(t, "retryable failure", *f.StatusReason)
require.NoError(t, up.ChangeStatus(ctx, fileID, []uploads.Status{uploads.StatusImportFailed}, uploads.StatusApproved))
require.NoError(t, up.ChangeStatus(ctx, fileID, []uploads.Status{uploads.StatusApproved}, uploads.StatusImporting))
require.NoError(t, up.CompleteImport(ctx, fileID, src.ID, "Кино - Группа крови.flac", fileIDs[1]))
require.ErrorIs(t, up.CompleteImport(ctx, fileID, src.ID, "ignored.flac", fileIDs[1]), uploads.ErrWrongStatus)
used, _, err = up.Usage(ctx, author)
require.NoError(t, err)
assert.EqualValues(t, 0, used)
// Two reviewers cannot both decide the same pending file or release quota twice.
secondHash := sha256.Sum256([]byte("concurrent decision"))
_, reserved, err := up.CreateBatch(ctx, author, nil, []uploads.NewFile{
{ClientID: "race", Name: "race.flac", Size: 300, SHA256: secondHash[:]},
}, settings)
require.NoError(t, err)
require.Len(t, reserved, 1)
raceID := reserved[0].FileID
require.NotEqual(t, uuid.Nil, raceID)
require.NoError(t, up.ChangeStatus(ctx, raceID, []uploads.Status{uploads.StatusUploading}, uploads.StatusProcessing))
require.NoError(t, up.ChangeStatus(ctx, raceID, []uploads.Status{uploads.StatusProcessing}, uploads.StatusPendingReview))
var wg sync.WaitGroup
results := make(chan error, 2)
for _, reason := range []string{"first", "second"} {
wg.Add(1)
go func(reason string) { defer wg.Done(); results <- up.Decide(ctx, raceID, author, false, reason) }(reason)
}
wg.Wait()
close(results)
success, conflicts := 0, 0
for err := range results {
if err == nil {
success++
} else {
require.ErrorIs(t, err, uploads.ErrWrongStatus)
conflicts++
}
}
assert.Equal(t, 1, success)
assert.Equal(t, 1, conflicts)
used, _, err = up.Usage(ctx, author)
require.NoError(t, err)
assert.Zero(t, used)
f, err = up.File(ctx, raceID)
require.NoError(t, err)
assert.Equal(t, uploads.StatusRejected, f.Status)
require.NotNil(t, f.ReviewedBy)
assert.Equal(t, author, *f.ReviewedBy)
require.NotNil(t, f.StatusReason)
assert.Contains(t, []string{"first", "second"}, *f.StatusReason)
}