Files
gastown/internal/util/atomic_test.go
Steve Yegge 213b3bab20 feat: Add atomic write pattern for state files (gt-wled7)
Prevents data loss from concurrent/interrupted state file writes by using
atomic write pattern (write to .tmp, then rename).

Changes:
- Add internal/util package with AtomicWriteJSON/AtomicWriteFile helpers
- Update witness/manager.go saveState to use atomic writes
- Update refinery/manager.go saveState to use atomic writes
- Update crew/manager.go saveState to use atomic writes
- Update daemon/types.go SaveState to use atomic writes
- Update polecat/namepool.go Save to use atomic writes
- Add comprehensive tests for atomic write utilities

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 15:57:53 -08:00

89 lines
2.1 KiB
Go

package util
import (
"os"
"path/filepath"
"testing"
)
func TestAtomicWriteJSON(t *testing.T) {
tmpDir := t.TempDir()
testFile := filepath.Join(tmpDir, "test.json")
// Test basic write
data := map[string]string{"key": "value"}
if err := AtomicWriteJSON(testFile, data); err != nil {
t.Fatalf("AtomicWriteJSON error: %v", err)
}
// Verify file exists
if _, err := os.Stat(testFile); os.IsNotExist(err) {
t.Fatal("File was not created")
}
// Verify temp file was cleaned up
tmpFile := testFile + ".tmp"
if _, err := os.Stat(tmpFile); !os.IsNotExist(err) {
t.Fatal("Temp file was not cleaned up")
}
// Read and verify content
content, err := os.ReadFile(testFile)
if err != nil {
t.Fatalf("ReadFile error: %v", err)
}
if string(content) != "{\n \"key\": \"value\"\n}" {
t.Fatalf("Unexpected content: %s", content)
}
}
func TestAtomicWriteFile(t *testing.T) {
tmpDir := t.TempDir()
testFile := filepath.Join(tmpDir, "test.txt")
// Test basic write
data := []byte("hello world")
if err := AtomicWriteFile(testFile, data, 0644); err != nil {
t.Fatalf("AtomicWriteFile error: %v", err)
}
// Verify content
content, err := os.ReadFile(testFile)
if err != nil {
t.Fatalf("ReadFile error: %v", err)
}
if string(content) != "hello world" {
t.Fatalf("Unexpected content: %s", content)
}
// Verify temp file was cleaned up
tmpFile := testFile + ".tmp"
if _, err := os.Stat(tmpFile); !os.IsNotExist(err) {
t.Fatal("Temp file was not cleaned up")
}
}
func TestAtomicWriteOverwrite(t *testing.T) {
tmpDir := t.TempDir()
testFile := filepath.Join(tmpDir, "test.json")
// Write initial content
if err := AtomicWriteJSON(testFile, "first"); err != nil {
t.Fatalf("First write error: %v", err)
}
// Overwrite with new content
if err := AtomicWriteJSON(testFile, "second"); err != nil {
t.Fatalf("Second write error: %v", err)
}
// Verify new content
content, err := os.ReadFile(testFile)
if err != nil {
t.Fatalf("ReadFile error: %v", err)
}
if string(content) != "\"second\"" {
t.Fatalf("Unexpected content: %s", content)
}
}