e2e/issuesearch_test.go

v1.26.0
gitbay/e2e/issuesearch_test.go history · blame · raw

114 lines · 4390 bytes

  1package e2e
  2
  3import (
  4	"encoding/json"
  5	"strings"
  6	"testing"
  7)
  8
  9// issueNumbers runs issue list with the given flags and returns the numbers.
 10func issueNumbers(t *testing.T, inst *instance, key string, args ...string) []int64 {
 11	t.Helper()
 12	out, errOut, code := inst.ssh(t, key, "", append([]string{"issue", "list"}, append(args, "--json")...)...)
 13	if code != 0 {
 14		t.Fatalf("issue list: %s", errOut)
 15	}
 16	var env struct {
 17		Data []struct {
 18			Number int64 `json:"number"`
 19		} `json:"data"`
 20	}
 21	if err := json.Unmarshal([]byte(out), &env); err != nil {
 22		t.Fatalf("not JSON: %v\n%s", err, out)
 23	}
 24	var ns []int64
 25	for _, d := range env.Data {
 26		ns = append(ns, d.Number)
 27	}
 28	return ns
 29}
 30
 31func TestIssueSearch(t *testing.T) {
 32	inst := startInstance(t)
 33	aliceKey := inst.newKey(t, "alice")
 34	inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
 35	if _, errOut, code := inst.ssh(t, aliceKey, "", "repo", "create", "alice/app"); code != 0 {
 36		t.Fatalf("repo create: %s", errOut)
 37	}
 38
 39	mk := func(title, body string) {
 40		t.Helper()
 41		if _, errOut, code := inst.ssh(t, aliceKey, "", "issue", "create", "alice/app",
 42			"--title", "'"+title+"'", "--body", "'"+body+"'"); code != 0 {
 43			t.Fatalf("issue create: %s", errOut)
 44		}
 45	}
 46	mk("memory leak in the parser", "resident size climbs forever")
 47	mk("flaky test", "the runner times out sometimes")
 48	mk("docs typo", "spelled parser wrong")
 49
 50	// Title match.
 51	if got := issueNumbers(t, inst, aliceKey, "alice/app", "--search", "leak"); len(got) != 1 || got[0] != 1 {
 52		t.Fatalf("title search = %v, want [1]", got)
 53	}
 54	// Body match — the half a title-only search could never reach.
 55	if got := issueNumbers(t, inst, aliceKey, "alice/app", "--search", "climbs"); len(got) != 1 || got[0] != 1 {
 56		t.Fatalf("body search = %v, want [1]", got)
 57	}
 58	// One word in two issues, one in a title and one in a body.
 59	got := issueNumbers(t, inst, aliceKey, "alice/app", "--search", "parser")
 60	if len(got) != 2 {
 61		t.Fatalf("search across title and body = %v, want two issues", got)
 62	}
 63	// Terms are ANDed, not ORed.
 64	if got := issueNumbers(t, inst, aliceKey, "alice/app", "--search", "'parser runner'"); len(got) != 0 {
 65		t.Fatalf("terms ORed rather than ANDed: %v", got)
 66	}
 67	// Search composes with the other filters.
 68	if _, _, code := inst.ssh(t, aliceKey, "", "issue", "close", "alice/app", "1"); code != 0 {
 69		t.Fatal("close failed")
 70	}
 71	if got := issueNumbers(t, inst, aliceKey, "alice/app", "--search", "parser"); len(got) != 1 || got[0] != 3 {
 72		t.Fatalf("search ignored --state open: %v", got)
 73	}
 74	if got := issueNumbers(t, inst, aliceKey, "alice/app", "--search", "parser", "--state", "all"); len(got) != 2 {
 75		t.Fatalf("--state all with search = %v", got)
 76	}
 77
 78	// An edit moves the issue in the index rather than leaving the old
 79	// words matching.
 80	if _, errOut, code := inst.ssh(t, aliceKey, "", "issue", "edit", "alice/app", "2",
 81		"--title", "'renamed entirely'", "--body", "'nothing of the original remains'"); code != 0 {
 82		t.Fatalf("issue edit: %s", errOut)
 83	}
 84	if got := issueNumbers(t, inst, aliceKey, "alice/app", "--search", "flaky", "--state", "all"); len(got) != 0 {
 85		t.Fatalf("edited-away words still match: %v", got)
 86	}
 87	if got := issueNumbers(t, inst, aliceKey, "alice/app", "--search", "renamed", "--state", "all"); len(got) != 1 {
 88		t.Fatalf("new words not indexed: %v", got)
 89	}
 90
 91	// FTS5 operators typed by a person are terms, not syntax errors.
 92	// Single characters are refused for length before they get this far,
 93	// which the "x" case below covers.
 94	for _, q := range []string{"c++", "AND", "NOT", "foo:", "a AND", "b*", "()"} {
 95		if _, errOut, code := inst.ssh(t, aliceKey, "", "issue", "list", "alice/app",
 96			"--search", "'"+q+"'", "--json"); code != 0 {
 97			t.Fatalf("search %q failed: %s", q, errOut)
 98		}
 99	}
100	// Too short is a usage error, the same as every other query.
101	if _, errOut, code := inst.ssh(t, aliceKey, "", "issue", "list", "alice/app", "--search", "x"); code != 2 ||
102		!strings.Contains(errOut, "2 to 200 characters") {
103		t.Fatalf("short search: exit %d, %s", code, errOut)
104	}
105
106	// The instance-wide search reaches bodies too now.
107	out, errOut, code := inst.ssh(t, aliceKey, "", "search", "climbs", "--json")
108	if code != 0 {
109		t.Fatalf("search: %s", errOut)
110	}
111	if !strings.Contains(out, "alice/app") || !strings.Contains(out, "memory leak") {
112		t.Fatalf("global body search: %s", out)
113	}
114}