Skip to content
quarry Artifact [29af73d5f5]
Login

Artifact [29af73d5f5]

Artifact 29af73d5f5caec11c04116f9d346af13fe040c78a15a83f9b204f96c94586744:


package index

import (
	"strconv"
	"strings"
	"testing"

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

// A wiki page and a technote are EDITED: every revision is its own
// artifact under one stable name. Keyed by artifact hash they piled up,
// and Search's group-by then returned whichever revision matched BEST —
// so a query found text deleted years ago and linked to a page that no
// longer contains it. On the live fleet "terrified" still matched
// spatialite_gui's wiki home, which has not said it since 2016.
//
// Each case folds the revisions in a different order, because a clone
// delivers artifacts in wire order and "the newest arrives last" is not
// something the fold may lean on.
func TestSearchFindsOnlyTheCurrentRevision(t *testing.T) {
	wiki := func(date, body string) string {
		return "D " + date + "\nL Design\nN text/x-markdown\nU dan\nW " +
			strconv.Itoa(len(body)+1) + "\n" + body + "\n\n"
	}
	old := wiki("2019-01-01T10:00:00", "the terrified prototype")
	mid := wiki("2020-01-01T10:00:00", "the cautious prototype")
	cur := wiki("2021-01-01T10:00:00", "the confident release")

	for name, order := range map[string][]string{
		"in order":      {old, mid, cur},
		"newest first":  {cur, mid, old},
		"newest middle": {mid, cur, old},
	} {
		t.Run(name, func(t *testing.T) {
			ix := openIndex(t)
			for i, body := range order {
				art, err := artifact.Parse(seal(body))
				if err != nil || art == nil {
					t.Fatalf("revision %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)
				}
			}

			// One row for the page, not one per revision.
			var rows int
			if err := ix.db.QueryRow(`SELECT count(*) FROM search_doc WHERE kind='wiki'`).
				Scan(&rows); err != nil {
				t.Fatal(err)
			}
			if rows != 1 {
				t.Errorf("search_doc holds %d wiki rows, want 1 — every revision is searchable", rows)
			}

			// The words that were REMOVED must not match. This is the
			// whole bug: the page does not say them any more.
			for _, gone := range []string{"terrified", "cautious"} {
				hits, err := ix.Search(gone, "wiki", 10)
				if err != nil {
					t.Fatal(err)
				}
				if len(hits) != 0 {
					t.Errorf("%q still matches %d rows — it was deleted from the page", gone, len(hits))
				}
			}
			// ...and what the page says now must.
			hits, err := ix.Search("confident", "wiki", 10)
			if err != nil {
				t.Fatal(err)
			}
			if len(hits) != 1 {
				t.Fatalf("the current text matched %d rows, want 1", len(hits))
			}
			if !strings.Contains(hits[0].Snippet, "confident") {
				t.Errorf("snippet = %q, want it from the current revision", hits[0].Snippet)
			}
		})
	}
}