Files
pipu-music/services/api/internal/library/scanner_test.go
T
mrleo1nid 75ae2092ad
ci / api / api (push) Successful in 56s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 1m23s
ci / db / db (push) Successful in 1m8s
ci / recommender / recommender (push) Failing after 40s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m25s
ci / ts / ts (packages + web) (push) Successful in 7m20s
feat(library): exclude configured folders from scanning and analysis
2026-10-22 15:51:15 +03:00

132 lines
4.8 KiB
Go

package library
import (
"os"
"path/filepath"
"testing"
"time"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestMatchRenames(t *testing.T) {
a := FileRow{ID: uuid.New(), RelPath: "old/a.flac", Size: 10, ContentHash: []byte{1}}
b := FileRow{ID: uuid.New(), RelPath: "old/b.flac", Size: 10, ContentHash: []byte{2}}
noHash := FileRow{ID: uuid.New(), RelPath: "old/c.flac", Size: 10}
newFiles := []walked{
{rel: "new/a.flac", size: 10, hash: []byte{1}},
{rel: "new/a-copy.flac", size: 10, hash: []byte{1}}, // второй файл с тем же содержимым — новый
{rel: "new/x.flac", size: 11, hash: []byte{2}}, // другой размер — не переименование
{rel: "new/y.flac", size: 10},
}
moves, rest, gone := matchRenames(newFiles, []FileRow{a, b, noHash})
require.Len(t, moves, 1)
assert.Equal(t, a.ID, moves[0].from.ID)
assert.Equal(t, "new/a.flac", moves[0].to.rel)
assert.Equal(t, []string{"new/a-copy.flac", "new/x.flac", "new/y.flac"}, rels(rest))
assert.Equal(t, []uuid.UUID{b.ID, noHash.ID}, []uuid.UUID{gone[0].ID, gone[1].ID})
}
func rels(ws []walked) []string {
var out []string
for _, w := range ws {
out = append(out, w.rel)
}
return out
}
func TestWalkAndHash(t *testing.T) {
root := t.TempDir()
write := func(rel string, size int) {
p := filepath.Join(root, filepath.FromSlash(rel))
require.NoError(t, os.MkdirAll(filepath.Dir(p), 0o755))
data := make([]byte, size)
for i := range data {
data[i] = byte(i)
}
require.NoError(t, os.WriteFile(p, data, 0o644))
}
write("Artist/Album/01 - Song.FLAC", 100)
write("Artist/Album/cover.jpg", 10)
write("Artist/.hidden/02.mp3", 10)
write("_incoming/new.mp3", 10)
write("big.mp3", 3<<20)
found, err := walk(root, nil, func(rel string) bool { return rel == "_incoming/new.mp3" }, nil)
require.NoError(t, err)
assert.ElementsMatch(t, []string{"Artist/Album/01 - Song.FLAC", "big.mp3"}, rels(found))
for _, w := range found {
assert.Equal(t, w.mtime, w.mtime.Truncate(time.Microsecond))
}
h1, err := contentHash(filepath.Join(root, "big.mp3"), 3<<20)
require.NoError(t, err)
require.NoError(t, os.Rename(filepath.Join(root, "big.mp3"), filepath.Join(root, "moved.mp3")))
h2, err := contentHash(filepath.Join(root, "moved.mp3"), 3<<20)
require.NoError(t, err)
assert.Equal(t, h1, h2, "hash survives a rename")
_, err = walk(filepath.Join(root, "nope"), nil, func(string) bool { return false }, nil)
assert.Error(t, err)
}
func TestWalkReportsIncrementalProgress(t *testing.T) {
root := t.TempDir()
for _, dir := range []string{"a-empty", "b-album", ".hidden"} {
require.NoError(t, os.MkdirAll(filepath.Join(root, dir), 0o755))
}
for _, rel := range []string{"b-album/1.mp3", "b-album/2.flac", "b-album/skip.mp3", ".hidden/3.mp3"} {
require.NoError(t, os.WriteFile(filepath.Join(root, filepath.FromSlash(rel)), []byte("audio"), 0o644))
}
var counts, folders []int
var paths []string
found, err := walk(root, nil, func(rel string) bool { return rel == "b-album/skip.mp3" }, func(n, dirs int, path string) {
counts, folders, paths = append(counts, n), append(folders, dirs), append(paths, path)
})
require.NoError(t, err)
require.Len(t, found, 2)
assert.Equal(t, []int{0, 0, 0, 1, 2}, counts, "reports activity before any file is found and after each accepted audio file")
assert.Equal(t, []int{1, 2, 3, 3, 3}, folders)
assert.Equal(t, []string{".", "a-empty", "b-album", "b-album", "b-album"}, paths)
}
func TestProgressDTOIncludesWalkActivity(t *testing.T) {
p := Progress{Phase: "walking", Running: true, Found: 7, WalkedDirs: 4, CurrentDir: "Artist/Album"}
dto := progressDTO(p)
require.NotNil(t, dto.Phase)
assert.Equal(t, "walking", string(*dto.Phase))
assert.Equal(t, p.Found, dto.Found)
require.NotNil(t, dto.WalkedDirs)
assert.Equal(t, p.WalkedDirs, *dto.WalkedDirs)
require.NotNil(t, dto.CurrentDir)
assert.Equal(t, p.CurrentDir, *dto.CurrentDir)
}
func TestParseFFprobe(t *testing.T) {
raw := []byte(`{
"streams": [
{"codec_type": "video", "codec_name": "mjpeg"},
{"codec_type": "audio", "codec_name": "opus", "sample_rate": "48000", "channels": 2,
"tags": {"TITLE": "Song", "ARTIST": "Band feat. Guest", "ALBUM": "Record", "DATE": "2020", "track": "3/10"}}
],
"format": {"duration": "215.004", "bit_rate": "128000", "tags": {"album_artist": "Band"}}
}`)
p, err := parseFFprobe(raw, "/x/Song.OPUS")
require.NoError(t, err)
assert.Equal(t, "opus", p.Format)
assert.Equal(t, "opus", p.Codec)
assert.Equal(t, 215004, p.DurationMs)
assert.Equal(t, 128000, p.Bitrate)
assert.Equal(t, 48000, p.SampleRate)
assert.Equal(t, "Song", p.Tags.Title)
assert.Equal(t, "Band", p.Tags.AlbumArtist)
assert.Equal(t, "3/10", p.Tags.Track)
assert.False(t, p.Lossless())
_, err = parseFFprobe([]byte(`{"streams": [], "format": {}}`), "x.mp3")
assert.Error(t, err)
}