summaryrefslogtreecommitdiff
path: root/internal/apply/apply_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/apply/apply_test.go')
-rw-r--r--internal/apply/apply_test.go42
1 files changed, 42 insertions, 0 deletions
diff --git a/internal/apply/apply_test.go b/internal/apply/apply_test.go
index 64b0176..4bf6c30 100644
--- a/internal/apply/apply_test.go
+++ b/internal/apply/apply_test.go
@@ -250,3 +250,45 @@ func TestChainMadeIsOutermostFirstForNestedDirectories(t *testing.T) {
t.Errorf("Made = %v, want %v (outermost first)", got[0].Made, want)
}
}
+
+// TestMoveFileRefusesOccupiedDestination is item 16 (fix round 2026-09-12,
+// plan 5 Task 2): moveFile must refuse an occupied destination on its own,
+// not merely rely on runFileStep having already checked - the exact
+// arrangement that produced plan 4's Task 5 Critical, where a helper that
+// replaced silently was trusted because some caller had checked. Called
+// directly, bypassing runFileStep's own pre-check entirely.
+func TestMoveFileRefusesOccupiedDestination(t *testing.T) {
+ dir := t.TempDir()
+ src := write(t, filepath.Join(dir, "x.pdf"), "source", 0o644)
+ dst := write(t, filepath.Join(dir, "y.pdf"), "already there", 0o644)
+
+ if err := moveFile(src, dst); err == nil {
+ t.Fatal("moveFile overwrote an existing destination")
+ }
+ if b, err := os.ReadFile(src); err != nil || string(b) != "source" {
+ t.Errorf("moveFile touched its source: %q, %v", b, err)
+ }
+ if b, err := os.ReadFile(dst); err != nil || string(b) != "already there" {
+ t.Errorf("moveFile touched its destination: %q, %v", b, err)
+ }
+}
+
+// TestRenameFileRefusesOccupiedDestination is item 16's other half:
+// runFileStep's bare os.Rename call for the Rename kind was just as
+// unguarded in itself as moveFile was. renameFile is the helper that now
+// carries the same independent guard, called directly here.
+func TestRenameFileRefusesOccupiedDestination(t *testing.T) {
+ dir := t.TempDir()
+ src := write(t, filepath.Join(dir, "x.pdf"), "source", 0o644)
+ dst := write(t, filepath.Join(dir, "y.pdf"), "already there", 0o644)
+
+ if err := renameFile(src, dst); err == nil {
+ t.Fatal("renameFile overwrote an existing destination")
+ }
+ if b, err := os.ReadFile(src); err != nil || string(b) != "source" {
+ t.Errorf("renameFile touched its source: %q, %v", b, err)
+ }
+ if b, err := os.ReadFile(dst); err != nil || string(b) != "already there" {
+ t.Errorf("renameFile touched its destination: %q, %v", b, err)
+ }
+}