78c894234c
gates / gates (push) Successful in 46s
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
73 lines
3.0 KiB
Go
73 lines
3.0 KiB
Go
package stacks
|
|
|
|
import (
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
// R-1027 — „delete my data too" must never delete a read-only bind or the shared media library. MEASURED 2026-10-14
|
|
// on Tester 1: removing a throwaway Plex (a READER, `:ro`) with „delete data" deleted drive A's whole
|
|
// `userdata/media`. The test asserts the EFFECT on a real tree: the library's files are still there, the app's own
|
|
// folder is gone.
|
|
//
|
|
// COMPANION RED-PROOF (2026-10-15): make keepOnRemoval return "" → this test fails (the library and the :ro folder are
|
|
// deleted). Restored.
|
|
func TestRemoveStack_R1027_NeverDeletesSharedOrReadOnly(t *testing.T) {
|
|
drive := t.TempDir()
|
|
compose := "services:\n" +
|
|
" app:\n" +
|
|
" image: nginx:1.27\n" +
|
|
" volumes:\n" +
|
|
" - ${HDD_PATH}/appdata/app:/data\n" +
|
|
" - " + drive + "/userdata/media:" + drive + "/userdata/media\n" + // a literal, rw, shared library (the drill shape)
|
|
" - " + drive + "/userdata/documents/scans:/scans:ro\n" + // someone else's folder, read only
|
|
" - app_cfg:/config\n" +
|
|
"volumes:\n" +
|
|
" app_cfg:\n"
|
|
m, _, _ := newR442Manager(t, "app", compose, driveAppYAML(drive), drive)
|
|
own := filepath.Join(drive, "appdata", "app", "state.db")
|
|
film := filepath.Join(drive, "userdata", "media", "movies", "Nosferatu (1922)", "Nosferatu.mkv")
|
|
scan := filepath.Join(drive, "userdata", "documents", "scans", "letter.pdf")
|
|
plantFile(t, own)
|
|
plantFile(t, film)
|
|
plantFile(t, scan)
|
|
|
|
resp, err := m.RemoveStack("app", true, nil)
|
|
if err != nil {
|
|
t.Fatalf("remove: %v", err)
|
|
}
|
|
if exists(own) {
|
|
t.Errorf("the app's OWN folder must be deleted with delete-my-data: %s still exists", own)
|
|
}
|
|
if !exists(film) {
|
|
t.Fatalf("the shared media library was DELETED by an app's removal (R-1027): %s is gone; resp=%+v", film, resp)
|
|
}
|
|
if !exists(scan) {
|
|
t.Fatalf("a read-only bind was DELETED by an app's removal (R-1027): %s is gone", scan)
|
|
}
|
|
}
|
|
|
|
// R-1032 — the safety list knows the userdata layout: the userdata root, the media library and its folders, the
|
|
// download folders. COMPANION RED-PROOF: remove the R-1032 block from ProtectedHDDPaths → this test fails. Restored.
|
|
func TestProtectedHDDPaths_R1032_UserdataLayout(t *testing.T) {
|
|
p := ProtectedHDDPaths("/mnt/felhom-drives/hdd_1")
|
|
for _, want := range []string{
|
|
"/mnt/felhom-drives/hdd_1/userdata",
|
|
"/mnt/felhom-drives/hdd_1/userdata/media",
|
|
"/mnt/felhom-drives/hdd_1/userdata/media/tv",
|
|
"/mnt/felhom-drives/hdd_1/userdata/media/movies",
|
|
"/mnt/felhom-drives/hdd_1/userdata/media/torrents",
|
|
"/mnt/felhom-drives/hdd_1/userdata/media/transmission",
|
|
"/mnt/felhom-drives/hdd_1/userdata/documents",
|
|
"/mnt/felhom-drives/hdd_1/userdata/downloads",
|
|
"/mnt/felhom-drives/hdd_1/media", // the old layout stays protected too
|
|
} {
|
|
if !p[want] {
|
|
t.Errorf("ProtectedHDDPaths does not protect %s", want)
|
|
}
|
|
}
|
|
if p["/mnt/felhom-drives/hdd_1/userdata/media/movies/Some Film"] {
|
|
t.Error("a single film folder is not a top-level folder — the list must stay a list of roots")
|
|
}
|
|
}
|