Make atomic file replacement crash durable

This commit is contained in:
2026-08-10 17:44:38 +00:00
parent 1dccf5f140
commit 59f3fe3d1d
13 changed files with 502 additions and 233 deletions

View File

@@ -3,6 +3,7 @@ package manifest
import (
"context"
"encoding/json"
"errors"
"os"
"path/filepath"
"strings"
@@ -205,6 +206,30 @@ func TestLocalStoreSaveAtomicPractical(t *testing.T) {
}
}
func TestWriteJSONAtomicallyChecksCancellationBeforeReplacement(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
cancel()
dir := t.TempDir()
path := filepath.Join(dir, "manifest.json")
err := writeJSONAtomically(ctx, path, []byte("{\"session_id\":\"session\"}\n"))
if !errors.Is(err, context.Canceled) {
t.Fatalf("writeJSONAtomically() error = %v, want context cancellation", err)
}
if _, err := os.Stat(path); !os.IsNotExist(err) {
t.Fatalf("replacement destination exists after cancellation: stat err = %v", err)
}
entries, err := os.ReadDir(dir)
if err != nil {
t.Fatalf("ReadDir() error = %v", err)
}
for _, entry := range entries {
if strings.HasPrefix(entry.Name(), ".manifest.json.tmp-") {
t.Fatalf("temporary manifest remained after cancellation: %s", entry.Name())
}
}
}
func TestLoadRejectsInvalidManifest(t *testing.T) {
store := &LocalStore{}
ctx := context.Background()