Skip to content
quarry Artifact [e303d836e5]
Login

Artifact [e303d836e5]

Artifact e303d836e5016c4de36c99a255bb3ff790fda41389b7eb43601e4d40d1ebfc81:


package index

import (
	"strconv"
	"strings"
	"testing"

	"craftdesign.group/quarry/internal/artifact"
)

// A technote is ONE timeline row however often it is edited — fossil's
// rule, and the one quarry broke. libfossil has 50 technote artifacts
// under 26 event ids; fossil's timeline shows 26 and quarry showed 50,
// which put a superseded title on the page five rows above its own
// correction.
//
// The order matters as much as the count: a clone hands the fold
// artifacts in wire order, so the newest edit is NOT reliably last. Each
// case below folds the same three versions in a different order and
// wants the same answer.
func TestTechnoteEditsCollapseToOneRow(t *testing.T) {
	const ev = "e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1"

	// Three edits of one note. The E card (where the note is FILED)
	// stays put while the D card (when it was written) advances, which
	// is the shape libfossil's eight-edit note has.
	version := func(edited, title, body string) string {
		return "C " + strings.ReplaceAll(title, " ", `\s`) +
			"\nD " + edited +
			"\nE 2013-09-01T10:50:00 " + ev +
			"\nN text/x-markdown\nU stephan\nW " + strconv.Itoa(len(body)+1) + "\n" + body + "\n\n"
	}
	draft := version("2013-08-31T16:08:21", "Milestone: basic diff generation bits are in place", "First cut.")
	middle := version("2013-09-01T10:56:39", "Milestone: basic diff generation bits are in place", "Second cut.")
	final := version("2013-09-01T11:38:15", "Milestone: diff generation bits are in place", "It works.")

	orders := map[string][]string{
		"in order":             {draft, middle, final},
		"newest first":         {final, middle, draft},
		"newest in the middle": {middle, final, draft},
	}
	for name, bodies := range orders {
		t.Run(name, func(t *testing.T) {
			ix := openIndex(t)
			for i, body := range bodies {
				art, err := artifact.Parse(seal(body))
				if err != nil || art == nil {
					t.Fatalf("version %d did not parse: %v", i, err)
				}
				hash := strings.Repeat(string(rune('a'+i)), 64)
				if err := ix.FoldNow([]Parsed{{Hash: hash, Art: art}}); err != nil {
					t.Fatalf("fold %d: %v", i, err)
				}
			}

			tl, _, err := ix.Timeline(TimelineFilter{Limit: 10, Kind: "e"})
			if err != nil {
				t.Fatal(err)
			}
			if len(tl) != 1 {
				t.Fatalf("timeline rows = %d, want 1 — every edit is on the page: %+v", len(tl), tl)
			}
			// The WINNER's title. The draft's wording surviving here is
			// the exact bug: fossil shows the correction.
			if !strings.Contains(tl[0].Comment, "Technote: Milestone: diff generation") ||
				strings.Contains(tl[0].Comment, "basic") {
				t.Errorf("timeline title = %q, want the newest edit's", tl[0].Comment)
			}
			if tl[0].Excerpt != "It works." {
				t.Errorf("excerpt = %q, want the newest edit's body", tl[0].Excerpt)
			}

			counts, err := ix.Counts(true)
			if err != nil {
				t.Fatal(err)
			}
			if counts.Technotes != 1 {
				t.Errorf("Counts.Technotes = %d, want 1 — the tab badge counts artifacts", counts.Technotes)
			}

			// The history keeps every version, which is the other half:
			// collapsing the timeline must not lose the record.
			hist, err := ix.TechnoteHistory(ev)
			if err != nil {
				t.Fatal(err)
			}
			if len(hist) != 3 {
				t.Fatalf("history = %d versions, want 3", len(hist))
			}
			if hist[0].Edited != "2013-09-01T11:38:15" {
				t.Errorf("history[0] = %q, want newest first", hist[0].Edited)
			}
			if hist[2].Edited != "2013-08-31T16:08:21" {
				t.Errorf("history[2] = %q, want oldest last", hist[2].Edited)
			}

			// And a superseded version stays READABLE by its own hash,
			// the way a wiki revision is.
			n, ok, err := ix.TechnoteRevision(ev[:12], hist[2].Hash[:12])
			if err != nil || !ok {
				t.Fatalf("superseded revision unreadable: ok=%v err=%v", ok, err)
			}
			if strings.TrimSpace(n.Body) != "First cut." {
				t.Errorf("revision body = %q, want the draft's", n.Body)
			}
		})
	}
}