package ci
import (
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"strings"
"sync"
"testing"
)
// A RUN'S START IS RECORDED: ITS EVENT, ITS STARTER, EACH JOB'S
// DECLARATION (RFC 0021 §runs). The engine never sees permissions:.
func TestARunRecordsItsEventStarterAndJobs(t *testing.T) {
var mu sync.Mutex
var specs []string
n := 0
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
mu.Lock()
defer mu.Unlock()
switch {
case r.URL.Path == "/v1/ci/compile":
var body struct{ Name, Spec string }
json.NewDecoder(r.Body).Decode(&body)
specs = append(specs, body.Spec)
fmt.Fprintf(w, `{"workflow":{"name":%q},"registered":true,"on":{"tag":{},"workflow_dispatch":{}}}`, body.Name)
case r.URL.Path == "/runs" && r.Method == http.MethodPost:
n++
w.WriteHeader(http.StatusCreated)
fmt.Fprintf(w, `{"run_id":"r%d"}`, n)
case r.URL.Path == "/runs":
fmt.Fprint(w, `[]`)
}
}))
defer srv.Close()
e := New(srv.URL)
release := []byte("on:\n tag: {}\n workflow_dispatch: {}\npermissions:\n uv: write\njobs:\n publish:\n environment: release\n steps:\n - run: ./publish\n")
st := &fakeStore{mark: map[string]string{"alpha": "t1"}, pushedBy: map[string]string{"s0": "tagger"}}
repo := &fakeRepo{
files: []string{"ci/release.yml"},
blobs: map[string][]byte{"ci/release.yml": release},
news: []Checkin{{Hash: "t1", Branch: "trunk", User: "author"}},
}
key := TagKey{Src: "s0", Target: "t1", Name: "version-0.1.0"}
repo.tags = nil
if err := TriggerTags(e, st, "alpha", repo, "u"); err != nil { // first sight
t.Fatal(err)
}
repo.tags = []Tagged{{TagKey: key, At: Checkin{Hash: "t1", Branch: "trunk", User: "author"}}}
if err := TriggerTags(e, st, "alpha", repo, "u"); err != nil {
t.Fatal(err)
}
want := RunStart{Event: "tag", Starter: "tagger", Jobs: map[string]JobGrant{"publish": {Environment: "release", Letters: "y"}}}
if got := st.starts["r1"]; got.Event != want.Event || got.Starter != want.Starter || got.Jobs["publish"] != want.Jobs["publish"] || len(got.Jobs) != 1 {
t.Errorf("FAIL: the tag run recorded %+v; want %+v", got, want)
}
if err := RunAt(e, st, "alpha", repo, "u", "t1", "clicker"); err != nil {
t.Fatal(err)
}
if got := st.starts["r2"]; got.Event != "workflow_dispatch" || got.Starter != "clicker" {
t.Errorf("FAIL: the dispatched run recorded %+v; want workflow_dispatch by clicker", got)
}
for _, s := range specs {
if strings.Contains(s, "permissions") || strings.Contains(s, "uv: write") {
t.Errorf("FAIL: the engine was handed permissions:\n%s", s)
}
}
t.Log("ok: tag and dispatch runs record their event, starter and job grants; the engine never sees permissions:")
}