feat(jobs): surface background jobs with progress, logs and controls
Add a central job registry that library scans and search index builds report into, so background work is visible instead of buried in the settings page. - backend/jobs: registry with per-job ring-buffer logs, capability-driven controls, and one coalesced JobsChanged snapshot at 4Hz - pause survives restart via a job_state table; a paused scan is adopted back on launch and skipped by the soft scan - top-bar indicator, popover, details drawer and a Jobs page replacing the config page's scan UI; per-library start/stop retained - scan timing breakdown moves into the job log, Full rescan to the Jobs page; delete the orphaned library-manager component Also add cmd/indexbuild and cmd/indexexport so the explore index can be built once centrally rather than by every install, which today streams ~205GB from the ListenBrainz spark dump on first run. indexbuild picks build/refresh/rebuild from index state; the Gitea workflow runs it on push, weekly, or manually and publishes only when content changed. fresh-install no longer defaults YJ_HOME under /tmp: it is tmpfs on most distros, and the import needs ~6GB of real disk. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestResolveUserDirPath_HomeOverride(t *testing.T) {
|
||||
home := t.TempDir()
|
||||
t.Setenv(envHomeOverride, home)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
dt dirType
|
||||
want string
|
||||
}{
|
||||
{name: "config", dt: dirTypeConfig, want: filepath.Join(home, "config")},
|
||||
{name: "data", dt: dirTypeData, want: filepath.Join(home, "data")},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := resolveUserDirPath(tt.dt)
|
||||
if err != nil {
|
||||
t.Fatalf("resolveUserDirPath(%q) returned error: %v", tt.dt, err)
|
||||
}
|
||||
|
||||
if got != tt.want {
|
||||
t.Errorf("resolveUserDirPath(%q) = %q, want %q", tt.dt, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveUserDirPath_NoOverrideUsesOSPath(t *testing.T) {
|
||||
t.Setenv(envHomeOverride, "")
|
||||
|
||||
got, err := resolveUserDirPath(dirTypeConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("resolveUserDirPath returned error: %v", err)
|
||||
}
|
||||
|
||||
// Without the override the path must fall back to the OS-specific
|
||||
// yellowjacket location, not a bare "<home>/config" base dir.
|
||||
if !filepath.IsAbs(got) || !strings.HasSuffix(got, "yellowjacket") {
|
||||
t.Errorf(
|
||||
"resolveUserDirPath fallback = %q, want absolute path ending in %q",
|
||||
got, "yellowjacket",
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user