package main import ( "bytes" "context" "net/http" "net/http/httptest" "os" "strings" "testing" "time" ) // Every cgit page forks a child, and browsing a public repository takes no // credential, so the budget is what stands between a visitor and one child per // connection. A request that cannot have a slot waits for one, but not on // behalf of somebody who has already hung up. func TestBrowsingIsBounded(t *testing.T) { f := testForge(t) addRepo(t, f, "notes", Repo{Visibility: visPublic, Issues: featDisabled, Wiki: featDisabled}) h := f.routes() for range cap(browsing) { browsing <- struct{}{} } defer func() { for range cap(browsing) { <-browsing } }() ctx, cancel := context.WithCancel(context.Background()) cancel() done := make(chan struct{}) go func() { h.ServeHTTP(httptest.NewRecorder(), httptest.NewRequest("GET", "/notes/", nil).WithContext(ctx)) close(done) }() select { case <-done: case <-time.After(5 * time.Second): t.Fatal("a browse held a goroutine waiting to fork for a client that had gone") } } // A tab that leads to a 404 is worse than no tab. One template draws the row // for both kinds of page, so both are asked here. func TestTabsFollowTheKnobs(t *testing.T) { f := testForge(t) addRepo(t, f, "docs", Repo{Visibility: visPublic, Issues: featDisabled, Wiki: featUsers}) addRepo(t, f, "code", Repo{Visibility: visPublic, Issues: featUsers, Wiki: featDisabled}) h := f.routes() for _, c := range []struct { path, page string issues, wiki bool }{ {"docs", "/docs/wiki", false, true}, {"code", "/code/issues", true, false}, } { repo, _ := f.store.Repo(c.path) r := httptest.NewRequest("GET", "/"+c.path+"/", nil) rows := []struct{ where, html string }{ {"injected into cgit's row", string(f.navPart(r, "tabs", c.path, "", repo))}, {"on our own page", get(h, c.page, nil).Body.String()}, } for _, row := range rows { for _, tab := range []struct { name string want bool }{{"issues", c.issues}, {"wiki", c.wiki}} { got := strings.Contains(row.html, ">"+tab.name+"") if got != tab.want { t.Errorf("%s %s: the %s tab is present %v, wanted %v", c.path, row.where, tab.name, got, tab.want) } } } } } // The bar is spliced into cgit's own markup by matching it, so a cgit whose // page has changed shape has to fail here rather than quietly serve a page // with no bar on it. func TestTheBarGoesInsideCgitsPage(t *testing.T) { f := testForge(t) addRepo(t, f, "notes", Repo{Visibility: visPublic, Issues: featUsers, Wiki: featUsers}) if _, err := os.Stat(cgitBinary()); err != nil { t.Skip("cgit is not installed, and this is a test of cgit's markup") } page := get(f.routes(), "/notes/", nil).Body.String() div := strings.Index(page, string(cgitDiv)) header := strings.Index(page, `