package backup import ( "io" "log" "os" "path/filepath" "testing" "gitea.dooplex.hu/admin/felhom-controller/internal/config" ) // exclFake is volDumpFakeProvider plus the production adapter's exclusion answer. type exclFake struct { volDumpFakeProvider excluded []string } func (f *exclFake) GetExcludedDockerVolumes(string) []string { return f.excluded } // R-49 (D21 = B) — the consequence across a whole dump run, through the F7 tar seam (no Docker): the excluded cache // volume is not dumped, the other volume is, and the cache's OLD tar (from before the template marked it) is gone // from the unit, so no later copy carries it. // RED-PROOF: with DumpAppVolumes listing GetDockerVolumes again (no exclusion), the cache is dumped and its old tar // replaced, not removed — seen failing 2026-10-22. func TestR49_AnExcludedVolumeLeavesTheUnit(t *testing.T) { drive := t.TempDir() cfg := &config.Config{} cfg.Paths.SystemDataPath = drive fake := &exclFake{volDumpFakeProvider: volDumpFakeProvider{ stacks: []StackSummary{{Name: "immich"}}, volumes: map[string][]string{"immich": {"immich_immich_ml_cache", "immich_immich_postgres_data"}}, }, excluded: []string{"immich_immich_ml_cache"}} var tarred []string m := &Manager{cfg: cfg, logger: log.New(io.Discard, "", 0), systemDataPath: drive, stackProvider: fake, tarVolume: func(vol, dir string) ([]byte, error) { tarred = append(tarred, vol) return nil, os.WriteFile(filepath.Join(dir, vol+".tar.tmp"), []byte("TAR "+vol), 0o644) }} dumpDir := AppVolumeDumpPath(m.namespaceRoot(drive), "immich") os.MkdirAll(dumpDir, 0o755) old := filepath.Join(dumpDir, "immich_immich_ml_cache.tar") os.WriteFile(old, []byte("824 MB OF MODELS"), 0o644) if err := m.DumpAppVolumes("immich"); err != nil { t.Fatalf("DumpAppVolumes: %v", err) } if len(tarred) != 1 || tarred[0] != "immich_immich_postgres_data" { t.Fatalf("dumped %v, want only the database volume", tarred) } if _, err := os.Stat(old); !os.IsNotExist(err) { t.Fatal("the excluded cache's old tar is still in the unit — every later copy would carry it") } if _, err := os.Stat(filepath.Join(dumpDir, "immich_immich_postgres_data.tar")); err != nil { t.Fatalf("the database volume's tar is missing: %v", err) } } // A provider without the exclusion answer (every test fake, an older wiring) dumps every volume, as before. func TestR49_NoExclusionProviderDumpsEverything(t *testing.T) { m := &Manager{logger: log.New(io.Discard, "", 0), stackProvider: &volDumpFakeProvider{ volumes: map[string][]string{"a": {"a_x", "a_y"}}}} if got := m.dumpableVolumes("a"); len(got) != 2 { t.Fatalf("dumpable = %v, want both", got) } }