Files
felhom-controller/controller/internal/mediawire/repair_test.go
T

99 lines
3.7 KiB
Go

package mediawire
import (
"encoding/json"
"io"
"log"
"net/http"
"net/http/httptest"
"strings"
"sync"
"testing"
)
// newLayoutBox: Sonarr runs the NEW template — no /media bind, the drive's media at the same path.
type newLayoutBox struct{ fakeBox }
func (b *newLayoutBox) Mounts(string) map[string]string {
return map[string]string{"/mnt/felhom-drives/hdd_1/userdata/media": "/mnt/felhom-drives/hdd_1/userdata/media"}
}
// TestRepairOldRoot_K7 pins R-1047 with the bench's measured sequence (K7): an install from before bc54c01 stored its
// library at /media/tv; after the update the repair adds the same-path root, moves the series' root with
// moveFiles=false, rescans, and deletes the old root — and a second run does nothing.
//
// COMPANION RED-PROOF (2026-10-15): send moveFiles=true → this test fails (a file move is never allowed). Restored.
func TestRepairOldRoot_K7(t *testing.T) {
var mu sync.Mutex
roots := []map[string]interface{}{{"id": 1, "path": "/media/tv"}}
series := []map[string]interface{}{{"id": 7, "path": "/media/tv/Problem Child"}}
var calls []string
srv := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
mu.Lock()
defer mu.Unlock()
j := func(v interface{}) { _ = json.NewEncoder(rw).Encode(v) }
switch p := r.URL.Path; {
case p == "/api/v3/series":
j(series)
case p == "/api/v3/rootfolder" && r.Method == "GET":
j(roots)
case p == "/api/v3/rootfolder" && r.Method == "POST":
var in map[string]interface{}
_ = json.NewDecoder(r.Body).Decode(&in)
in["id"] = 2
roots = append(roots, in)
calls = append(calls, "add root "+in["path"].(string))
case p == "/api/v3/series/editor":
var in map[string]interface{}
_ = json.NewDecoder(r.Body).Decode(&in)
if in["moveFiles"] != false {
t.Errorf("the repair asked the app to MOVE FILES: %v", in)
}
for _, s := range series {
s["path"] = strings.Replace(s["path"].(string), "/media/tv", in["rootFolderPath"].(string), 1)
}
calls = append(calls, "editor "+in["rootFolderPath"].(string))
case p == "/api/v3/command":
calls = append(calls, "rescan")
case p == "/api/v3/rootfolder/1" && r.Method == "DELETE":
roots = roots[1:]
calls = append(calls, "delete old root")
default:
rw.WriteHeader(404)
}
}))
defer srv.Close()
w := &Wirer{Box: &newLayoutBox{}, Logger: log.New(io.Discard, "", 0), HTTP: srv.Client(), URL: func(string, int) string { return srv.URL }}
var r Result
if err := w.repairOldRoot("sonarr", srv.URL+"/api/v3", nil, "tv", &r); err != nil {
t.Fatal(err)
}
want := "add root /mnt/felhom-drives/hdd_1/userdata/media/tv|editor /mnt/felhom-drives/hdd_1/userdata/media/tv|rescan|delete old root"
if got := strings.Join(calls, "|"); got != want {
t.Fatalf("repair calls:\n got %s\n want %s", got, want)
}
if series[0]["path"] != "/mnt/felhom-drives/hdd_1/userdata/media/tv/Problem Child" {
t.Fatalf("the series' stored path did not move: %v", series[0]["path"])
}
n := len(calls)
if err := w.repairOldRoot("sonarr", srv.URL+"/api/v3", nil, "tv", &r); err != nil {
t.Fatal(err)
}
if len(calls) != n {
t.Fatalf("a second repair run wrote again: %v", calls[n:])
}
}
// The OLD template still running (/media bound): nothing is touched.
func TestRepairOldRoot_OldTemplateUntouched(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
t.Errorf("no call may reach an app still on the old template: %s %s", r.Method, r.URL.Path)
}))
defer srv.Close()
w := &Wirer{Box: &fakeBox{}, Logger: log.New(io.Discard, "", 0), HTTP: srv.Client(), URL: func(string, int) string { return srv.URL }}
var r Result
if err := w.repairOldRoot("sonarr", srv.URL+"/api/v3", nil, "tv", &r); err != nil {
t.Fatal(err)
}
}