Skip to content
quarry Artifact [960346de60]
Login

Artifact [960346de60]

Artifact 960346de60e2dbe2dbe99b5a11cfd16f0426fd79e8fe142df29bf22b3eb001a2:


package ci

import (
	"encoding/json"
	"strings"
	"testing"
)

// The bug: a pipeline was registered under its file's stem alone, so
// two repositories with the same file name shared one definition on the
// engine — and quarry re-registers immediately before every run, so the
// last writer won and a run could execute the other's pipeline.
func TestTwoRepositoriesDoNotShareAPipelineName(t *testing.T) {
	alpha := ScopedName("alpha", "build")
	beta := ScopedName("beta", "build")
	if alpha == beta {
		t.Fatalf("FAIL: %q == %q — the collision this exists to remove", alpha, beta)
	}
	t.Logf("ok: alpha/build and beta/build register apart (%s, %s)", alpha, beta)
}

// The ambiguity a plain dotted join leaves behind, which is the same one
// dagnats documents for its own version keys. Repository names may
// contain dots and so may file stems, so the join alone is not enough.
func TestADottedNameCannotBeConfusedWithAnother(t *testing.T) {
	a := ScopedName("a.b", "c")
	b := ScopedName("a", "b.c")
	if a == b {
		t.Fatalf("FAIL: %q == %q — a fixed-length digest is what separates these", a, b)
	}
	if !strings.HasPrefix(a, "a.b.c.") || !strings.HasPrefix(b, "a.b.c.") {
		t.Fatalf("both should stay readable as a.b.c.<digest>, got %q and %q", a, b)
	}
	t.Logf("ok: %s and %s differ only where they must", a, b)
}

// Stable across calls: the name is recomputed before every run, and a
// name that moved would register a second definition each time.
func TestTheNameIsStable(t *testing.T) {
	if ScopedName("quarry", "quarry") != ScopedName("quarry", "quarry") {
		t.Fatal("FAIL: the name is not stable")
	}
	t.Log("ok: the same pair always names the same workflow")
}

// The readable half is kept, because this is what shows in the engine's
// console and a name nobody can read is a name nobody can debug.
func TestTheNameStaysReadable(t *testing.T) {
	got := ScopedName("quarry", "build")
	if !strings.HasPrefix(got, "quarry.build.") {
		t.Fatalf("FAIL: %q does not lead with the repository and the pipeline", got)
	}
	t.Logf("ok: %s", got)
}

// A JSON pipeline carries its own name, so registering one under
// quarry's name must replace that field and keep everything else — the
// struct knows only what quarry reads, and most of a pipeline is not it.
func TestRenamingAJSONPipelineKeepsTheRest(t *testing.T) {
	def := []byte(`{"name":"build","steps":[{"task":"shell"}],"unknown_field":{"kept":true}}`)
	var raw map[string]any
	if err := json.Unmarshal(def, &raw); err != nil {
		t.Fatal(err)
	}
	raw["name"] = ScopedName("alpha", "build")
	out, err := json.Marshal(raw)
	if err != nil {
		t.Fatal(err)
	}
	var back map[string]any
	if err := json.Unmarshal(out, &back); err != nil {
		t.Fatal(err)
	}
	if back["unknown_field"] == nil {
		t.Fatal("FAIL: a field quarry does not read was dropped by the rename")
	}
	if back["name"] == "build" {
		t.Fatal("FAIL: the name was not replaced")
	}
	t.Log("ok: the rename keeps fields quarry never reads")
}

// A cancelled run is not a verdict. quarry cancels runs itself when a
// newer check-in lands on the same branch, so recording one as a
// failure marks a check-in that was never finished being tested — in a
// tag artifact that clones with the repository.
func TestACancelledRunIsNeitherPassedNorAFailure(t *testing.T) {
	for _, tc := range []struct {
		status             string
		terminal, passed   bool
		cancelled, verdict bool
		why                string
	}{
		{"completed", true, true, false, true, "a finished run earns ci-pass"},
		{"failed", true, false, false, true, "a run that failed earns ci-fail"},
		{"error", true, false, false, true, "an errored run earns ci-fail"},
		{"cancelled", true, false, true, false, "a superseded run earns nothing"},
		{"canceled", true, false, true, false, "the other spelling too"},
		{"running", false, false, false, false, "an unfinished run earns nothing yet"},
	} {
		r := Run{Status: tc.status}
		if r.Terminal() != tc.terminal || r.Passed() != tc.passed || r.Cancelled() != tc.cancelled {
			t.Errorf("FAIL: %q: terminal=%v passed=%v cancelled=%v", tc.status,
				r.Terminal(), r.Passed(), r.Cancelled())
			continue
		}
		// The rule the caller applies: a verdict is written for a
		// terminal run that was not cancelled.
		if got := r.Terminal() && !r.Cancelled(); got != tc.verdict {
			t.Errorf("FAIL: %q: writes a verdict = %v, want %v — %s", tc.status, got, tc.verdict, tc.why)
			continue
		}
		t.Logf("ok: %s", tc.why)
	}
}