xtablo-source/go-backend/router_test.go
2026-05-09 20:18:24 +02:00

752 lines
22 KiB
Go

package main
import (
"context"
"net/http"
"net/http/httptest"
"net/url"
"os"
"strings"
"testing"
"xtablo-backend/internal/web/handlers"
)
func TestRootRedirectsToLoginWhenUnauthenticated(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/", nil)
rec := httptest.NewRecorder()
router := newTestRouter()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusSeeOther {
t.Fatalf("expected status 303, got %d", rec.Code)
}
if location := rec.Header().Get("Location"); location != "/login" {
t.Fatalf("expected redirect to /login, got %q", location)
}
}
func TestUnknownRouteRedirectsToLoginWhenUnauthenticated(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/missing", nil)
rec := httptest.NewRecorder()
router := newTestRouter()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusSeeOther {
t.Fatalf("expected status 303, got %d", rec.Code)
}
if location := rec.Header().Get("Location"); location != "/login" {
t.Fatalf("expected redirect to /login, got %q", location)
}
}
func TestLoginPageRenders(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/login", nil)
rec := httptest.NewRecorder()
router := newTestRouter()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected status 200, got %d", rec.Code)
}
body := rec.Body.String()
for _, want := range []string{
"Se connecter à Xtablo",
`hx-post="/login"`,
"https://cdn.jsdelivr.net/npm/htmx.org@4.0.0-beta2/dist/htmx.min.js",
`href="/static/tailwind.css"`,
`href="/pwa-icons/favicon-32x32.png"`,
`href="/pwa-icons/favicon-16x16.png"`,
`href="/pwa-icons/apple-touch-icon-180x180.png"`,
`href="/manifest.webmanifest"`,
`src="/logo_dark.png"`,
`src="/logo_white.png"`,
`data-testid="auth-card-shell"`,
"Découvrez la nouvelle expérience de connexion",
"Mot de passe oublié ?",
} {
if !strings.Contains(body, want) {
t.Fatalf("expected body to contain %q", want)
}
}
}
func TestTailwindStylesheetIsServed(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/static/tailwind.css", nil)
rec := httptest.NewRecorder()
router := newTestRouter()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected status 200, got %d", rec.Code)
}
body := rec.Body.String()
for _, want := range []string{
".text-2xl",
".grid-cols-1",
".whitespace-nowrap",
".justify-end",
} {
if !strings.Contains(body, want) {
t.Fatalf("expected tailwind.css to contain %q", want)
}
}
}
func TestSignupPageRenders(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/signup", nil)
rec := httptest.NewRecorder()
router := newTestRouter()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected status 200, got %d", rec.Code)
}
body := rec.Body.String()
for _, want := range []string{
"S'inscrire à Xtablo",
`hx-post="/signup"`,
"Vous avez déjà un compte ?",
} {
if !strings.Contains(body, want) {
t.Fatalf("expected body to contain %q", want)
}
}
}
func TestBrandingAssetsAreServed(t *testing.T) {
testCases := []string{
"/logo_dark.png",
"/logo_white.png",
"/pwa-icons/favicon-32x32.png",
"/pwa-icons/favicon-16x16.png",
"/pwa-icons/apple-touch-icon-180x180.png",
"/manifest.webmanifest",
}
router := newTestRouter()
for _, path := range testCases {
req := httptest.NewRequest(http.MethodGet, path, nil)
rec := httptest.NewRecorder()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected %s to return 200, got %d", path, rec.Code)
}
}
}
func TestLoginReturnsValidationError(t *testing.T) {
form := url.Values{}
form.Set("email", "")
form.Set("password", "")
req := httptest.NewRequest(http.MethodPost, "/login", strings.NewReader(form.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
rec := httptest.NewRecorder()
router := newTestRouter()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusUnprocessableEntity {
t.Fatalf("expected status 422, got %d", rec.Code)
}
if !strings.Contains(rec.Body.String(), "Veuillez renseigner votre email et votre mot de passe") {
t.Fatalf("expected validation error fragment, got %q", rec.Body.String())
}
}
func TestLoginCreatesSessionAndRedirects(t *testing.T) {
form := url.Values{}
form.Set("email", "demo@xtablo.com")
form.Set("password", "xtablo-demo")
req := httptest.NewRequest(http.MethodPost, "/login", strings.NewReader(form.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
rec := httptest.NewRecorder()
router := newTestRouter()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected status 200, got %d", rec.Code)
}
if redirect := rec.Header().Get("HX-Redirect"); redirect != "/" {
t.Fatalf("expected HX-Redirect to /, got %q", redirect)
}
sessionCookie := findCookie(rec.Result().Cookies(), "xtablo_session")
if sessionCookie == nil {
t.Fatalf("expected session cookie to be set")
}
homeReq := httptest.NewRequest(http.MethodGet, "/", nil)
homeReq.AddCookie(sessionCookie)
homeRec := httptest.NewRecorder()
router.ServeHTTP(homeRec, homeReq)
if homeRec.Code != http.StatusOK {
t.Fatalf("expected authenticated root status 200, got %d", homeRec.Code)
}
for _, want := range []string{
"Bonjour,",
"Aperçu",
"https://cdn.jsdelivr.net/npm/htmx.org@4.0.0-beta2/dist/htmx.min.js",
`hx-get="/tasks"`,
`hx-target="#app-main-content"`,
`hx-swap="outerHTML"`,
`hx-push-url="true"`,
"Tâches",
"Projets",
"Planning",
"Discussions",
"Fichiers",
"Feedback",
"Arctic Matrix",
"Créer un projet",
"Créer une tâche",
"Inviter l'équipe",
"Envoyer un message",
"Mes Projets",
"Mes Tâches",
`action="/logout"`,
} {
if !strings.Contains(homeRec.Body.String(), want) {
t.Fatalf("expected authenticated home page to contain %q, got %q", want, homeRec.Body.String())
}
}
}
func TestTasksPageRendersFullDashboardPage(t *testing.T) {
form := url.Values{}
form.Set("email", "demo@xtablo.com")
form.Set("password", "xtablo-demo")
loginReq := httptest.NewRequest(http.MethodPost, "/login", strings.NewReader(form.Encode()))
loginReq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
loginRec := httptest.NewRecorder()
router := newTestRouter()
router.ServeHTTP(loginRec, loginReq)
sessionCookie := findCookie(loginRec.Result().Cookies(), "xtablo_session")
if sessionCookie == nil {
t.Fatalf("expected session cookie to be set")
}
req := httptest.NewRequest(http.MethodGet, "/tasks", nil)
req.AddCookie(sessionCookie)
rec := httptest.NewRecorder()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected status 200, got %d", rec.Code)
}
body := rec.Body.String()
for _, want := range []string{
`class="sidebar-nav-shell"`,
`id="app-main-content"`,
"Tâches",
"Suivez les tâches de votre équipe",
} {
if !strings.Contains(body, want) {
t.Fatalf("expected tasks page to contain %q", want)
}
}
}
func TestHomePageProjectsUseSharedTabloGridCardWithDeleteAction(t *testing.T) {
repo := handlers.NewInMemoryAuthRepository()
authUser, err := repo.GetAuthUserByEmail(context.Background(), "demo@xtablo.com")
if err != nil {
t.Fatalf("expected demo user, got error: %v", err)
}
if _, err := repo.CreateTablo(context.Background(), handlers.CreateTabloInput{
OwnerID: authUser.ID,
Name: "Hello",
Status: handlers.TabloStatusInProgress,
}); err != nil {
t.Fatalf("expected tablo creation to succeed, got error: %v", err)
}
form := url.Values{}
form.Set("email", "demo@xtablo.com")
form.Set("password", "xtablo-demo")
loginReq := httptest.NewRequest(http.MethodPost, "/login", strings.NewReader(form.Encode()))
loginReq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
loginRec := httptest.NewRecorder()
router := newRouterWithHandler(handlers.NewAuthHandler(repo))
router.ServeHTTP(loginRec, loginReq)
sessionCookie := findCookie(loginRec.Result().Cookies(), "xtablo_session")
if sessionCookie == nil {
t.Fatalf("expected session cookie to be set")
}
req := httptest.NewRequest(http.MethodGet, "/", nil)
req.AddCookie(sessionCookie)
rec := httptest.NewRecorder()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected status 200, got %d", rec.Code)
}
body := rec.Body.String()
for _, want := range []string{
`class="project-card"`,
`class="project-date-row"`,
`hx-delete="/tablos/`,
} {
if !strings.Contains(body, want) {
t.Fatalf("expected home page to contain %q", want)
}
}
}
func TestHomePageProjectsCollapseAfterSixByDefault(t *testing.T) {
repo := handlers.NewInMemoryAuthRepository()
authUser, err := repo.GetAuthUserByEmail(context.Background(), "demo@xtablo.com")
if err != nil {
t.Fatalf("expected demo user, got error: %v", err)
}
for i := 0; i < 8; i++ {
if _, err := repo.CreateTablo(context.Background(), handlers.CreateTabloInput{
OwnerID: authUser.ID,
Name: "Project " + string(rune('A'+i)),
Status: handlers.TabloStatusTodo,
}); err != nil {
t.Fatalf("expected tablo creation to succeed, got error: %v", err)
}
}
form := url.Values{}
form.Set("email", "demo@xtablo.com")
form.Set("password", "xtablo-demo")
loginReq := httptest.NewRequest(http.MethodPost, "/login", strings.NewReader(form.Encode()))
loginReq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
loginRec := httptest.NewRecorder()
router := newRouterWithHandler(handlers.NewAuthHandler(repo))
router.ServeHTTP(loginRec, loginReq)
sessionCookie := findCookie(loginRec.Result().Cookies(), "xtablo_session")
if sessionCookie == nil {
t.Fatalf("expected session cookie to be set")
}
req := httptest.NewRequest(http.MethodGet, "/", nil)
req.AddCookie(sessionCookie)
rec := httptest.NewRecorder()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected status 200, got %d", rec.Code)
}
body := rec.Body.String()
if count := strings.Count(body, `class="project-card"`); count != 6 {
t.Fatalf("expected 6 visible project cards by default, got %d", count)
}
for _, want := range []string{
`id="overview-projects-section"`,
`Voir 2 de plus`,
`hx-get="/?show_projects=all"`,
`hx-target="#overview-projects-section"`,
} {
if !strings.Contains(body, want) {
t.Fatalf("expected home page to contain %q", want)
}
}
}
func TestHomePageProjectsExpandViaHTMXSectionSwap(t *testing.T) {
repo := handlers.NewInMemoryAuthRepository()
authUser, err := repo.GetAuthUserByEmail(context.Background(), "demo@xtablo.com")
if err != nil {
t.Fatalf("expected demo user, got error: %v", err)
}
for i := 0; i < 8; i++ {
if _, err := repo.CreateTablo(context.Background(), handlers.CreateTabloInput{
OwnerID: authUser.ID,
Name: "Project " + string(rune('A'+i)),
Status: handlers.TabloStatusTodo,
}); err != nil {
t.Fatalf("expected tablo creation to succeed, got error: %v", err)
}
}
form := url.Values{}
form.Set("email", "demo@xtablo.com")
form.Set("password", "xtablo-demo")
loginReq := httptest.NewRequest(http.MethodPost, "/login", strings.NewReader(form.Encode()))
loginReq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
loginRec := httptest.NewRecorder()
router := newRouterWithHandler(handlers.NewAuthHandler(repo))
router.ServeHTTP(loginRec, loginReq)
sessionCookie := findCookie(loginRec.Result().Cookies(), "xtablo_session")
if sessionCookie == nil {
t.Fatalf("expected session cookie to be set")
}
req := httptest.NewRequest(http.MethodGet, "/?show_projects=all", nil)
req.Header.Set("HX-Request", "true")
req.Header.Set("HX-Target", "section#overview-projects-section")
req.AddCookie(sessionCookie)
rec := httptest.NewRecorder()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected status 200, got %d", rec.Code)
}
body := rec.Body.String()
if count := strings.Count(body, `class="project-card"`); count != 8 {
t.Fatalf("expected 8 visible project cards after expansion, got %d", count)
}
if !strings.Contains(body, `id="overview-projects-section"`) {
t.Fatalf("expected section swap root in response, got %q", body)
}
if strings.Contains(body, `id="app-main-content"`) {
t.Fatalf("expected projects section response, got main content swap %q", body)
}
if strings.Contains(body, `Voir 2 de plus`) {
t.Fatalf("expected see more button to disappear after expansion, got %q", body)
}
if strings.Contains(body, `class="sidebar-nav-shell"`) {
t.Fatalf("expected projects section swap to avoid rerendering the full dashboard shell")
}
}
func TestTasksPageReturnsHTMXMainContentSwap(t *testing.T) {
form := url.Values{}
form.Set("email", "demo@xtablo.com")
form.Set("password", "xtablo-demo")
loginReq := httptest.NewRequest(http.MethodPost, "/login", strings.NewReader(form.Encode()))
loginReq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
loginRec := httptest.NewRecorder()
router := newTestRouter()
router.ServeHTTP(loginRec, loginReq)
sessionCookie := findCookie(loginRec.Result().Cookies(), "xtablo_session")
if sessionCookie == nil {
t.Fatalf("expected session cookie to be set")
}
req := httptest.NewRequest(http.MethodGet, "/tasks", nil)
req.Header.Set("HX-Request", "true")
req.AddCookie(sessionCookie)
rec := httptest.NewRecorder()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected status 200, got %d", rec.Code)
}
body := rec.Body.String()
for _, want := range []string{
`id="app-main-content"`,
`hx-swap-oob="outerHTML"`,
`id="sidebar-nav-tasks"`,
"Tâches",
"Suivez les tâches de votre équipe",
} {
if !strings.Contains(body, want) {
t.Fatalf("expected HTMX tasks response to contain %q", want)
}
}
if strings.Contains(body, `class="sidebar-nav-shell"`) {
t.Fatalf("expected HTMX tasks response to avoid rerendering the full sidebar")
}
}
func TestTablosPageRendersFullDashboardPage(t *testing.T) {
form := url.Values{}
form.Set("email", "demo@xtablo.com")
form.Set("password", "xtablo-demo")
loginReq := httptest.NewRequest(http.MethodPost, "/login", strings.NewReader(form.Encode()))
loginReq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
loginRec := httptest.NewRecorder()
router := newTestRouter()
router.ServeHTTP(loginRec, loginReq)
sessionCookie := findCookie(loginRec.Result().Cookies(), "xtablo_session")
if sessionCookie == nil {
t.Fatalf("expected session cookie to be set")
}
req := httptest.NewRequest(http.MethodGet, "/tablos", nil)
req.AddCookie(sessionCookie)
rec := httptest.NewRecorder()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected status 200, got %d", rec.Code)
}
body := rec.Body.String()
for _, want := range []string{
`class="sidebar-nav-shell"`,
`id="app-main-content" class="flex-1 overflow-auto"`,
"Mes Projets",
"Nouveau projet",
"Vue en grille",
"Rechercher...",
} {
if !strings.Contains(body, want) {
t.Fatalf("expected tablos page to contain %q", want)
}
}
}
func TestTablosPageReturnsHTMXMainContentSwap(t *testing.T) {
form := url.Values{}
form.Set("email", "demo@xtablo.com")
form.Set("password", "xtablo-demo")
loginReq := httptest.NewRequest(http.MethodPost, "/login", strings.NewReader(form.Encode()))
loginReq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
loginRec := httptest.NewRecorder()
router := newTestRouter()
router.ServeHTTP(loginRec, loginReq)
sessionCookie := findCookie(loginRec.Result().Cookies(), "xtablo_session")
if sessionCookie == nil {
t.Fatalf("expected session cookie to be set")
}
req := httptest.NewRequest(http.MethodGet, "/tablos?view=list&status=all", nil)
req.Header.Set("HX-Request", "true")
req.AddCookie(sessionCookie)
rec := httptest.NewRecorder()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected status 200, got %d", rec.Code)
}
body := rec.Body.String()
for _, want := range []string{
`id="app-main-content"`,
`hx-swap-oob="outerHTML"`,
`id="sidebar-nav-tablos"`,
"Mes Projets",
"Vue en liste",
} {
if !strings.Contains(body, want) {
t.Fatalf("expected HTMX tablos response to contain %q", want)
}
}
if strings.Contains(body, `class="sidebar-nav-shell"`) {
t.Fatalf("expected HTMX tablos response to avoid rerendering the full sidebar")
}
}
func TestTablosPageUtilityStylesExist(t *testing.T) {
content, err := os.ReadFile("static/tailwind.css")
if err != nil {
t.Fatalf("read tailwind.css: %v", err)
}
css := string(content)
for _, want := range []string{
".flex-1",
".overflow-auto",
".text-2xl",
".bg-purple-600",
".grid-cols-1",
".rounded-xl",
".md\\:flex-row",
".sm\\:grid-cols-2",
".lg\\:grid-cols-3",
".xl\\:grid-cols-4",
} {
if !strings.Contains(css, want) {
t.Fatalf("expected tailwind.css to contain utility %q", want)
}
}
}
func TestSignupCreatesUserSessionAndRedirects(t *testing.T) {
form := url.Values{}
form.Set("email", "new@xtablo.com")
form.Set("password", "xtablo-secret")
req := httptest.NewRequest(http.MethodPost, "/signup", strings.NewReader(form.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
rec := httptest.NewRecorder()
router := newTestRouter()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected status 200, got %d", rec.Code)
}
if redirect := rec.Header().Get("HX-Redirect"); redirect != "/" {
t.Fatalf("expected HX-Redirect to /, got %q", redirect)
}
sessionCookie := findCookie(rec.Result().Cookies(), "xtablo_session")
if sessionCookie == nil {
t.Fatalf("expected session cookie to be set")
}
loginReq := httptest.NewRequest(http.MethodPost, "/login", strings.NewReader(form.Encode()))
loginReq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
loginRec := httptest.NewRecorder()
router.ServeHTTP(loginRec, loginReq)
if loginRec.Header().Get("HX-Redirect") != "/" {
t.Fatalf("expected signed up user to be able to log in")
}
}
func TestLogoutClearsSessionAndRedirectsToLogin(t *testing.T) {
form := url.Values{}
form.Set("email", "demo@xtablo.com")
form.Set("password", "xtablo-demo")
loginReq := httptest.NewRequest(http.MethodPost, "/login", strings.NewReader(form.Encode()))
loginReq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
loginRec := httptest.NewRecorder()
router := newTestRouter()
router.ServeHTTP(loginRec, loginReq)
sessionCookie := findCookie(loginRec.Result().Cookies(), "xtablo_session")
if sessionCookie == nil {
t.Fatalf("expected session cookie to be set")
}
logoutReq := httptest.NewRequest(http.MethodPost, "/logout", nil)
logoutReq.AddCookie(sessionCookie)
logoutRec := httptest.NewRecorder()
router.ServeHTTP(logoutRec, logoutReq)
if logoutRec.Code != http.StatusSeeOther {
t.Fatalf("expected logout status 303, got %d", logoutRec.Code)
}
if location := logoutRec.Header().Get("Location"); location != "/login" {
t.Fatalf("expected logout redirect to /login, got %q", location)
}
clearedCookie := findCookie(logoutRec.Result().Cookies(), "xtablo_session")
if clearedCookie == nil {
t.Fatalf("expected cleared session cookie")
}
if clearedCookie.MaxAge >= 0 && clearedCookie.Value != "" {
t.Fatalf("expected cleared session cookie to be expired, got %+v", clearedCookie)
}
homeReq := httptest.NewRequest(http.MethodGet, "/", nil)
homeReq.AddCookie(sessionCookie)
homeRec := httptest.NewRecorder()
router.ServeHTTP(homeRec, homeReq)
if homeRec.Code != http.StatusSeeOther {
t.Fatalf("expected logged-out root access to redirect, got %d", homeRec.Code)
}
if location := homeRec.Header().Get("Location"); location != "/login" {
t.Fatalf("expected logged-out root redirect to /login, got %q", location)
}
}
func TestUnknownRouteShowsDashboard404WhenAuthenticated(t *testing.T) {
form := url.Values{}
form.Set("email", "demo@xtablo.com")
form.Set("password", "xtablo-demo")
loginReq := httptest.NewRequest(http.MethodPost, "/login", strings.NewReader(form.Encode()))
loginReq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
loginRec := httptest.NewRecorder()
router := newTestRouter()
router.ServeHTTP(loginRec, loginReq)
sessionCookie := findCookie(loginRec.Result().Cookies(), "xtablo_session")
if sessionCookie == nil {
t.Fatalf("expected session cookie to be set")
}
req := httptest.NewRequest(http.MethodGet, "/missing", nil)
req.AddCookie(sessionCookie)
rec := httptest.NewRecorder()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusNotFound {
t.Fatalf("expected status 404, got %d", rec.Code)
}
body := rec.Body.String()
for _, want := range []string{
"Aperçu",
"Page introuvable",
"Cette page n'existe pas",
`href="/"`,
`action="/logout"`,
} {
if !strings.Contains(body, want) {
t.Fatalf("expected authenticated 404 page to contain %q", want)
}
}
}
func TestSessionSurvivesHandlerRecreation(t *testing.T) {
repo := handlers.NewInMemoryAuthRepository()
loginRouter := newRouterWithHandler(handlers.NewAuthHandler(repo))
form := url.Values{}
form.Set("email", "demo@xtablo.com")
form.Set("password", "xtablo-demo")
loginReq := httptest.NewRequest(http.MethodPost, "/login", strings.NewReader(form.Encode()))
loginReq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
loginRec := httptest.NewRecorder()
loginRouter.ServeHTTP(loginRec, loginReq)
sessionCookie := findCookie(loginRec.Result().Cookies(), "xtablo_session")
if sessionCookie == nil {
t.Fatalf("expected session cookie to be set")
}
reloadedRouter := newRouterWithHandler(handlers.NewAuthHandler(repo))
homeReq := httptest.NewRequest(http.MethodGet, "/", nil)
homeReq.AddCookie(sessionCookie)
homeRec := httptest.NewRecorder()
reloadedRouter.ServeHTTP(homeRec, homeReq)
if homeRec.Code != http.StatusOK {
t.Fatalf("expected session to survive handler recreation, got status %d", homeRec.Code)
}
}
func findCookie(cookies []*http.Cookie, name string) *http.Cookie {
for _, cookie := range cookies {
if cookie.Name == name {
return cookie
}
}
return nil
}