Skip to content
quarry Artifact [86e6bf6b54]
Login

Artifact [86e6bf6b54]

Artifact 86e6bf6b54af2da1e1757cf6c82becb989b78bfa6cda044debf7f8f4fd516228:


package api

import (
	"encoding/json"
	"errors"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"

	"craftdesign.group/quarry/internal/ops"
	"craftdesign.group/quarry/internal/zitcli"
)

// /api/v1/version says which zit the forge needs and whether the one it
// runs falls short, so an ordering slip in a deploy is visible there.
func TestTheVersionReportNamesTheZitGap(t *testing.T) {
	for _, c := range []struct {
		found string
		short bool
	}{{"0.3.30", true}, {"0.3.31", false}, {"0.4.0", false}} {
		r := ops.NewRegistry()
		if err := RegisterAll(r, Deps{Version: "v", ZitVersion: c.found}); err != nil {
			t.Fatal(err)
		}
		out, err := r.Invoke(as("", ""), "meta.version", versionIn{})
		if err != nil {
			t.Fatal(err)
		}
		b, _ := json.Marshal(out)
		var body map[string]any
		if err := json.Unmarshal(b, &body); err != nil {
			t.Fatal(err)
		}
		if body["zitMin"] != zitcli.MinVersion || body["zitTooOld"] != c.short {
			t.Errorf("FAIL: zit %s: body %s; want zitMin %s, zitTooOld %v", c.found, b, zitcli.MinVersion, c.short)
		}
	}
}

func assertZitTooOld(t *testing.T, err error, found string) {
	t.Helper()
	if !errors.Is(err, ops.ErrUnavailable) {
		t.Fatalf("FAIL: accept on an old zit = %v; want unavailable (503)", err)
	}
	var ref *ops.Refusal
	if !errors.As(err, &ref) || ref.Code != RefusalZitTooOld {
		t.Fatalf("FAIL: no %q refusal: %#v", RefusalZitTooOld, err)
	}
	if ref.Fields["found"] != found || ref.Fields["required"] != zitcli.MinVersion {
		t.Fatalf("FAIL: fields %v; want found %s, required %s", ref.Fields, found, zitcli.MinVersion)
	}
	if !strings.Contains(err.Error(), found) || !strings.Contains(err.Error(), zitcli.MinVersion) {
		t.Fatalf("FAIL: the message does not name both versions: %v", err)
	}
}

// Known at startup: the accept is refused before anything is written.
func TestAnAcceptOnAKnownOldZitIsUnavailableNamingBothVersions(t *testing.T) {
	dir, id, ix, pipeSync := acceptStore(t)
	r := ops.NewRegistry()
	if err := RegisterAll(r, Deps{Version: "v", ZitVersion: "0.3.30", Repos: NewRepos(map[string]*Repo{
		"test": {Index: ix, Zit: zitcli.New(dir), Sync: pipeSync}})}); err != nil {
		t.Fatal(err)
	}
	_, err := r.Invoke(as("dan", "i"), "landing.accept", landingSayIn{Repo: "test", ID: id})
	assertZitTooOld(t, err, "0.3.30")
	if out := zrun(t, dir, "landing", "ls"); !strings.Contains(out, id[:10]) {
		t.Fatalf("FAIL: the landing is not open after a refused accept:\n%s", out)
	}
	t.Logf("ok: %v", err)
}

// Found only when zit refuses the flag: the same refusal, with the
// startup probe's answer unknown.
func TestAnAcceptOnAZitThatRefusesTheFlagIsUnavailable(t *testing.T) {
	dir, id, ix, pipeSync := acceptStore(t)
	real, err := exec.LookPath("zit")
	if err != nil {
		t.Skip("zit not on PATH")
	}
	bin := t.TempDir()
	script := "#!/bin/sh\nif [ \"$1\" = landing ] && [ \"$2\" = accept ]; then\n" +
		"  echo \"zit landing: unknown option '--expect-tip'\" >&2; exit 2\nfi\nexec '" + real + "' \"$@\"\n"
	if err := os.WriteFile(filepath.Join(bin, "zit"), []byte(script), 0o755); err != nil {
		t.Fatal(err)
	}
	t.Setenv("PATH", bin+string(os.PathListSeparator)+os.Getenv("PATH"))
	r := ops.NewRegistry()
	if err := RegisterAll(r, Deps{Version: "v", Repos: NewRepos(map[string]*Repo{
		"test": {Index: ix, Zit: zitcli.New(dir), Sync: pipeSync}})}); err != nil {
		t.Fatal(err)
	}
	_, err = r.Invoke(as("dan", "i"), "landing.accept", landingSayIn{Repo: "test", ID: id})
	assertZitTooOld(t, err, "unknown")
	t.Logf("ok: %v", err)
}