FileBrowser is published only after its default login is checked (R-1126); the memory refusal says what to do (R-1125)

R-1126 (D11 = A): FileBrowser starts off traefik-public, on its own network felhom-filebrowser; the controller joins it
there, checks this run's default login, and only then puts FileBrowser on traefik-public (files.<domain> served).
FileBrowser's own admin-password setting re-applies at every start (1.5.6 cmd/user.go:50-57, measured on 9202), so it
cannot carry the box's password without overwriting the household's own. Tests red-proved.
R-1125 (D10 = A): err.stacks.not_enough_memory says the memory is held by the installed apps and to remove one or use
a machine with more memory (HU + EN, reworded on purpose). TestR1125_NotEnoughMemorySaysWhatToDo, red-proved.

Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-10-17 12:49:34 +02:00
parent 3e8d027677
commit e3bb775ac5
12 changed files with 231 additions and 17 deletions
@@ -7,6 +7,7 @@ import (
"net/http"
"net/http/httptest"
"path/filepath"
"strings"
"sync"
"testing"
@@ -244,3 +245,54 @@ func TestFileBrowserAdmin_R977_NewRunIsWaitedForInTheSameTick(t *testing.T) {
t.Fatalf("state %q, want generated", s)
}
}
// R-1126 (D11 = A): files.<domain> is published (FileBrowser joined to traefik-public) only AFTER this run's default
// login was checked; a check that cannot finish leaves it unpublished; a later run already published is not touched.
// RED-PROOF (2026-10-17): ensureFileBrowserPublic without the „this run was checked" test → the unreachable case fails.
func TestFileBrowserGate_PublishedOnlyAfterTheCheck(t *testing.T) {
var events []string
fb := &fakeFB{password: "admin"}
inner := fb.handler()
down := false
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if down {
w.WriteHeader(http.StatusBadGateway)
return
}
if r.Method == http.MethodPut {
events = append(events, "password replaced")
}
inner.ServeHTTP(w, r)
}))
defer srv.Close()
m, _, _ := fbManager(t, srv.URL)
run := "run-1"
m.fbStarted = func() string { return run }
nets := map[string]bool{}
m.fbNetOnFn = func(c, n string) bool { return nets[c+"@"+n] }
m.fbNetConnectFn = func(c, n string) error {
nets[c+"@"+n] = true
events = append(events, "connect "+c+" "+n)
return nil
}
// A new run whose FileBrowser does not answer: nothing is published.
down = true
m.fileBrowserGateLocked()
if nets["filebrowser@traefik-public"] {
t.Fatal("published while this run's default login was never checked")
}
// It answers, on its default: replaced first, published after.
down = false
m.fileBrowserGateLocked()
want := []string{"connect felhom-controller felhom-filebrowser", "password replaced", "connect filebrowser traefik-public"}
if strings.Join(events, " | ") != strings.Join(want, " | ") {
t.Fatalf("order:\n got %v\n want %v", events, want)
}
// A new run (a restart) on the box's own password: checked, nothing changes, nothing connected twice.
run = "run-2"
m.fileBrowserGateLocked()
if len(events) != 3 {
t.Fatalf("a checked restart did more: %v", events)
}
}