arche / internal/archesrv/issues_test.go

commit 154431fd
  1package archesrv
  2
  3import (
  4	"io"
  5	"net/http"
  6	"strings"
  7	"testing"
  8)
  9
 10func TestForgeServer_Issues_CreateAndList(t *testing.T) {
 11	s, ts := newTestServer(t)
 12	_, client := loginAsAdmin(t, s, ts)
 13	setupRepoWithDisk(t, s, "myrepo", "private")
 14
 15	resp, err := client.PostForm(ts.URL+"/myrepo/issues", map[string][]string{
 16		"title": {"First issue"},
 17		"body":  {"some body text"},
 18	})
 19	if err != nil {
 20		t.Fatalf("POST /myrepo/issues: %v", err)
 21	}
 22	resp.Body.Close()
 23	if resp.StatusCode >= 400 {
 24		t.Fatalf("create issue: got %d", resp.StatusCode)
 25	}
 26
 27	resp2, err := client.Get(ts.URL + "/myrepo/issues")
 28	if err != nil {
 29		t.Fatalf("GET /myrepo/issues: %v", err)
 30	}
 31	defer resp2.Body.Close()
 32	if resp2.StatusCode != http.StatusOK {
 33		t.Errorf("GET /myrepo/issues: want 200, got %d", resp2.StatusCode)
 34	}
 35	body, _ := io.ReadAll(resp2.Body)
 36	if !strings.Contains(string(body), "First issue") {
 37		t.Error("issue list page should contain the issue title")
 38	}
 39}
 40
 41func TestForgeServer_Issues_UnauthenticatedCannotCreate(t *testing.T) {
 42	s, ts := newTestServer(t)
 43	setupRepoWithDisk(t, s, "myrepo", "public")
 44
 45	resp, err := http.PostForm(ts.URL+"/myrepo/issues", map[string][]string{
 46		"title": {"sneaky issue"},
 47	})
 48	if err != nil {
 49		t.Fatalf("POST /myrepo/issues: %v", err)
 50	}
 51	resp.Body.Close()
 52	if resp.StatusCode != http.StatusUnauthorized {
 53		t.Errorf("expected 401, got %d", resp.StatusCode)
 54	}
 55}
 56
 57func TestForgeServer_Issues_MissingTitleIsError(t *testing.T) {
 58	s, ts := newTestServer(t)
 59	_, client := loginAsAdmin(t, s, ts)
 60	setupRepoWithDisk(t, s, "myrepo", "private")
 61
 62	resp, err := client.PostForm(ts.URL+"/myrepo/issues", map[string][]string{
 63		"title": {""},
 64	})
 65	if err != nil {
 66		t.Fatalf("POST: %v", err)
 67	}
 68	resp.Body.Close()
 69	if resp.StatusCode != http.StatusBadRequest {
 70		t.Errorf("empty title should return 400, got %d", resp.StatusCode)
 71	}
 72}
 73
 74func TestForgeServer_Issues_AddComment(t *testing.T) {
 75	s, ts := newTestServer(t)
 76	_, client := loginAsAdmin(t, s, ts)
 77	setupRepoWithDisk(t, s, "myrepo", "private")
 78
 79	resp, _ := client.PostForm(ts.URL+"/myrepo/issues", map[string][]string{
 80		"title": {"Bug report"},
 81		"body":  {"details"},
 82	})
 83	resp.Body.Close()
 84
 85	issueID := extractIssueIDFromList(t, client, ts.URL+"/myrepo/issues")
 86
 87	resp2, err := client.PostForm(ts.URL+"/myrepo/issue/comment", map[string][]string{
 88		"issue_id": {issueID},
 89		"text":     {"looks good"},
 90	})
 91	if err != nil {
 92		t.Fatalf("POST comment: %v", err)
 93	}
 94	resp2.Body.Close()
 95	if resp2.StatusCode >= 400 {
 96		t.Errorf("add comment: got %d", resp2.StatusCode)
 97	}
 98}
 99
100func TestForgeServer_Issues_SetStatus(t *testing.T) {
101	s, ts := newTestServer(t)
102	_, client := loginAsAdmin(t, s, ts)
103	setupRepoWithDisk(t, s, "myrepo", "private")
104
105	client.PostForm(ts.URL+"/myrepo/issues", map[string][]string{"title": {"Close me"}}) //nolint:errcheck
106
107	issueID := extractIssueIDFromList(t, client, ts.URL+"/myrepo/issues")
108
109	resp, err := client.PostForm(ts.URL+"/myrepo/issue/status", map[string][]string{
110		"issue_id": {issueID},
111		"status":   {"closed"},
112	})
113	if err != nil {
114		t.Fatalf("POST status: %v", err)
115	}
116	resp.Body.Close()
117	if resp.StatusCode >= 400 {
118		t.Errorf("set status: got %d", resp.StatusCode)
119	}
120}
121
122func TestForgeServer_Issues_ViewPage(t *testing.T) {
123	s, ts := newTestServer(t)
124	_, client := loginAsAdmin(t, s, ts)
125	setupRepoWithDisk(t, s, "myrepo", "private")
126
127	resp, err := client.PostForm(ts.URL+"/myrepo/issues", map[string][]string{
128		"title": {"View me"},
129		"body":  {"body of the issue"},
130	})
131	if err != nil {
132		t.Fatalf("POST issue: %v", err)
133	}
134	resp.Body.Close()
135
136	loc := resp.Header.Get("Location")
137	if loc == "" {
138		t.Skip("no Location header on issue create; skipping view test")
139	}
140
141	resp2, err := client.Get(ts.URL + loc)
142	if err != nil {
143		t.Fatalf("GET issue page: %v", err)
144	}
145	defer resp2.Body.Close()
146	if resp2.StatusCode != http.StatusOK {
147		t.Errorf("issue view: want 200, got %d", resp2.StatusCode)
148	}
149	body, _ := io.ReadAll(resp2.Body)
150	if !strings.Contains(string(body), "View me") {
151		t.Error("issue page should show the issue title")
152	}
153	if !strings.Contains(string(body), "body of the issue") {
154		t.Error("issue page should show the issue body")
155	}
156}
157
158func TestForgeServer_Issues_ViewMissingIssue404(t *testing.T) {
159	s, ts := newTestServer(t)
160	_, client := loginAsAdmin(t, s, ts)
161	setupRepoWithDisk(t, s, "myrepo", "private")
162
163	resp, err := client.Get(ts.URL + "/myrepo/issue?id=nonexistentissue")
164	if err != nil {
165		t.Fatalf("GET: %v", err)
166	}
167	resp.Body.Close()
168	if resp.StatusCode != http.StatusNotFound {
169		t.Errorf("missing issue: want 404, got %d", resp.StatusCode)
170	}
171}
172
173func TestForgeServer_Issues_ViewOnMissingRepo404(t *testing.T) {
174	s, ts := newTestServer(t)
175	s.db.CreateUser("admin", "adminpass", true) //nolint:errcheck
176	client := loginAs(t, ts, "admin", "adminpass")
177
178	resp, err := client.Get(ts.URL + "/ghost/issues")
179	if err != nil {
180		t.Fatalf("GET: %v", err)
181	}
182	resp.Body.Close()
183	if resp.StatusCode != http.StatusNotFound {
184		t.Errorf("missing repo: want 404, got %d", resp.StatusCode)
185	}
186}
187
188func extractIssueIDFromList(t *testing.T, client *http.Client, listURL string) string {
189	t.Helper()
190	resp, err := client.Get(listURL)
191	if err != nil {
192		t.Fatalf("GET issue list: %v", err)
193	}
194	body, _ := io.ReadAll(resp.Body)
195	resp.Body.Close()
196	page := string(body)
197
198	idx := strings.Index(page, "?id=")
199	if idx == -1 {
200		t.Skip("could not find issue ID in listing; skipping")
201	}
202	end := strings.IndexAny(page[idx+4:], `"& `)
203	if end == -1 {
204		end = 64
205	}
206	return page[idx+4 : idx+4+end]
207}