package e2e import ( "net/url" "os" "strings" "testing" "github.com/PuerkitoBio/goquery" ) // A repo whose git directory is gone keeps its database row. The admin // settings page lists it and lets the admin drop it. func TestMissingRepos(t *testing.T) { e := newEnv(t) admin := e.admin() for _, name := range []string{"gone-a", "gone-b", "stays"} { e.createRepo(admin, name) admin.post("/"+name+"/issues", url.Values{"title": {"An issue"}, "body": {"x"}}).mustStatus(302) } for _, name := range []string{"gone-a", "gone-b"} { if err := os.RemoveAll(e.repoPath(name)); err != nil { t.Fatal(err) } } // dropID returns the hidden id of the drop form for a listed repo. dropID := func(name string) string { var id string admin.get("/settings").Find(".queue-item").Each(func(_ int, s *goquery.Selection) { if s.Find("strong").Text() == name { id, _ = s.Find(`input[name=id]`).Attr("value") } }) return id } t.Run("settings lists missing repos with counts", func(t *testing.T) { r := admin.get("/settings") names := r.Texts(".queue-item strong") if !contains(names, "gone-a") || !contains(names, "gone-b") || contains(names, "stays") { t.Errorf("missing list = %v", names) } if !r.Contains("1 issue, 0 patches") { t.Error("issue count not shown") } }) t.Run("opening a missing repo errors", func(t *testing.T) { if c := admin.get("/gone-a").Code; c < 400 { t.Errorf("status = %d", c) } }) t.Run("drop refuses an unknown or present repo", func(t *testing.T) { r := admin.post("/admin/repos/drop", url.Values{"id": {"999999"}}) if !strings.Contains(r.Location(), "error=") { t.Errorf("location = %q", r.Location()) } }) t.Run("drop one removes only that repo", func(t *testing.T) { id := dropID("gone-a") if id == "" { t.Fatal("no drop form for gone-a") } admin.post("/admin/repos/drop", url.Values{"id": {id}}).mustRedirect("/settings?success=repo_dropped") names := admin.get("/settings").Texts(".queue-item strong") if contains(names, "gone-a") || !contains(names, "gone-b") { t.Errorf("after drop: %v", names) } admin.get("/gone-a").mustStatus(404) admin.get("/stays/issues/1").mustStatus(200) }) t.Run("drop all clears the section", func(t *testing.T) { admin.post("/admin/repos/drop-all", nil).mustRedirect("/settings?success=repos_dropped") r := admin.get("/settings") if r.Count(".queue-item input[name=id]") != 0 || !r.Contains("Every repository has its git directory") { t.Error("section not empty after drop all") } admin.get("/gone-b").mustStatus(404) admin.get("/stays/issues/1").mustStatus(200) }) t.Run("restart keeps a missing repo's data", func(t *testing.T) { e.createRepo(admin, "gone-c") admin.post("/gone-c/issues", url.Values{"title": {"Kept"}, "body": {"x"}}).mustStatus(302) if err := os.RemoveAll(e.repoPath("gone-c")); err != nil { t.Fatal(err) } if err := e.Srv.SyncRepos(t.Context()); err != nil { t.Fatal(err) } if !contains(admin.get("/settings").Texts(".queue-item strong"), "gone-c") { t.Error("gone-c dropped by startup sync") } }) t.Run("non-admin cannot drop", func(t *testing.T) { e.register("bob", "password123").post("/admin/repos/drop-all", nil).mustStatus(403) }) }