package contratest import ( "net/http" "net/http/httptest" "regexp" "strings" "testing" contra "github.com/carlosframework/contra/internal/contra" ) // hashedStylesheet is the fingerprinted URL shape the index page // links: /static/tokens.<16 hex>.css. var hashedStylesheet = regexp.MustCompile(`/static/tokens\.[0-9a-f]{16}\.css`) func TestIndexRenders(t *testing.T) { rec := get(t, newApp(t), "/") if rec.Code != http.StatusOK { t.Fatalf("GET /: status %d, want 200", rec.Code) } if !strings.Contains(rec.Body.String(), "the server knows nothing about you") { t.Errorf("GET /: page is missing its heading:\n%s", rec.Body.String()) } } func TestIndexLinksFingerprintedStylesheet(t *testing.T) { h := newApp(t) href := hashedStylesheet.FindString(get(t, h, "/").Body.String()) if href == "" { t.Fatal("index page links no fingerprinted stylesheet") } rec := get(t, h, href) if rec.Code != http.StatusOK { t.Fatalf("GET %s: status %d, want 200", href, rec.Code) } if got := rec.Header().Get("Cache-Control"); got != "public, max-age=31536000, immutable" { t.Errorf("GET %s: Cache-Control = %q, want immutable year", href, got) } } // The bare name keeps working, just never long-cached — so a changed // file always shows on an ordinary reload. func TestBareAssetNameStaysFresh(t *testing.T) { rec := get(t, newApp(t), "/static/tokens.css") if rec.Code != http.StatusOK { t.Fatalf("GET /static/tokens.css: status %d, want 200", rec.Code) } if got := rec.Header().Get("Cache-Control"); got != "no-cache" { t.Errorf("Cache-Control = %q, want no-cache", got) } } // ErrorPage is what main.go points Options.ErrorPage at (and the seam // to wire into Ctx.ErrorPage as handlers grow); called directly, it // pins that a panic's page is the framework's error-page partial, not // a stray net/http default. func TestErrorPageRendersFrameworkPartial(t *testing.T) { rec := httptest.NewRecorder() contra.ErrorPage(rec, httptest.NewRequest(http.MethodGet, "/", nil), http.StatusInternalServerError, "abc123") if rec.Code != http.StatusInternalServerError { t.Fatalf("ErrorPage: status %d, want %d", rec.Code, http.StatusInternalServerError) } body := rec.Body.String() if !strings.Contains(body, "Something went wrong on our side") { t.Errorf("ErrorPage body missing the 500 title:\n%s", body) } if !strings.Contains(body, "abc123") { t.Errorf("ErrorPage body missing the ref:\n%s", body) } } // The home page shipped for a while saying "there is no signup, no vault and // no stored data behind it" on a build that had all three. It was true when // it was written and nobody re-read it as the app grew. // // So: the claims on the front page are pinned against the routes that exist. // A page that says a thing does not exist, on a build where it does, is worse // than no page — this is a product whose entire pitch is that its statements // are literally true. func TestHomePageDoesNotContradictTheApp(t *testing.T) { h := newApp(t) body := get(t, h, "/").Body.String() // If /signup answers, the home page must not claim there is no signup. if get(t, h, "/signup").Code == http.StatusOK { for _, lie := range []string{ "no signup", "no vault and no stored data", "not open yet", } { if strings.Contains(strings.ToLower(body), lie) { t.Errorf("the home page still says %q, but /signup works", lie) } } } } // Every page must offer a way onward. An empty navigation rail is how the // canary shipped, and a visitor who cannot reach signup from the front page // has been handed a dead end. func TestEveryPageOffersAWayOnward(t *testing.T) { h := newApp(t) for _, path := range []string{"/", "/signup", "/signin"} { body := get(t, h, path).Body.String() if !strings.Contains(body, `href="/signup"`) && !strings.Contains(body, `href="/vault"`) { t.Errorf("%s offers no way onward: no link to signup or the vault", path) } if !strings.Contains(body, `href="/"`) { t.Errorf("%s has no link home", path) } } }