fix: harden agent writeback safety
This commit is contained in:
@@ -6,6 +6,8 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"codex-agent-manager/internal/codexhome"
|
||||
)
|
||||
|
||||
func TestValidateInvalidTOMLReturnsInvalidAndDoesNotWrite(t *testing.T) {
|
||||
@@ -78,6 +80,62 @@ func TestWriteExpectedHashMismatchRejectsAndLeavesOriginal(t *testing.T) {
|
||||
assertFileContent(t, target, `name = "用户已改"`+"\n")
|
||||
}
|
||||
|
||||
func TestWriteRejectsAgentsDirectoryReplacementBeforeBackup(t *testing.T) {
|
||||
store, target := writebackFixture(t, `name = "旧名称"`+"\n")
|
||||
validation, err := store.ValidateDraft("backend", `name = "新名称"`+"\n")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
root := filepath.Dir(filepath.Dir(target))
|
||||
agentsDir := filepath.Join(root, "agents")
|
||||
realAgentsDir := filepath.Join(root, "agents-real")
|
||||
externalDir := filepath.Join(root, "external")
|
||||
if err := os.MkdirAll(externalDir, 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(externalDir, "backend.toml"), []byte(`name = "外部"`+"\n"), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
writebackTestHookBeforeBackup = func() {
|
||||
if err := os.Rename(agentsDir, realAgentsDir); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.Symlink(externalDir, agentsDir); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
defer func() { writebackTestHookBeforeBackup = nil }()
|
||||
|
||||
_, err = store.WriteDraft("backend", `name = "新名称"`+"\n", validation.CurrentHash)
|
||||
if !errors.Is(err, ErrWriteConflict) && !errors.Is(err, codexhome.ErrForbiddenPath) {
|
||||
t.Fatalf("expected directory replacement to be rejected, got %v", err)
|
||||
}
|
||||
assertFileContent(t, filepath.Join(realAgentsDir, "backend.toml"), `name = "旧名称"`+"\n")
|
||||
assertFileContent(t, filepath.Join(externalDir, "backend.toml"), `name = "外部"`+"\n")
|
||||
}
|
||||
|
||||
func TestWriteRejectsTargetChangeAfterBackup(t *testing.T) {
|
||||
store, target := writebackFixture(t, `name = "旧名称"`+"\n")
|
||||
validation, err := store.ValidateDraft("backend", `name = "新名称"`+"\n")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
writebackTestHookAfterBackup = func() {
|
||||
if err := os.WriteFile(target, []byte(`name = "用户已改"`+"\n"), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
defer func() { writebackTestHookAfterBackup = nil }()
|
||||
|
||||
_, err = store.WriteDraft("backend", `name = "新名称"`+"\n", validation.CurrentHash)
|
||||
if !errors.Is(err, ErrWriteConflict) {
|
||||
t.Fatalf("expected post-backup target change to be rejected, got %v", err)
|
||||
}
|
||||
assertFileContent(t, target, `name = "用户已改"`+"\n")
|
||||
}
|
||||
|
||||
func TestWriteSuccessCreatesBackupAndAtomicallyWrites(t *testing.T) {
|
||||
store, target := writebackFixture(t, `name = "旧名称"`+"\n")
|
||||
validation, err := store.ValidateDraft("backend", `name = "新名称"`+"\n")
|
||||
|
||||
Reference in New Issue
Block a user