package apply import ( "os" "path/filepath" "strings" "testing" "github.com/novox/mesh-host/internal/store" ) // Defends novox/hq ADR 0118 with ADR 0102: a file the host wrote whole over one it found is given // its kept original back when it is undeclared — not deleted, which left a machine whose package // manager's configuration a module wrote with no configuration at all once that module was // unassigned. const pacmanFound = "[options]\nArchitecture = auto\n\n[core]\nInclude = /etc/pacman.d/mirrorlist\n" const pacmanMesh = "# written by the mesh\n[options]\nArchitecture = auto\nParallelDownloads = 5\n" // writtenOver is a file found at path with content and mode, then written whole by the mesh. func writtenOver(t *testing.T, content string, mode os.FileMode) (path string, state store.State) { t.Helper() path = filepath.Join(t.TempDir(), "pacman.conf") if err := os.WriteFile(path, []byte(content), mode); err != nil { t.Fatal(err) } if err := os.Chmod(path, mode); err != nil { t.Fatal(err) } _, state = applyKeepingIn(t, wholeDecl(path, pacmanMesh), store.State{}, t.TempDir()) if got := readText(t, path); got != pacmanMesh { t.Fatalf("the mesh's file was not written: %q", got) } return path, state } func TestAFileWrittenOverGetsItsKeptOriginalBackWhenUndeclared(t *testing.T) { path, state := writtenOver(t, pacmanFound, 0o640) rec, _ := state.Find(namesID) if rec.Kept == "" || rec.KeptMode != "0640" { t.Fatalf("the original and how it was found were not recorded: kept %q, mode %q", rec.Kept, rec.KeptMode) } if steps := Plan(somethingElse(t), state, store.OriginDeclared); !strings.Contains(verbs(steps), "restore "+namesID) { t.Errorf("the plan did not say the original goes back: %s", verbs(steps)) } report, after := undeclare(t, state) if got := readText(t, path); got != pacmanFound { t.Fatalf("undeclared, the machine did not get its original back: %q", got) } info, err := os.Stat(path) if err != nil || info.Mode().Perm() != 0o640 { t.Errorf("the original came back with mode %o, it was found 640", info.Mode().Perm()) } o := outcomeOf(report, namesID) if o.Action != "restored" || !strings.Contains(o.Detail, rec.Kept) { t.Errorf("the give-back was reported as %q: %s", o.Action, o.Detail) } if _, still := after.Find(namesID); still { t.Error("the record outlived its declaration") } if _, err := os.Stat(rec.Kept); err != nil { t.Errorf("the kept copy went with the give-back: %v", err) } } func TestAFileTheMeshMadeIsRemovedWhenUndeclared(t *testing.T) { path := filepath.Join(t.TempDir(), "pacman.conf") _, state := applyKeepingIn(t, wholeDecl(path, pacmanMesh), store.State{}, t.TempDir()) if rec, _ := state.Find(namesID); rec.Kept != "" { t.Fatalf("a file that was not there recorded an original at %s", rec.Kept) } report, _ := undeclare(t, state) if _, err := os.Stat(path); !os.IsNotExist(err) { t.Errorf("a file the mesh made outlived its declaration: %v", err) } if got := outcomeOf(report, namesID).Action; got != "removed" { t.Errorf("removal was reported as %q", got) } } func TestAFileWrittenOverAndChangedSinceIsLeftAsItStands(t *testing.T) { path, state := writtenOver(t, pacmanFound, 0o644) edited := pacmanMesh + "IgnorePkg = linux\n" if err := os.WriteFile(path, []byte(edited), 0o644); err != nil { t.Fatal(err) } report, _ := undeclare(t, state) if got := readText(t, path); got != edited { t.Fatalf("the operator's change was clobbered: %q", got) } rec, _ := state.Find(namesID) o := outcomeOf(report, namesID) if o.Action != "kept" || !strings.Contains(o.Detail, "changed on the machine") || !strings.Contains(o.Detail, rec.Kept) { t.Errorf("leaving it was reported as %q: %s", o.Action, o.Detail) } } func TestAFileWhoseKeptOriginalIsMissingIsLeftAndSaysSo(t *testing.T) { path, state := writtenOver(t, pacmanFound, 0o644) rec, _ := state.Find(namesID) if err := os.Remove(rec.Kept); err != nil { t.Fatal(err) } report, _ := undeclare(t, state) if got := readText(t, path); got != pacmanMesh { t.Fatalf("with no original to put back, the file became %q", got) } o := outcomeOf(report, namesID) if o.Action != "kept" || !strings.Contains(o.Detail, "cannot be read at "+rec.Kept) { t.Errorf("leaving it was reported as %q: %s", o.Action, o.Detail) } } func TestAFileWrittenOverAndDeletedSinceIsNotBroughtBack(t *testing.T) { path, state := writtenOver(t, pacmanFound, 0o644) if err := os.Remove(path); err != nil { t.Fatal(err) } report, _ := undeclare(t, state) if _, err := os.Stat(path); !os.IsNotExist(err) { t.Errorf("a file somebody deleted was brought back: %v", err) } if got := outcomeOf(report, namesID).Action; got != "forgotten" { t.Errorf("reported as %q", got) } } func TestAFileWhosePathMovedIsNeverGivenTheOldPathsOriginal(t *testing.T) { // The old path's original stays with the old path's record; the new path keeps its own. oldPath, state := writtenOver(t, pacmanFound, 0o644) newPath := filepath.Join(filepath.Dir(oldPath), "pacman.d.conf") newFound := "# the machine's own at the new path\n" if err := os.WriteFile(newPath, []byte(newFound), 0o644); err != nil { t.Fatal(err) } _, state = applyKeepingIn(t, wholeDecl(newPath, pacmanMesh), state, t.TempDir()) rec, _ := state.Find(namesID) if rec.Kept == "" { t.Fatal("the original at the new path was written over without being kept") } if kept := readText(t, rec.Kept); kept != newFound { t.Fatalf("the new path's record names the wrong original: %q", kept) } // The old path is a former target, given back by the next apply; the new one by undeclaring. undeclare(t, state) if got := readText(t, newPath); got != newFound { t.Errorf("undeclared, the new path holds %q", got) } if got := readText(t, oldPath); got != pacmanFound { t.Errorf("the old path did not get its own original back: %q", got) } } func TestARecordFromBeforeTheModeWasKeptPutsTheOriginalBackAsTheFileStands(t *testing.T) { path, state := writtenOver(t, pacmanFound, 0o644) for i := range state.Resources { state.Resources[i].KeptMode, state.Resources[i].KeptOwner = "", "" } if err := os.Chmod(path, 0o600); err != nil { t.Fatal(err) } report, _ := undeclare(t, state) if got := readText(t, path); got != pacmanFound { t.Fatalf("got %q", got) } info, _ := os.Stat(path) if info.Mode().Perm() != 0o600 { t.Errorf("mode %o, the file stood at 600", info.Mode().Perm()) } if got := outcomeOf(report, namesID).Action; got != "restored" { t.Errorf("reported as %q", got) } }