Files
pad/internal/cli/credentials_bug3810_test.go
T
xarmian 22dcce6e6c fix(cli): a failed stat fails the credentials write instead of skipping the truncate (BUG-3810) (#2181)
writeFileInPlace0600 read a failed f.Stat() as "not a regular file" and
skipped the truncate, so a shorter body left the old tail behind and
credentials.json was corrupt. It now returns the stat error before writing
anything. statOpenFile is the test seam.

Claude-Session: https://claude.ai/code/session_01Rj4CMyK2EqxerMihbGs3Kr
2026-10-14 18:57:28 -04:00

44 lines
1.3 KiB
Go

package cli
import (
"errors"
"os"
"path/filepath"
"testing"
)
// BUG-3810: a failed stat used to read as "not a regular file", which skips
// the truncate, so a shorter body left the old tail behind. It now fails the
// write before anything is written.
func TestBUG3810_FailedStatFailsTheWriteAndLeavesTheFile(t *testing.T) {
path := filepath.Join(t.TempDir(), "credentials.json")
old := `{"credentials": {"a long body that a shorter write would leave a tail of": {}}}`
if err := os.WriteFile(path, []byte(old), 0600); err != nil {
t.Fatal(err)
}
statErr := errors.New("stat failed")
orig := statOpenFile
statOpenFile = func(*os.File) (os.FileInfo, error) { return nil, statErr }
t.Cleanup(func() { statOpenFile = orig })
if err := writeFileInPlace0600(path, []byte(`{}`)); !errors.Is(err, statErr) {
t.Fatalf("write with a failed stat: err = %v, want the stat error", err)
}
got, err := os.ReadFile(path)
if err != nil {
t.Fatal(err)
}
if string(got) != old {
t.Fatalf("a refused write changed the file to %q", got)
}
// CONTROL: with the stat working, the shorter body replaces the file whole.
statOpenFile = orig
if err := writeFileInPlace0600(path, []byte(`{}`)); err != nil {
t.Fatal(err)
}
if got, _ := os.ReadFile(path); string(got) != `{}` {
t.Fatalf("a working write left %q, want {}", got)
}
}