package projectfile import ( "os" "path/filepath" "runtime" "strings" "testing" ) func TestWriteCreatesTheFileAndItsDirectory(t *testing.T) { path := filepath.Join(t.TempDir(), ".turbo-go", "tools.toml") if err := Write(path, []byte("[[tool]]\n")); err != nil { t.Fatalf("Write() error = %v", err) } if got := readFile(t, path); got != "[[tool]]\n" { t.Errorf("the file holds %q", got) } } func TestWriteReplacesAnExistingFile(t *testing.T) { path := filepath.Join(t.TempDir(), "settings.toml") if err := os.WriteFile(path, []byte("old"), 0o644); err != nil { t.Fatalf("writing the first version: %v", err) } if err := Write(path, []byte("new")); err != nil { t.Fatalf("Write() error = %v", err) } if got := readFile(t, path); got != "new" { t.Errorf("the file holds %q, want the new contents", got) } } func TestTheFileIsReadableByEveryone(t *testing.T) { // These files are meant to be committed and read by a team. CreateTemp // makes a file owner-readable only, and the rename would carry that // through unless the mode is set on the way. if runtime.GOOS == "windows" { t.Skip("file modes do not work this way on Windows") } path := filepath.Join(t.TempDir(), "tools.toml") if err := Write(path, []byte("x")); err != nil { t.Fatalf("Write() error = %v", err) } info, err := os.Stat(path) if err != nil { t.Fatalf("stat: %v", err) } if got := info.Mode().Perm(); got != fileMode { t.Errorf("the file is %o, want %o", got, fileMode) } } func TestWriteLeavesNoTemporaryFileBehind(t *testing.T) { // A failed or finished write must not litter .turbo-go with half-written // files somebody then has to explain. dir := t.TempDir() path := filepath.Join(dir, "tools.toml") if err := Write(path, []byte("x")); err != nil { t.Fatalf("Write() error = %v", err) } entries, err := os.ReadDir(dir) if err != nil { t.Fatalf("reading the directory: %v", err) } for _, entry := range entries { if strings.HasPrefix(entry.Name(), ".projectfile-") { t.Errorf("a temporary file was left behind: %s", entry.Name()) } } if len(entries) != 1 { t.Errorf("the directory holds %d entries, want just the file", len(entries)) } } func TestWriteNamesTheFileInItsError(t *testing.T) { // A path that cannot be a directory: the parent is a regular file. dir := t.TempDir() blocker := filepath.Join(dir, "blocker") if err := os.WriteFile(blocker, nil, 0o644); err != nil { t.Fatalf("writing the blocker: %v", err) } path := filepath.Join(blocker, "tools.toml") err := Write(path, []byte("x")) if err == nil { t.Fatal("Write() accepted a path under a regular file") } if !strings.Contains(err.Error(), path) { t.Errorf("the error does not name the file: %v", err) } } func TestWriteAcceptsEmptyContents(t *testing.T) { path := filepath.Join(t.TempDir(), "empty.toml") if err := Write(path, nil); err != nil { t.Fatalf("Write() error = %v", err) } if got := readFile(t, path); got != "" { t.Errorf("the file holds %q, want nothing", got) } } // readFile returns a file's contents. func readFile(t *testing.T, path string) string { t.Helper() data, err := os.ReadFile(path) if err != nil { t.Fatalf("reading %s: %v", path, err) } return string(data) }