Skip to content
quarry Artifact [e161837c74]
Login

Artifact [e161837c74]

Artifact e161837c7451dac82a1f8e6b888bfcd4ffcb2bec08ee9acc6f00d3bb977bab3d:


package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"sync"
	"testing"

	"craftdesign.group/quarry/internal/api"
	"craftdesign.group/quarry/internal/ci"
	"craftdesign.group/quarry/internal/events"
	"craftdesign.group/quarry/internal/index"
	"craftdesign.group/quarry/internal/ingest"
	"craftdesign.group/quarry/internal/lease"
	"craftdesign.group/quarry/internal/zitcli"
)

// A CLOSED LANDING'S MERGE RUNS ARE CANCELLED.
//
// Accepted or rejected, a landing is decided, and a verdict on its
// merge answers nobody. pikchr-studio's first deploy after a landing
// waited for a merge test started 40 seconds before the accept to run
// to its end, holding the runner the accept's own check-in needed.
func TestAClosedLandingsMergeRunsAreCancelled(t *testing.T) {
	if _, err := exec.LookPath("zit"); err != nil {
		t.Skip("zit not on PATH")
	}
	dir := t.TempDir()
	write := func(name, body string) {
		t.Helper()
		os.MkdirAll(filepath.Dir(filepath.Join(dir, name)), 0o755)
		if err := os.WriteFile(filepath.Join(dir, name), []byte(body), 0o644); err != nil {
			t.Fatal(err)
		}
	}
	zitIn(t, dir, "init", ".")
	write("ci/test.json", `{"name":"test","version":"1.0","steps":[]}`)
	write("f.txt", "root\n")
	zitIn(t, dir, "add", "ci/test.json", "f.txt")
	zitIn(t, dir, "commit", "-m", "root", "--user", "u")
	write("g.txt", "feat\n")
	zitIn(t, dir, "add", "g.txt")
	zitIn(t, dir, "commit", "-m", "feat", "--branch", "feat", "--user", "u")
	cli := zitcli.New(dir)
	id, err := cli.LandingOpen(lease.Unguarded("test"), "dan", "feat", "trunk", "land feat", "")
	if err != nil {
		t.Fatal(err)
	}
	ev, err := events.Start(filepath.Join(t.TempDir(), "nats"))
	if err != nil {
		t.Fatal(err)
	}
	t.Cleanup(ev.Close)
	ix, err := index.Open(filepath.Join(t.TempDir(), "index.db"))
	if err != nil {
		t.Fatal(err)
	}
	t.Cleanup(func() { ix.Close() })
	pipe := ingest.NewPipeline(cli, ev, ix, "r", "n1")
	if _, err := pipe.Sync(); err != nil {
		t.Fatal(err)
	}

	var mu sync.Mutex
	var cancelled []string
	runs := 0
	labels := map[string]map[string]string{}
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		mu.Lock()
		defer mu.Unlock()
		switch {
		case r.URL.Path == "/workflows":
			w.WriteHeader(http.StatusCreated)
			w.Write([]byte(`{"status":"registered"}`))
		case r.URL.Path == "/runs" && r.Method == http.MethodPost:
			var body struct {
				Labels map[string]string `json:"labels"`
			}
			json.NewDecoder(r.Body).Decode(&body)
			runs++
			labels[fmt.Sprintf("r-%d", runs)] = body.Labels
			w.WriteHeader(http.StatusCreated)
			fmt.Fprintf(w, `{"run_id":"r-%d"}`, runs)
		case r.URL.Path == "/runs":
			w.Write([]byte(`[]`))
		case strings.HasSuffix(r.URL.Path, "/cancel"):
			cancelled = append(cancelled, strings.TrimSuffix(strings.TrimPrefix(r.URL.Path, "/runs/"), "/cancel"))
		case strings.HasPrefix(r.URL.Path, "/runs/"):
			id := strings.TrimPrefix(r.URL.Path, "/runs/")
			json.NewEncoder(w).Encode(ci.Run{RunID: id, Status: "running", Labels: labels[id]})
		default:
			http.NotFound(w, r)
		}
	}))
	t.Cleanup(srv.Close)
	rg := newTestRegistrar(t, 25)
	rg.engine = ci.New(srv.URL)
	rg.deps = &api.Deps{Repos: api.NewRepos(map[string]*api.Repo{"r": {Index: ix, Zit: cli}})}

	rg.landingSweep("r", ix) // seeds the open set, and starts the landing's merge run
	mu.Lock()
	started := runs
	mu.Unlock()
	if started != 1 {
		t.Fatalf("FAIL: the open landing started %d merge runs, want 1", started)
	}
	rg.landingSweep("r", ix)
	mu.Lock()
	if len(cancelled) != 0 {
		t.Fatalf("FAIL: an open landing's merge run was cancelled: %v", cancelled)
	}
	mu.Unlock()

	zitIn(t, dir, "landing", "reject", id, "--user", "dan")
	if _, err := pipe.Sync(); err != nil {
		t.Fatal(err)
	}
	rg.landingSweep("r", ix)
	mu.Lock()
	defer mu.Unlock()
	if len(cancelled) != 1 || cancelled[0] != "r-1" {
		t.Fatalf("FAIL: after the landing closed, cancelled %v; want its merge run r-1", cancelled)
	}
	t.Log("ok: the merge run of an open landing kept, of a closed one cancelled")
}