package zitcli
import (
"context"
"errors"
"path/filepath"
"testing"
"craftdesign.group/quarry/internal/lease"
"craftdesign.group/quarry/internal/ops"
)
// zit SAYS `check-in 'X' not found`, AND THAT SENTENCE IS THE SIGNAL.
//
// ArchiveTo tells a version nobody wrote apart from a build that failed
// by matching zit's words, because zit spends the same exit status on
// every user error. Prose is a fragile thing to lean on, so this runs the
// REAL binary and asserts both halves of the match are still there: if a
// zit release rewords it, THIS fails — rather than the forge quietly
// going back to answering "the server is at capacity, retry shortly" for
// a version that will never exist.
//
// The forge's half of that pair is worse than a plain wrong answer: a
// client told to retry something that cannot appear retries forever.
func TestArchiveOfAVersionNobodyWroteIsNoVersion(t *testing.T) {
repo := testRepo(t, 1)
c := New(repo)
err := c.ArchiveTo(context.Background(), "tar", "nosuchversion", "",
filepath.Join(t.TempDir(), "out.tgz"), 1<<30)
if err == nil {
t.Fatal("an archive of a version nobody wrote succeeded")
}
if !errors.Is(err, ErrNoVersion) {
t.Errorf("FAIL: an unknown version is not ErrNoVersion: %v\n"+
"the forge answers 'busy' whenever it cannot tell these apart, so a "+
"reworded zit must break HERE and not there", err)
}
// And a real version still works: a classifier that swallowed
// successes would pass the check above and be useless.
good := filepath.Join(t.TempDir(), "ok.tgz")
if err := c.ArchiveTo(context.Background(), "tar", "trunk", "", good, 1<<30); err != nil {
t.Errorf("a real version failed to archive: %v", err)
}
}
// The listing and the diff say the same thing about a check-in the
// store does not have, and it reads as not found everywhere above: a
// store replaced under its index served every old check-in's diff as a
// 500. A full-length hash, as a stale index names one.
func TestListingAndDiffOfAMissingCheckinAreNotFound(t *testing.T) {
c := New(testRepo(t, 1))
missing := "8d2017d4447b1885e1c42a52a892515700b1a1de393dd013439423b2e70fc561"
_, lsErr := c.Ls(context.Background(), missing)
_, namesErr := c.LsNames(context.Background(), missing)
_, _, diffErr := c.Diff(context.Background(), "trunk", missing)
for name, err := range map[string]error{"ls": lsErr, "ls names": namesErr, "diff": diffErr} {
if !errors.Is(err, ErrNoVersion) || !errors.Is(err, ops.ErrNotFound) {
t.Errorf("FAIL: %s of a missing check-in is not a not-found: %v", name, err)
}
}
if _, err := c.LsNames(context.Background(), "trunk"); err != nil {
t.Errorf("a real check-in failed to list: %v", err)
}
}
// Only zit's sentence for a version nobody wrote reads as one. A damaged
// store's report carries the same words, and must stay a failure: as a
// missing version it would answer 404 and leave the exceptions page.
func TestADamagedStoreIsNotAMissingVersion(t *testing.T) {
for msg, want := range map[string]bool{
"zit ls: check-in '8d2017d4447b' not found": true,
"zit archive: check-in 'nosuchversion' not found": true,
"zit update: cannot read repository: check-in 'abc123' baseline is not found": false,
"zit pull: cannot read repository: check-in 'abc123' not found": false,
"zit sync: the check-in was not found among the frames the peer offered, try again": false,
} {
if got := missingVersion(msg); got != want {
t.Errorf("FAIL: missingVersion(%q) = %v, want %v", msg, got, want)
}
}
}
// A write naming a check-in the store lacks is a failure to do what was
// asked, not a not-found: a CI verdict that cannot be tagged must stay
// visible.
func TestATagOnAMissingCheckinIsAFailure(t *testing.T) {
c := New(testRepo(t, 1))
err := c.TagAdd(lease.Unguarded("test"), "ci-pass", "8d2017d4447b1885e1c42a52a892515700b1a1de393dd013439423b2e70fc561", "", false)
if err == nil {
t.Fatal("a tag on a missing check-in succeeded")
}
if errors.Is(err, ops.ErrNotFound) {
t.Errorf("FAIL: a failed tag write reads as not found: %v", err)
}
}