e2e/issuesearch_test.go

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

115 lines · 4404 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	t.Parallel()
 33	inst := startInstance(t)
 34	aliceKey := inst.newKey(t, "alice")
 35	inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
 36	if _, errOut, code := inst.ssh(t, aliceKey, "", "repo", "create", "alice/app"); code != 0 {
 37		t.Fatalf("repo create: %s", errOut)
 38	}
 39
 40	mk := func(title, body string) {
 41		t.Helper()
 42		if _, errOut, code := inst.ssh(t, aliceKey, "", "issue", "create", "alice/app",
 43			"--title", "'"+title+"'", "--body", "'"+body+"'"); code != 0 {
 44			t.Fatalf("issue create: %s", errOut)
 45		}
 46	}
 47	mk("memory leak in the parser", "resident size climbs forever")
 48	mk("flaky test", "the runner times out sometimes")
 49	mk("docs typo", "spelled parser wrong")
 50
 51	// Title match.
 52	if got := issueNumbers(t, inst, aliceKey, "alice/app", "--search", "leak"); len(got) != 1 || got[0] != 1 {
 53		t.Fatalf("title search = %v, want [1]", got)
 54	}
 55	// Body match — the half a title-only search could never reach.
 56	if got := issueNumbers(t, inst, aliceKey, "alice/app", "--search", "climbs"); len(got) != 1 || got[0] != 1 {
 57		t.Fatalf("body search = %v, want [1]", got)
 58	}
 59	// One word in two issues, one in a title and one in a body.
 60	got := issueNumbers(t, inst, aliceKey, "alice/app", "--search", "parser")
 61	if len(got) != 2 {
 62		t.Fatalf("search across title and body = %v, want two issues", got)
 63	}
 64	// Terms are ANDed, not ORed.
 65	if got := issueNumbers(t, inst, aliceKey, "alice/app", "--search", "'parser runner'"); len(got) != 0 {
 66		t.Fatalf("terms ORed rather than ANDed: %v", got)
 67	}
 68	// Search composes with the other filters.
 69	if _, _, code := inst.ssh(t, aliceKey, "", "issue", "close", "alice/app", "1"); code != 0 {
 70		t.Fatal("close failed")
 71	}
 72	if got := issueNumbers(t, inst, aliceKey, "alice/app", "--search", "parser"); len(got) != 1 || got[0] != 3 {
 73		t.Fatalf("search ignored --state open: %v", got)
 74	}
 75	if got := issueNumbers(t, inst, aliceKey, "alice/app", "--search", "parser", "--state", "all"); len(got) != 2 {
 76		t.Fatalf("--state all with search = %v", got)
 77	}
 78
 79	// An edit moves the issue in the index rather than leaving the old
 80	// words matching.
 81	if _, errOut, code := inst.ssh(t, aliceKey, "", "issue", "edit", "alice/app", "2",
 82		"--title", "'renamed entirely'", "--body", "'nothing of the original remains'"); code != 0 {
 83		t.Fatalf("issue edit: %s", errOut)
 84	}
 85	if got := issueNumbers(t, inst, aliceKey, "alice/app", "--search", "flaky", "--state", "all"); len(got) != 0 {
 86		t.Fatalf("edited-away words still match: %v", got)
 87	}
 88	if got := issueNumbers(t, inst, aliceKey, "alice/app", "--search", "renamed", "--state", "all"); len(got) != 1 {
 89		t.Fatalf("new words not indexed: %v", got)
 90	}
 91
 92	// FTS5 operators typed by a person are terms, not syntax errors.
 93	// Single characters are refused for length before they get this far,
 94	// which the "x" case below covers.
 95	for _, q := range []string{"c++", "AND", "NOT", "foo:", "a AND", "b*", "()"} {
 96		if _, errOut, code := inst.ssh(t, aliceKey, "", "issue", "list", "alice/app",
 97			"--search", "'"+q+"'", "--json"); code != 0 {
 98			t.Fatalf("search %q failed: %s", q, errOut)
 99		}
100	}
101	// Too short is a usage error, the same as every other query.
102	if _, errOut, code := inst.ssh(t, aliceKey, "", "issue", "list", "alice/app", "--search", "x"); code != 2 ||
103		!strings.Contains(errOut, "2 to 200 characters") {
104		t.Fatalf("short search: exit %d, %s", code, errOut)
105	}
106
107	// The instance-wide search reaches bodies too now.
108	out, errOut, code := inst.ssh(t, aliceKey, "", "search", "climbs", "--json")
109	if code != 0 {
110		t.Fatalf("search: %s", errOut)
111	}
112	if !strings.Contains(out, "alice/app") || !strings.Contains(out, "memory leak") {
113		t.Fatalf("global body search: %s", out)
114	}
115}