Skip to content
quarry Artifact [90804d5589]
Login

Artifact [90804d5589]

Artifact 90804d5589d8ad5d2a788429bcf8847c651d5e7524d21c12e61e976e8baef841:


package harness

import (
	"io"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"testing"
)

// A repository configures its forge through tracked files in .quarry/,
// the way .github configures GitHub — versioned, clone-carried, and
// rebuilt by reingest, so disposability holds. tickets.json is the
// first tenant: ordered word lists whose LIST ORDER IS SORT ORDER.
//
// This drives the whole loop against a real store: commit the file,
// then see the project's own words in the board's columns, the sorted
// list's order, and the forms' suggestions — and fossil's defaults
// gone from all three.
func TestQuarryDirConfiguresTicketVocabulary(t *testing.T) {
	store := buildStore(t, "vocabseed")

	dir := filepath.Join(store, ".quarry")
	if err := os.MkdirAll(dir, 0o755); err != nil {
		t.Fatal(err)
	}
	cfg := `{"priority": ["P0", "P1", "P2"], "status": ["Todo", "Doing", "Done"]}`
	if err := os.WriteFile(filepath.Join(dir, "tickets.json"), []byte(cfg), 0o644); err != nil {
		t.Fatal(err)
	}
	zrun(t, store, "add", ".quarry/tickets.json")
	zrun(t, store, "commit", "-m", "configure ticket vocabulary")

	zrun(t, store, "ticket", "new", "--user", "vocab",
		"title=second priority", "status=Todo", "priority=P1")
	zrun(t, store, "ticket", "new", "--user", "vocab",
		"title=top priority", "status=Doing", "priority=P0")

	srv := ingestAndServe(t, map[string]string{"alpha": store}, t.TempDir())
	get := func(path string) string {
		resp, err := http.Get(srv.URL + path)
		if err != nil {
			t.Fatal(err)
		}
		defer resp.Body.Close()
		b, err := io.ReadAll(resp.Body)
		if err != nil {
			t.Fatal(err)
		}
		return string(b)
	}

	// The vocabulary is on the API too (0001 §9 parity).
	api := get("/api/v1/r/alpha/ticket-config")
	if !strings.Contains(api, `"P0"`) || !strings.Contains(api, ".quarry/tickets.json") {
		t.Fatalf("ticket.config does not serve the file:\n%s", api)
	}

	// The board's columns are the file's words in the file's order,
	// empty "Done" included; fossil's defaults are gone.
	board := get("/r/alpha/ticket?view=board")
	todo, doing, done := strings.Index(board, ">Todo<"), strings.Index(board, ">Doing<"), strings.Index(board, ">Done<")
	if todo < 0 || doing < 0 || done < 0 || !(todo < doing && doing < done) {
		t.Fatalf("board columns not in the file's order (Todo@%d Doing@%d Done@%d):\n%s",
			todo, doing, done, board)
	}
	if strings.Contains(board, ">Verified<") {
		t.Fatalf("fossil's default columns leaked into a configured repo:\n%s", board)
	}

	// The priority-sorted list leads with P0, though P1 is older.
	list := get("/r/alpha/ticket")
	if p0, p1 := strings.Index(list, "top priority"), strings.Index(list, "second priority"); p0 < 0 || p1 < 0 || p0 > p1 {
		t.Fatalf("P0 did not sort above P1 (P0@%d P1@%d):\n%s", p0, p1, list)
	}

	// The new-ticket form suggests the project's words.
	form := get("/r/alpha/ticket/new")
	if !strings.Contains(form, `<option value="P0">`) || !strings.Contains(form, `<option value="Doing">`) {
		t.Fatalf("form datalists do not offer the project's words:\n%s", form)
	}
	if strings.Contains(form, `<option value="Immediate">`) {
		t.Fatalf("form still suggests fossil's priorities in a configured repo:\n%s", form)
	}

}