chore: add routes test
This commit is contained in:
parent
7c375e1002
commit
4181264a68
1 changed files with 233 additions and 0 deletions
233
internal/routes/routes_test.go
Normal file
233
internal/routes/routes_test.go
Normal file
|
|
@ -0,0 +1,233 @@
|
||||||
|
package routes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.juancwu.dev/juancwu/budgit/internal/app"
|
||||||
|
"git.juancwu.dev/juancwu/budgit/internal/model"
|
||||||
|
"git.juancwu.dev/juancwu/budgit/internal/repository"
|
||||||
|
"git.juancwu.dev/juancwu/budgit/internal/service"
|
||||||
|
"git.juancwu.dev/juancwu/budgit/internal/testutil"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newTestApp(dbi testutil.DBInfo) *app.App {
|
||||||
|
cfg := testutil.TestConfig()
|
||||||
|
cfg.DBDriver = dbi.Driver
|
||||||
|
|
||||||
|
userRepo := repository.NewUserRepository(dbi.DB)
|
||||||
|
tokenRepo := repository.NewTokenRepository(dbi.DB)
|
||||||
|
spaceRepo := repository.NewSpaceRepository(dbi.DB)
|
||||||
|
inviteRepo := repository.NewInvitationRepository(dbi.DB)
|
||||||
|
|
||||||
|
spaceSvc := service.NewSpaceService(spaceRepo)
|
||||||
|
emailSvc := service.NewEmailService(nil, "test@example.com", "http://localhost:9999", "Budgit Test", false)
|
||||||
|
authSvc := service.NewAuthService(emailSvc, userRepo, tokenRepo, spaceSvc, cfg.JWTSecret, cfg.JWTExpiry, cfg.TokenMagicLinkExpiry, false)
|
||||||
|
userSvc := service.NewUserService(userRepo)
|
||||||
|
inviteSvc := service.NewInviteService(inviteRepo, spaceRepo, userRepo, emailSvc)
|
||||||
|
|
||||||
|
return &app.App{
|
||||||
|
Cfg: cfg,
|
||||||
|
DB: dbi.DB,
|
||||||
|
UserService: userSvc,
|
||||||
|
AuthService: authSvc,
|
||||||
|
EmailService: emailSvc,
|
||||||
|
SpaceService: spaceSvc,
|
||||||
|
InviteService: inviteSvc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// authCookie generates a valid JWT cookie for the given user.
|
||||||
|
func authCookie(a *app.App, user *model.User) *http.Cookie {
|
||||||
|
token, err := a.AuthService.GenerateJWT(user)
|
||||||
|
if err != nil {
|
||||||
|
panic("failed to generate test JWT: " + err.Error())
|
||||||
|
}
|
||||||
|
return &http.Cookie{
|
||||||
|
Name: "auth_token",
|
||||||
|
Value: token,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetupRoutes_PublicRoutes(t *testing.T) {
|
||||||
|
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||||
|
a := newTestApp(dbi)
|
||||||
|
handler := SetupRoutes(a)
|
||||||
|
|
||||||
|
routes := []string{"/forbidden", "/privacy", "/terms"}
|
||||||
|
|
||||||
|
for _, path := range routes {
|
||||||
|
t.Run(path, func(t *testing.T) {
|
||||||
|
req := httptest.NewRequest(http.MethodGet, path, nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
handler.ServeHTTP(w, req)
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetupRoutes_HomeRedirects(t *testing.T) {
|
||||||
|
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||||
|
a := newTestApp(dbi)
|
||||||
|
handler := SetupRoutes(a)
|
||||||
|
|
||||||
|
// Unauthenticated → redirect to /auth
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
handler.ServeHTTP(w, req)
|
||||||
|
assert.Equal(t, http.StatusSeeOther, w.Code)
|
||||||
|
assert.Equal(t, "/auth", w.Header().Get("Location"))
|
||||||
|
|
||||||
|
// Authenticated → redirect to /app/spaces
|
||||||
|
name := "Test User"
|
||||||
|
user := testutil.CreateTestUserWithName(t, dbi.DB, "home@example.com", &name)
|
||||||
|
req = httptest.NewRequest(http.MethodGet, "/", nil)
|
||||||
|
req.AddCookie(authCookie(a, user))
|
||||||
|
w = httptest.NewRecorder()
|
||||||
|
handler.ServeHTTP(w, req)
|
||||||
|
assert.Equal(t, http.StatusSeeOther, w.Code)
|
||||||
|
assert.Equal(t, "/app/spaces", w.Header().Get("Location"))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetupRoutes_GuestRoutes(t *testing.T) {
|
||||||
|
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||||
|
a := newTestApp(dbi)
|
||||||
|
handler := SetupRoutes(a)
|
||||||
|
|
||||||
|
routes := []string{"/auth", "/auth/password"}
|
||||||
|
|
||||||
|
for _, path := range routes {
|
||||||
|
t.Run(path, func(t *testing.T) {
|
||||||
|
req := httptest.NewRequest(http.MethodGet, path, nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
handler.ServeHTTP(w, req)
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetupRoutes_GuestRoutes_RedirectAuthenticated(t *testing.T) {
|
||||||
|
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||||
|
a := newTestApp(dbi)
|
||||||
|
handler := SetupRoutes(a)
|
||||||
|
|
||||||
|
name := "Test User"
|
||||||
|
user := testutil.CreateTestUserWithName(t, dbi.DB, "auth@example.com", &name)
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/auth", nil)
|
||||||
|
req.AddCookie(authCookie(a, user))
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
handler.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusSeeOther, w.Code)
|
||||||
|
assert.Equal(t, "/app/dashboard", w.Header().Get("Location"))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetupRoutes_AuthRequired_RedirectUnauthenticated(t *testing.T) {
|
||||||
|
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||||
|
a := newTestApp(dbi)
|
||||||
|
handler := SetupRoutes(a)
|
||||||
|
|
||||||
|
routes := []string{"/app/spaces", "/app/settings"}
|
||||||
|
|
||||||
|
for _, path := range routes {
|
||||||
|
t.Run(path, func(t *testing.T) {
|
||||||
|
req := httptest.NewRequest(http.MethodGet, path, nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
handler.ServeHTTP(w, req)
|
||||||
|
assert.Equal(t, http.StatusSeeOther, w.Code)
|
||||||
|
assert.Equal(t, "/auth", w.Header().Get("Location"))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetupRoutes_AuthRequired_Authenticated(t *testing.T) {
|
||||||
|
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||||
|
a := newTestApp(dbi)
|
||||||
|
handler := SetupRoutes(a)
|
||||||
|
|
||||||
|
name := "Test User"
|
||||||
|
user := testutil.CreateTestUserWithName(t, dbi.DB, "appuser@example.com", &name)
|
||||||
|
|
||||||
|
routes := []string{"/app/spaces", "/app/settings"}
|
||||||
|
|
||||||
|
for _, path := range routes {
|
||||||
|
t.Run(path, func(t *testing.T) {
|
||||||
|
req := httptest.NewRequest(http.MethodGet, path, nil)
|
||||||
|
req.AddCookie(authCookie(a, user))
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
handler.ServeHTTP(w, req)
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetupRoutes_OnboardingRedirect(t *testing.T) {
|
||||||
|
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||||
|
a := newTestApp(dbi)
|
||||||
|
handler := SetupRoutes(a)
|
||||||
|
|
||||||
|
// User without name → needs onboarding
|
||||||
|
user := testutil.CreateTestUser(t, dbi.DB, "noname@example.com", nil)
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/app/spaces", nil)
|
||||||
|
req.AddCookie(authCookie(a, user))
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
handler.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusSeeOther, w.Code)
|
||||||
|
assert.Equal(t, "/auth/onboarding", w.Header().Get("Location"))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetupRoutes_PermanentRedirect(t *testing.T) {
|
||||||
|
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||||
|
a := newTestApp(dbi)
|
||||||
|
handler := SetupRoutes(a)
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/app/dashboard", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
handler.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusMovedPermanently, w.Code)
|
||||||
|
assert.Equal(t, "/app/spaces", w.Header().Get("Location"))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetupRoutes_NotFound(t *testing.T) {
|
||||||
|
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||||
|
a := newTestApp(dbi)
|
||||||
|
handler := SetupRoutes(a)
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/nonexistent/page", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
handler.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
// The catch-all route renders the NotFound page (handler returns 200).
|
||||||
|
// This test verifies the catch-all route is registered and handles unknown paths.
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetupRoutes_StaticAssets(t *testing.T) {
|
||||||
|
testutil.ForEachDB(t, func(t *testing.T, dbi testutil.DBInfo) {
|
||||||
|
a := newTestApp(dbi)
|
||||||
|
handler := SetupRoutes(a)
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/assets/css/output.css", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
handler.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
// Static asset should be served (200) or at minimum not 404 via the catch-all
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
assert.Contains(t, w.Header().Get("Cache-Control"), "public")
|
||||||
|
})
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue