fix: buffer template execution to prevent corrupt HTML responses (closes #42)
Add renderTemplate helper method on Handlers that renders templates to a bytes.Buffer first, then writes to the ResponseWriter only on success. This prevents partial/corrupt HTML when template execution fails partway through. Applied to all template rendering call sites in: - setup.go (HandleSetupGET, renderSetupError) - auth.go (HandleLoginGET, HandleLoginPOST error paths) - dashboard.go (HandleDashboard) - app.go (HandleAppNew, HandleAppCreate, HandleAppDetail, HandleAppEdit, HandleAppUpdate, HandleAppDeployments)
This commit is contained in:
73
internal/handlers/render_template_test.go
Normal file
73
internal/handlers/render_template_test.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package handlers_test
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// TestRenderTemplateBuffersOutput verifies that successful template rendering
|
||||
// produces a complete HTML response (not partial/corrupt).
|
||||
func TestRenderTemplateBuffersOutput(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCtx := setupTestHandlers(t)
|
||||
|
||||
// The setup page is simple and has no DB dependencies
|
||||
request := httptest.NewRequest(http.MethodGet, "/setup", nil)
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
handler := testCtx.handlers.HandleSetupGET()
|
||||
handler.ServeHTTP(recorder, request)
|
||||
|
||||
assert.Equal(t, http.StatusOK, recorder.Code)
|
||||
|
||||
body := recorder.Body.String()
|
||||
// A properly buffered response should contain the closing </html> tag,
|
||||
// proving the full template was rendered before being sent.
|
||||
assert.Contains(t, body, "</html>")
|
||||
// Should NOT contain the error text that would be appended on failure
|
||||
assert.NotContains(t, body, "Internal Server Error")
|
||||
}
|
||||
|
||||
// TestDashboardRenderTemplateBuffersOutput verifies the dashboard handler
|
||||
// also uses buffered template rendering.
|
||||
func TestDashboardRenderTemplateBuffersOutput(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCtx := setupTestHandlers(t)
|
||||
|
||||
request := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
handler := testCtx.handlers.HandleDashboard()
|
||||
handler.ServeHTTP(recorder, request)
|
||||
|
||||
assert.Equal(t, http.StatusOK, recorder.Code)
|
||||
|
||||
body := recorder.Body.String()
|
||||
assert.Contains(t, body, "</html>")
|
||||
assert.NotContains(t, body, "Internal Server Error")
|
||||
}
|
||||
|
||||
// TestLoginRenderTemplateBuffersOutput verifies the login handler
|
||||
// uses buffered template rendering.
|
||||
func TestLoginRenderTemplateBuffersOutput(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCtx := setupTestHandlers(t)
|
||||
|
||||
request := httptest.NewRequest(http.MethodGet, "/login", nil)
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
handler := testCtx.handlers.HandleLoginGET()
|
||||
handler.ServeHTTP(recorder, request)
|
||||
|
||||
assert.Equal(t, http.StatusOK, recorder.Code)
|
||||
|
||||
body := recorder.Body.String()
|
||||
assert.Contains(t, body, "</html>")
|
||||
assert.NotContains(t, body, "Internal Server Error")
|
||||
}
|
||||
Reference in New Issue
Block a user