Skip to content
quarry Artifact [3d33df13b4]
Login

Artifact [3d33df13b4]

Artifact 3d33df13b45d3c3229bf1f6c8d6f5d1fb468b6743a1e1d06cfaa2faf65a83ad8:


package runner

import (
	"context"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
	"time"
)

// A RUNNER WITH --branch RUNS THAT BRANCH'S CHECK-INS AND REFUSES THE
// REST BEFORE FETCHING THEM.
//
// It is the deploy runner: it holds a production secret, and any
// check-in's pipelines can name its task type, so which check-ins it
// runs is decided here, from the forge's answer, not by the pipeline.
func TestABranchOnlyRunnerRefusesOtherCheckins(t *testing.T) {
	forge := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		h := strings.TrimPrefix(r.URL.Path, "/api/v1/r/alpha/checkin/")
		branch := map[string]string{"t1": "trunk", "b1": "feat"}[h]
		if branch == "" {
			http.NotFound(w, r)
			return
		}
		json.NewEncoder(w).Encode(map[string]string{"hash": h, "branch": branch})
	}))
	defer forge.Close()
	manifest := `{"tasks":{"t-deploy":["sh","-c","echo deployed"]}}`
	fetched := map[string]string{}
	b := &fakeBridge{queue: map[string][]polled{
		"t-deploy": {task("trunk", "t1"), task("branch", "b1"), task("pair", "pair:b1:t1"), task("unknown", "u1")},
	}, resolved: map[string]map[string]any{}, logs: map[string]string{}}
	srv := httptest.NewServer(b.handler())
	defer srv.Close()
	r, err := New(Config{
		Bridge: srv.URL, Token: "WT", Tasks: []string{"t-deploy"}, MaxTasks: 1,
		WorkDir: t.TempDir(), Checkout: recordingCheckout{fakeCheckout{map[string]string{"t1": manifest, "b1": manifest, "pair:b1:t1": manifest, "u1": manifest}}, fetched},
		Heartbeat: 50 * time.Millisecond, Logf: t.Logf,
		Branch: "trunk", BranchOf: ForgeBranch{API: forge.URL + "/api/v1/r/alpha"}.BranchOf,
	})
	if err != nil {
		t.Fatal(err)
	}
	ctx, cancel := context.WithCancel(context.Background())
	done := make(chan struct{})
	go func() { r.Run(ctx); close(done) }()
	for deadline := time.Now().Add(10 * time.Second); time.Now().Before(deadline); time.Sleep(20 * time.Millisecond) {
		b.mu.Lock()
		n := len(b.resolved)
		b.mu.Unlock()
		if n == 4 {
			break
		}
	}
	cancel()
	<-done
	b.mu.Lock()
	defer b.mu.Unlock()
	if got := b.resolved["trunk"]; got["action"] != "complete" || !strings.Contains(b.logs["trunk"], "deployed") {
		t.Errorf("FAIL: a trunk check-in resolved %v with log %q; want it run", got, b.logs["trunk"])
	}
	for _, id := range []string{"branch", "pair", "unknown"} {
		if got := b.resolved[id]; got["action"] != "fail" {
			t.Errorf("FAIL: the %s task resolved %v; want a refusal", id, got)
		}
		if strings.Contains(b.logs[id], "deployed") {
			t.Errorf("FAIL: the %s task ran: %q", id, b.logs[id])
		}
	}
	if len(fetched) != 1 || fetched["t1"] == "" {
		t.Errorf("FAIL: fetched %v; want only t1, nothing of a refused check-in", fetched)
	}
	if _, err := New(Config{Bridge: srv.URL, Token: "WT", Tasks: []string{"x"}, WorkDir: "w", Checkout: fakeCheckout{}, Branch: "trunk"}); err == nil {
		t.Error("FAIL: a branch-only runner with no way to ask the branch was accepted")
	}
	t.Log("ok: the trunk check-in ran; a branch check-in, a merge and an unknown check-in were refused unfetched")
}

// recordingCheckout notes each check-in it fetched.
type recordingCheckout struct {
	fakeCheckout
	fetched map[string]string
}

func (c recordingCheckout) Checkout(ctx context.Context, dir, checkin string) error {
	c.fetched[checkin] = dir
	return c.fakeCheckout.Checkout(ctx, dir, checkin)
}