vole
Clone
Read only, no credentials:
https://rodent.software/vole.git
Write over SSH with a registered key:
git clone ssh://git@rodent.software:2222/vole.git
A free and open-source forge that lives in your Git repo.
Search for an issue when linking it to a change
838f282eebf7e68f61318eae93d03263df1341df · Keenan Nemetz · 11d ago · parent 267feec2
Search for an issue when linking it to a change The link field only accepted an id that the author already knew. It is now a search box over the issue index that matches a title, a #number, or a ULID prefix. A matching issue is a button: picking one posts the same link write and replaces the Issues section with the new list. Issues already linked are left out of the results.
forge/index/index_test.go +60
| @@ -7,9 +7,69 @@ package index | |||
| 7 | 7 | ||
| 8 | 8 | import ( | |
| 9 | 9 | "path/filepath" | |
| 10 | "slices" | ||
| 10 | 11 | "testing" | |
| 11 | 12 | ) | |
| 12 | 13 | ||
| 14 | func TestSearchIssuesMatchesTitleNumberAndIDPrefix(t *testing.T) { | ||
| 15 | t.Parallel() | ||
| 16 | s, err := Open(filepath.Join(t.TempDir(), "index.db")) | ||
| 17 | if err != nil { | ||
| 18 | t.Fatal(err) | ||
| 19 | } | ||
| 20 | defer s.Close() | ||
| 21 | |||
| 22 | const ( | ||
| 23 | first = "01M5JR2JK29KKMAA1T9MDNTPBP" | ||
| 24 | second = "01M5JR2JK29KKMAA1T9MDNTRBZ" | ||
| 25 | ) | ||
| 26 | for _, issue := range []struct { | ||
| 27 | id string | ||
| 28 | title string | ||
| 29 | number int | ||
| 30 | }{ | ||
| 31 | {first, "Quick retry on timeout", 42}, | ||
| 32 | {second, "Slow parser", 43}, | ||
| 33 | } { | ||
| 34 | if _, err := s.db.Exec( | ||
| 35 | `INSERT INTO issues(repo_id, id, title, state, author, created) VALUES('r1',?,?,'open','keenan',0)`, | ||
| 36 | issue.id, issue.title); err != nil { | ||
| 37 | t.Fatal(err) | ||
| 38 | } | ||
| 39 | if _, err := s.db.Exec( | ||
| 40 | `INSERT INTO entity_numbers(repo_id, number, kind, id) VALUES('r1',?,'issue',?)`, | ||
| 41 | issue.number, issue.id); err != nil { | ||
| 42 | t.Fatal(err) | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 46 | for _, test := range []struct { | ||
| 47 | term string | ||
| 48 | want []string | ||
| 49 | }{ | ||
| 50 | {"quick", []string{first}}, | ||
| 51 | {"Quick", []string{first}}, | ||
| 52 | {"#42", []string{first}}, | ||
| 53 | {"42", []string{first}}, | ||
| 54 | {"01M5JR2JK29KKMAA1T9MDNTR", []string{second}}, | ||
| 55 | {first, []string{first}}, | ||
| 56 | {"nothing", nil}, | ||
| 57 | {"", nil}, | ||
| 58 | } { | ||
| 59 | got, err := s.SearchIssues("r1", test.term, 8) | ||
| 60 | if err != nil { | ||
| 61 | t.Fatalf("SearchIssues(%q): %v", test.term, err) | ||
| 62 | } | ||
| 63 | ids := make([]string, len(got)) | ||
| 64 | for i, issue := range got { | ||
| 65 | ids[i] = issue.ID | ||
| 66 | } | ||
| 67 | if !slices.Equal(ids, test.want) { | ||
| 68 | t.Fatalf("SearchIssues(%q) = %v, want %v", test.term, ids, test.want) | ||
| 69 | } | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 13 | 73 | func TestSchemaVersionDropsRowsFromAnOlderShape(t *testing.T) { | |
| 14 | 74 | t.Parallel() | |
| 15 | 75 | path := filepath.Join(t.TempDir(), "index.db") | |