navidrome/tests/init_tests.go

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

34 lines
846 B
Go
Raw Permalink Normal View History

2016-02-27 18:42:08 -05:00
package tests
import (
"os"
"path/filepath"
"runtime"
"sync"
2016-02-27 18:42:08 -05:00
"testing"
2016-03-08 18:33:35 -05:00
2020-01-23 19:44:08 -05:00
"github.com/navidrome/navidrome/conf"
fix(artwork): only use image files as local artwork sources (#6180) * fix(playlists): limit local cover paths to images in owner's libraries A local #EXTALBUMARTURL path (absolute or file://) was only checked against the union of all libraries. The artwork resolver then opened it with no further check and served the bytes as the playlist cover, undecoded. Any user who can upload an M3U could read any file under any library root, including libraries they were not granted, through getCoverArt (GHSA-vwq6-xrw5-phpg). resolveImageURL now requires an image extension, and for uploaded playlists (no folder) the library holding the cover must pass the owner's HasLibraryAccess. Scanner and CLI imports keep the all-libraries check, since those files are admin-controlled. resolveLocalFile, used by every file-backed artwork source, now ignores paths without an image extension, which covers playlists stored before this fix that were not resolved yet. openOriginal refuses a stored file-backed row whose path is not an image, so the existing dangling path re-resolves it and the playlist falls back to the generated grid. No migration is needed. * fix(artwork): skip non-image files matched by folder cover patterns Album and disc folder sources opened any file in the folder's image list that matched a cover pattern, without checking its extension. openOriginal now refuses to serve file-backed rows whose path is not an image, so a stored row like that would be refused, re-resolved to the same file, and refused again on every view. The list comes from the scanner, which only records image files, but a database scanned where the OS mime table knows more image types than the serving process could still reach this. Both fromExternalFile variants now skip matches that are not image files, so the album falls back to its next source instead. Also correct the parser comment: a playlist without a folder can come from an API upload or from a CLI import of a file outside all libraries. * fix(artwork): check stored source type before using the resize cache The image-extension check for file-backed rows ran inside openOriginal, which the resize cache skips on a hit. Before the fix, a resized request for a playlist pointing at a non-image file cached the raw bytes, because a failed resize falls back to the original data. After the upgrade the same request still hit that entry and returned the file. serveHash now refuses a file-backed row whose path is not an image before calling serveSource, so both full-size and resized requests go through dangling and re-resolve the item. The stale cache entry is keyed by the old hash and is no longer reachable once the row changes. * test: register mime_types.yaml in test binaries Artwork resolution now skips candidates that are not image files, and model.IsImageFile answers from the process mime table. The server registers the extra image types from resources/mime_types.yaml through a conf hook, but a test binary only does that if it links conf/mime, so the artwork e2e suite fell back to the host table: .jxl resolves on macOS and Linux and does not on Windows, where the #5950 cover spec then found no source. tests.Init now imports conf/mime for its side effect, so every suite that loads the test config sees the same image types as the server. * fix(artwork): drop the image-file guard from the disc art reader The guard was added to both fromExternalFile variants, but disc artwork keeps no state row and is never queued, so it cannot hit the refuse-and-re-resolve loop the guard exists to prevent. The only case where it can fire is a real image whose extension this process's mime table does not know, and there it drops a disc cover that used to work. The album variant keeps the guard, since those resolutions are stored and re-served.
2026-09-20 13:40:14 -04:00
_ "github.com/navidrome/navidrome/conf/mime" // registers mime_types.yaml, so tests see the same image types as the server
2020-01-23 19:44:08 -05:00
"github.com/navidrome/navidrome/log"
2016-02-27 18:42:08 -05:00
)
var once sync.Once
fix: assorted scanner, plugin, and server fixes from the Go 1.27 work (#6050) * fix(plugins): stop the cache janitor when a plugin cache is dropped newCacheService started a ttlcache janitor goroutine that only stopped via the explicit Close() path, so a cache service that was discarded without being closed leaked its janitor for the process lifetime. It now registers the same runtime.AddCleanup safety net that utils/cache.simpleCache already uses. * fix(scanner): stop splitting multi-byte characters when truncating tags sanitize() capped tag values with a byte slice, so a value whose limit falls in the middle of a multi-byte character was stored as invalid UTF-8. defaultMaxTagLength is 1024, which is not a multiple of 3, so any sufficiently long CJK title hit this. Only trailing invalid bytes are trimmed, leaving bad bytes elsewhere in the value untouched. * fix(scanner): store MusicBrainz ids in their canonical form uuid.Parse accepts a UUID wrapped in any two bytes, as well as braced and urn: forms, but sanitize() returned the raw string. A tag like {<mbid>} or a quoted value was therefore persisted with its wrapper into the mbz_* columns, where the exact-match MBID search can never find it. The parsed value is now stored, which also lowercases uppercase ids and adds the dashes to unhyphenated ones. * fix(plugins): parse IPv6 hosts correctly in the websocket allowlist isHostAllowed cut the host at the last colon, which mangles an IPv6 literal: "[::1]:8080" became "[::1]" and "[::1]" became "[:". A plugin manifest could therefore never allow an IPv6 host. It now uses net.SplitHostPort, falling back to unwrapping the brackets when there is no port. * fix(server): serve pprof profiles when a BaseURL is configured net/http/pprof's Index resolves the profile name by trimming "/debug/pprof/" from the raw request path, which never matches once MountRouter prepends the BasePath. Requests for any profile without an explicit chi route fell through to the index page, returning HTML with a 200 instead of the profile. The handler now strips the BasePath first. * test(scanner): run the goroutine leak check unconditionally The scanner suite's goleak check only ran when the GOLEAK env var was set, so it never ran in CI and could not catch a regression. It passes with the existing ignore list, verified over repeated runs, so the gate is removed. * fix(server): close the background image body on a non-200 response serveImage returned early on an unexpected status code without closing the response body, pinning the connection until the 5s client timeout. The nolint:bodyclose above the request suppressed the linter that would have caught it, and its justification only holds on the success path, where the body is handed to the CachedStream wrapper. * test(scanner): repair BenchmarkScan so it can actually run The benchmark failed three ways before reaching its first iteration: it reused a shared temp DB and tried to repoint the default library, it never loaded the config defaults so the scanner got a concurrency of 0, and it lacked the notify ignore that the suite already carries. tests.Init now takes a testing.TB so a benchmark can load the test config the same way the suites do. * refactor(artwork): drop the unused sourceFunc Stringer sourceFunc.String derived a label from the closure's symbol name via reflection, but nothing called it: the trace output builds its candidate labels from explicit strings. Whole-program analysis confirms it is unreachable, and dropping it removes a reflection-based dependency on compiler closure-naming details. * refactor(plugins): reuse extractHostname in the websocket allowlist The IPv6 host parsing added for isHostAllowed duplicated extractHostname, which already lives in the same package and backs the HTTP client's identical allowlist check. Two copies of a security-relevant parser can drift, so the websocket service now calls the existing helper. The port-stripping specs move into the URL Validation block that already covered them. * perf(scanner): bound the tag truncation trim to a partial rune The trim loop dropped every trailing byte that failed to decode, so a value ending in a long run of invalid bytes was walked one byte at a time: a 1 MiB lyrics tag measured 2.58ms against 45ns for a normal cut. A partial rune is at most 3 trailing bytes, so the loop is capped there, which also stops it consuming a pre-existing invalid run. * test: tighten the tests added with the Go 1.27 bugfixes Drop the testItem stub in favour of the package's own cacheKey, register the pprof test profile once at package scope, and replace the hand-rolled goroutine settle loop with Eventually. Also corrects a comment that credited a TestMain the scanner suite does not have. * test(scanner): ignore notify's nonrecursive-tree goroutines on Linux The goroutine leak check only ignored the recursive tree (macOS/FSEvents). Linux CI uses inotify, whose nonrecursive tree leaks dispatch and internal goroutines after Stop(), failing the check. * fix(scanner): avoid a truncation panic when MaxLength is 1 or 2 A value of only UTF-8 continuation bytes drained the partial-rune loop to empty, then sliced value[:-1] and panicked. Break when DecodeLastRune returns size 0 (empty string) by testing size != 1 instead of size > 1. * fix: address Codex review on the pprof base path and scan benchmark - profilerHandler: treat a root BasePath ("/") as no prefix, so http.StripPrefix keeps the leading slash chi needs; without this the profiler 404s when BaseURL is "/". Cover the root case in the test. - BenchmarkScan: make it run regardless of test/benchmark ordering. Add singleton.DeleteInstance so a fresh DB is opened after TestScanner closes the shared one, guard driver registration with sync.Once so the rebuild does not re-Register, and ignore the Ginkgo interrupt-handler and Linux notify goroutines the preceding suite leaves behind. * fix: address Codex round 2 on BasePath trailing slash and benchmark DB cleanup - profilerHandler: trim all trailing slashes (TrimRight), not just a bare "/", so a BaseURL like "/music/" strips correctly instead of 404ing. Cover it in the test. - BenchmarkScan: keep and defer db.Init's closer so the DB is closed before b.TempDir cleanup, which otherwise cannot delete the open SQLite/WAL files on Windows.
2026-08-30 21:24:50 -04:00
func Init(t testing.TB, skipOnShort bool) {
2016-02-27 18:42:08 -05:00
if skipOnShort && testing.Short() {
t.Skip("skipping test in short mode.")
}
once.Do(func() {
_, file, _, _ := runtime.Caller(0)
appPath, _ := filepath.Abs(filepath.Join(filepath.Dir(file), ".."))
confPath, _ := filepath.Abs(filepath.Join(appPath, "tests", "navidrome-test.toml"))
2020-01-26 18:07:06 -05:00
println("Loading test configuration file from " + confPath)
2020-04-26 12:35:26 -04:00
_ = os.Chdir(appPath)
conf.LoadFromFile(confPath)
2017-04-01 10:59:31 -04:00
noLog := os.Getenv("NOLOG")
if noLog != "" {
log.SetLevel(log.LevelError)
}
})
2016-02-27 18:42:08 -05:00
}