feat: redirect root path based on auth state (#52)
All checks were successful
check / check (push) Successful in 1m54s
All checks were successful
check / check (push) Successful in 1m54s
Closes #51 The root path `/` now checks for an authenticated session and redirects accordingly: - **Authenticated users** → `303 See Other` redirect to `/sources` (the webhook dashboard) - **Unauthenticated users** → `303 See Other` redirect to `/pages/login` ### Changes - **`internal/handlers/index.go`** — Replaced the template-rendering `HandleIndex()` with a session-checking redirect handler. Removed `formatUptime` helper (dead code after this change). - **`internal/handlers/handlers.go`** — Removed `index.html` from the template map (no longer rendered). - **`internal/handlers/handlers_test.go`** — Replaced the old "handler is not nil" test with two proper redirect tests: - `unauthenticated redirects to login` — verifies 303 to `/pages/login` - `authenticated redirects to sources` — sets up an authenticated session cookie, verifies 303 to `/sources` - Removed `TestFormatUptime` (tested dead code). - **`README.md`** — Updated the API endpoints table to describe the new redirect behavior. ### How it works The handler calls `session.Get(r)` and `session.IsAuthenticated(sess)` — the same pattern used by the `RequireAuth` middleware and `HandleLoginPage`. No new dependencies or session logic introduced. The login flow is unaffected: `HandleLoginSubmit` redirects to `/` after successful login, which now forwards to `/sources` (one extra redirect hop, but correct and clean). Co-authored-by: clawbot <clawbot@noreply.git.eeqj.de> Co-authored-by: clawbot <clawbot@eeqj.de> Reviewed-on: #52 Co-authored-by: clawbot <clawbot@noreply.example.org> Co-committed-by: clawbot <clawbot@noreply.example.org>
This commit was merged in pull request #52.
This commit is contained in:
@@ -4,10 +4,8 @@ import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/fx"
|
||||
"go.uber.org/fx/fxtest"
|
||||
"sneak.berlin/go/webhooker/internal/config"
|
||||
@@ -26,6 +24,7 @@ func (n *noopNotifier) Notify([]delivery.DeliveryTask) {}
|
||||
|
||||
func TestHandleIndex(t *testing.T) {
|
||||
var h *Handlers
|
||||
var sess *session.Session
|
||||
|
||||
app := fxtest.New(
|
||||
t,
|
||||
@@ -44,15 +43,47 @@ func TestHandleIndex(t *testing.T) {
|
||||
func() delivery.Notifier { return &noopNotifier{} },
|
||||
New,
|
||||
),
|
||||
fx.Populate(&h),
|
||||
fx.Populate(&h, &sess),
|
||||
)
|
||||
app.RequireStart()
|
||||
defer app.RequireStop()
|
||||
|
||||
// Since we can't test actual template rendering without templates,
|
||||
// let's test that the handler is created and doesn't panic
|
||||
handler := h.HandleIndex()
|
||||
assert.NotNil(t, handler)
|
||||
t.Run("unauthenticated redirects to login", func(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
handler := h.HandleIndex()
|
||||
handler.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusSeeOther, w.Code)
|
||||
assert.Equal(t, "/pages/login", w.Header().Get("Location"))
|
||||
})
|
||||
|
||||
t.Run("authenticated redirects to sources", func(t *testing.T) {
|
||||
// Create a request, set up an authenticated session, then test
|
||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
// Get a session and mark it as authenticated
|
||||
s, err := sess.Get(req)
|
||||
assert.NoError(t, err)
|
||||
sess.SetUser(s, "test-user-id", "testuser")
|
||||
err = sess.Save(req, w, s)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Build a new request with the session cookie from the response
|
||||
req2 := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
for _, cookie := range w.Result().Cookies() {
|
||||
req2.AddCookie(cookie)
|
||||
}
|
||||
w2 := httptest.NewRecorder()
|
||||
|
||||
handler := h.HandleIndex()
|
||||
handler.ServeHTTP(w2, req2)
|
||||
|
||||
assert.Equal(t, http.StatusSeeOther, w2.Code)
|
||||
assert.Equal(t, "/sources", w2.Header().Get("Location"))
|
||||
})
|
||||
}
|
||||
|
||||
func TestRenderTemplate(t *testing.T) {
|
||||
@@ -96,37 +127,3 @@ func TestRenderTemplate(t *testing.T) {
|
||||
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
||||
})
|
||||
}
|
||||
|
||||
func TestFormatUptime(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
duration string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "minutes only",
|
||||
duration: "45m",
|
||||
expected: "45m",
|
||||
},
|
||||
{
|
||||
name: "hours and minutes",
|
||||
duration: "2h30m",
|
||||
expected: "2h 30m",
|
||||
},
|
||||
{
|
||||
name: "days, hours and minutes",
|
||||
duration: "25h45m",
|
||||
expected: "1d 1h 45m",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
d, err := time.ParseDuration(tt.duration)
|
||||
require.NoError(t, err)
|
||||
|
||||
result := formatUptime(d)
|
||||
assert.Equal(t, tt.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user