Files
magpie/providers_unreadable_cli_test.go
yetone 9c54a560cc provider: a providers.json that can't be read is said where the providers are listed, never backed up or synced as none, and is replaced through a rename (PR #415's review) — #415 made edits refuse a file they couldn't read or decode, but every read for the user still took it as empty: magpie providers said "no providers yet", magpie models "no models yet", the Providers page opened the Add sheet as for a first use, and backup.Collect, so an export and a WebDAV/S3 sync, carried it as no providers, which another computer's Mirror would then take as every provider removed. magpie providers and magpie models now list what is left (the signed-in accounts) and end in the error, the TUI says it over its list, and the Providers page shows a "Your providers file can't be read" box (无法读取供应商配置文件) over the list with magpie's reason and no Add sheet; start-up, the gateway and the catalog still go on with it as empty. Stored and StoredGroups return the read error, so a backup and a sync stop on it with nothing pushed. The error, for reads and refused writes alike, names the file, says magpie left it unchanged and to fix it or move it aside (errors.Is ErrUnreadable). store writes through writePrivate, a 0600 temp file renamed over it (steady.Rename waits out a Windows reader holding it, a symlink keeps its target), instead of os.WriteFile's truncate-then-write, and the read goes through steady.ReadFile. TestFileErrorSaysUnreadable, TestWriteErrorsSayLeftUnchanged, TestStoredRefusesUnreadable, TestStoreReplacesAtomically, TestSyncStopsOnUnreadableProviders, TestProvidersCmdSaysUnreadable and provider-file-error.test.cjs fail without the change. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass; provider-file-error, click-scroll, old-webkit, providers-off-fold, add-sheet pass.
2026-10-01 19:45:24 +08:00

69 lines
1.6 KiB
Go

package main
import (
"bytes"
"errors"
"io"
"os"
"strings"
"testing"
"github.com/yetone/magpie/internal/catalog"
"github.com/yetone/magpie/internal/provider"
)
// printed is what fn printed, and the error it ended with.
func printed(t *testing.T, fn func() error) (string, error) {
t.Helper()
r, w, err := os.Pipe()
if err != nil {
t.Fatal(err)
}
old := os.Stdout
os.Stdout = w
var out strings.Builder
done := make(chan struct{})
go func() {
b, _ := io.ReadAll(r)
out.Write(b)
close(done)
}()
err = fn()
w.Close()
os.Stdout = old
<-done
r.Close()
return out.String(), err
}
// A providers.json that can't be read is not "no providers yet": magpie
// providers and magpie models end in why, and leave the file as it is
// (#415's review).
func TestProvidersCmdSaysUnreadable(t *testing.T) {
groupsHome(t)
catalog.Changed = nil
whole, err := os.ReadFile(provider.Path())
if err != nil {
t.Fatal(err)
}
broken := whole[:len(whole)/2]
if err := os.WriteFile(provider.Path(), broken, 0o600); err != nil {
t.Fatal(err)
}
for name, fn := range map[string]func() error{
"providers": providers,
"models": func() error { return models(nil) },
} {
out, err := printed(t, fn)
if !errors.Is(err, provider.ErrUnreadable) || !strings.Contains(err.Error(), "left it unchanged") {
t.Errorf("%s: %v", name, err)
}
if strings.Contains(out, "yet") {
t.Errorf("%s said there are none yet: %q", name, out)
}
}
if after, _ := os.ReadFile(provider.Path()); !bytes.Equal(after, broken) {
t.Fatal("the broken providers.json was changed")
}
}