Skip to content
quarry Artifact [7ab833514b]
Login

Artifact [7ab833514b]

Artifact 7ab833514b652d6b0a016a97455e1746db3b431a75249cc9d4546a06ac17eb7f:


package runner

import (
	"encoding/json"
	"os"
	"path/filepath"
	"regexp"
	"slices"
	"testing"

	"github.com/danmestas/dagnats/dag"
)

// quarry's own CI: every step in its pipeline names a task the runner
// manifest can run (a step no manifest entry serves sits queued until
// it times out), and one of those steps is the race detector, run as
// the same make target a local `make check` runs.
func TestQuarrysPipelineRunsTheRaceTargetAsItsOwnStep(t *testing.T) {
	root := filepath.Join("..", "..")
	b, err := os.ReadFile(filepath.Join(root, ManifestPath))
	if err != nil {
		t.Fatal(err)
	}
	m, err := ParseManifest(b)
	if err != nil {
		t.Fatal(err)
	}
	b, err = os.ReadFile(filepath.Join(root, "ci", "quarry.json"))
	if err != nil {
		t.Fatal(err)
	}
	var pipeline struct {
		Steps []struct {
			ID    string `json:"id"`
			Task  string `json:"task"`
			Retry *struct {
				MaxAttempts *int `json:"max_attempts"`
			} `json:"retry"`
		} `json:"steps"`
	}
	if err := json.Unmarshal(b, &pipeline); err != nil {
		t.Fatal(err)
	}
	// The engine's own reading: the file is a valid workflow, and after
	// a first failed attempt (Attempts == 1) no step is scheduled again
	// (internal/engine/retry_handlers.go retries while Attempts <=
	// MaxAttempts).
	var def dag.WorkflowDef
	if err := json.Unmarshal(b, &def); err != nil {
		t.Fatal(err)
	}
	if err := dag.Validate(def); err != nil {
		t.Fatalf("FAIL: the engine would refuse ci/quarry.json: %v", err)
	}
	for _, st := range def.Steps {
		if p := dag.ResolveRetryPolicy(def, st); p != nil && 1 <= p.MaxAttempts {
			t.Errorf("FAIL: step %q re-runs a failed attempt (max_attempts %d)", st.ID, p.MaxAttempts)
		}
	}
	raceStep := ""
	for _, s := range pipeline.Steps {
		// dagnats counts max_attempts as retries AFTER the first run
		// (retry while attempts <= max_attempts), so 0 is "run once".
		// A step's verdict is never re-rolled; the runner retries the
		// checkout, the only phase that meets infrastructure trouble.
		if s.Retry == nil || s.Retry.MaxAttempts == nil {
			t.Errorf("FAIL: step %q leaves its retry policy implicit", s.ID)
		}
		task, ok := m.Tasks[s.Task]
		if !ok {
			t.Errorf("FAIL: step %q runs task %q, which %s does not name", s.ID, s.Task, ManifestPath)
			continue
		}
		if slices.Equal(task.Argv, []string{"make", "race"}) {
			raceStep = s.ID
		}
	}
	if raceStep == "" {
		t.Fatal("FAIL: no pipeline step runs `make race`")
	}
	mk, err := os.ReadFile(filepath.Join(root, "Makefile"))
	if err != nil {
		t.Fatal(err)
	}
	if !regexp.MustCompile(`(?m)^check:.*\brace\b`).Match(mk) {
		t.Error("FAIL: make check does not run the race target CI runs")
	}
	if !regexp.MustCompile(`(?m)^race:\n(\t.*\n)*\tgo test -race `).Match(mk) {
		t.Error("FAIL: the Makefile has no race target running go test -race")
	}
	t.Logf("ok: step %q runs make race, and make check runs it too", raceStep)
}