Skip to content
quarry Artifact [079dd90dc4]
Login

Artifact [079dd90dc4]

Artifact 079dd90dc4737ef1d9102233498004eec1348054346c07c6590048b617136e00:


package web

import (
	"net/http/httptest"
	"path/filepath"
	"regexp"
	"strings"
	"testing"

	"craftdesign.group/quarry/internal/api"
	"craftdesign.group/quarry/internal/index"
	"craftdesign.group/quarry/internal/ops"
)

// statServer is a timeline of three check-ins: a counted change with
// both sides, an addition-only one, and one whose lines are unknown.
func statServer(t *testing.T) *httptest.Server {
	t.Helper()
	ix, err := index.Open(filepath.Join(t.TempDir(), "index.db"))
	if err != nil {
		t.Fatal(err)
	}
	t.Cleanup(func() { ix.Close() })
	if err := ix.FoldCheckins([]index.CheckinRecord{
		{Hash: "a1" + strings.Repeat("0", 38), Date: "2026-10-01T10:00:00", User: "u", Comment: "both",
			Changes: []string{"src/a.go", "src/b.go"},
			Lines:   map[string]index.LineCount{"src/a.go": {Added: 160, Removed: 2}, "src/b.go": {Added: 9}}},
		{Hash: "b2" + strings.Repeat("0", 38), Date: "2026-10-02T10:00:00", User: "u", Comment: "adds",
			Parents: []string{"a1" + strings.Repeat("0", 38)},
			Changes: []string{"src/a.go"}, Lines: map[string]index.LineCount{"src/a.go": {Added: 4}}},
		{Hash: "c3" + strings.Repeat("0", 38), Date: "2026-10-03T10:00:00", User: "u", Comment: "unknown",
			Parents: []string{"b2" + strings.Repeat("0", 38)}, Changes: []string{"docs/x.md"}},
	}); err != nil {
		t.Fatal(err)
	}
	r := ops.NewRegistry()
	if err := api.RegisterAll(r, api.Deps{Version: "v",
		Repos: api.NewRepos(map[string]*api.Repo{"alpha": {Index: ix}})}); err != nil {
		t.Fatal(err)
	}
	srv := httptest.NewServer(HandlerWith(r, nil, nil, nil))
	t.Cleanup(srv.Close)
	return srv
}

var statRe = regexp.MustCompile(`<span class="tl-stat" data-hook="checkin-stat" data-files="(\d+)"(?: data-added="(\d+)" data-removed="(\d+)")?[^>]*>(.*?)</span></span>`)

// Every check-in card sizes its change: files always, lines when they
// were counted, and a side that is zero is not printed.
func TestTimelineCardsSizeTheirChange(t *testing.T) {
	srv := statServer(t)
	_, body := getPage(t, srv, "/r/alpha/timeline")
	got := map[string][]string{}
	for _, m := range statRe.FindAllStringSubmatch(body, -1) {
		got[m[1]+"/"+m[2]+"/"+m[3]] = m
	}
	both, ok := got["2/169/2"]
	if !ok || !strings.Contains(both[4], `class="ci-add"`) || !strings.Contains(both[4], `class="ci-del"`) {
		t.Errorf("FAIL: the counted change: %v (all: %v)", both, keys(got))
	}
	if adds, ok := got["1/4/0"]; !ok || strings.Contains(adds[4], `class="ci-del"`) {
		t.Errorf("FAIL: an addition-only change prints a removed side, or none: %v", adds)
	}
	if unk, ok := got["1//"]; !ok || strings.Contains(unk[4], "ci-stat") {
		t.Errorf("FAIL: an uncounted change should show its files alone: %v", unk)
	}
	// A file's history sizes that file's change alone.
	_, body = getPage(t, srv, "/r/alpha/timeline?p=src/b.go")
	if m := statRe.FindAllStringSubmatch(body, -1); len(m) != 1 || m[0][1] != "1" || m[0][2] != "9" {
		t.Errorf("FAIL: src/b.go's history: %v", m)
	}
}

func keys(m map[string][]string) []string {
	var out []string
	for k := range m {
		out = append(out, k)
	}
	return out
}