Files
cv-site/internal/handlers/cv_pages_test.go
T
juanatsap 71d9258c58 feat: add application-level data caching for CV/UI
Eliminate per-request file I/O by loading CV and UI data once at startup.

## Problem
- LoadCV() and LoadUI() were called on every request
- Each call read from disk and unmarshaled JSON
- 6 locations affected: cv_cmdk, cv_helpers, cv_contact

## Solution
- New `internal/cache` package with language-keyed cache
- Data loaded once at startup via `cache.New(["en", "es"])`
- Handlers use `h.dataCache.GetCV(lang)` / `GetUI(lang)`
- Thread-safe concurrent reads via sync.RWMutex
- Deep copy for mutable slices (Experience, Projects)

## Performance
- Before: ~3ms file I/O per request
- After: <1µs cache lookup (~3000x improvement)

## Files
- internal/cache/data_cache.go (new)
- internal/cache/data_cache_test.go (new)
- internal/cache/README.md (new)
- internal/handlers/cv.go (added dataCache field)
- internal/handlers/cv_*.go (use cache)
- main.go (initialize cache at startup)
2025-12-06 15:57:23 +00:00

176 lines
4.0 KiB
Go

package handlers
import (
"net/http"
"net/http/httptest"
"testing"
)
// TestHome tests the Home handler
func TestHome(t *testing.T) {
handler := newTestCVHandler(t, "localhost:8080", nil)
tests := []struct {
name string
lang string
expectStatus int
expectContains string
}{
{
name: "Default language (English)",
lang: "",
expectStatus: http.StatusOK,
expectContains: "Juan Andrés Moreno Rubio",
},
{
name: "English language",
lang: "en",
expectStatus: http.StatusOK,
expectContains: "Juan Andrés Moreno Rubio",
},
{
name: "Spanish language",
lang: "es",
expectStatus: http.StatusOK,
expectContains: "Juan Andrés Moreno Rubio",
},
{
name: "Invalid language",
lang: "fr",
expectStatus: http.StatusBadRequest,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Create request
req := httptest.NewRequest(http.MethodGet, "/?lang="+tt.lang, nil)
w := httptest.NewRecorder()
// Call handler
handler.Home(w, req)
// Check status code
if w.Code != tt.expectStatus {
t.Errorf("Expected status %d, got %d", tt.expectStatus, w.Code)
}
// Check response body contains expected content (if success)
if tt.expectStatus == http.StatusOK && tt.expectContains != "" {
body := w.Body.String()
if len(body) == 0 {
t.Error("Expected non-empty response body")
}
}
})
}
}
// TestCVContent tests the CVContent handler
func TestCVContent(t *testing.T) {
handler := newTestCVHandler(t, "localhost:8080", nil)
tests := []struct {
name string
lang string
expectStatus int
}{
{
name: "Default language",
lang: "",
expectStatus: http.StatusOK,
},
{
name: "English language",
lang: "en",
expectStatus: http.StatusOK,
},
{
name: "Spanish language",
lang: "es",
expectStatus: http.StatusOK,
},
{
name: "Invalid language",
lang: "de",
expectStatus: http.StatusBadRequest,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Create request
req := httptest.NewRequest(http.MethodGet, "/cv-content?lang="+tt.lang, nil)
w := httptest.NewRecorder()
// Call handler
handler.CVContent(w, req)
// Check status code
if w.Code != tt.expectStatus {
t.Errorf("Expected status %d, got %d", tt.expectStatus, w.Code)
}
})
}
}
// TestDefaultCVShortcut tests the DefaultCVShortcut handler
// NOTE: This test requires a running server for PDF generation via chromedp
func TestDefaultCVShortcut(t *testing.T) {
// Skip if running in CI or if server is not available
// PDF generation requires a running HTTP server that chromedp can connect to
if testing.Short() {
t.Skip("Skipping PDF generation test - requires running server")
}
handler := newTestCVHandler(t, "localhost:8080", nil)
tests := []struct {
name string
path string
expectStatus int
}{
{
name: "Valid shortcut URL (current year EN)",
path: "/cv-jamr-2025-en.pdf",
expectStatus: http.StatusOK,
},
{
name: "Valid shortcut URL (current year ES)",
path: "/cv-jamr-2025-es.pdf",
expectStatus: http.StatusOK,
},
{
name: "Invalid year",
path: "/cv-jamr-2020-en.pdf",
expectStatus: http.StatusNotFound,
},
{
name: "Invalid language",
path: "/cv-jamr-2025-fr.pdf",
expectStatus: http.StatusNotFound,
},
{
name: "Invalid format",
path: "/cv-wrong-format.pdf",
expectStatus: http.StatusNotFound,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Create request
req := httptest.NewRequest(http.MethodGet, tt.path, nil)
w := httptest.NewRecorder()
// Call handler
handler.DefaultCVShortcut(w, req)
// Check status code
if w.Code != tt.expectStatus {
t.Errorf("Expected status %d, got %d", tt.expectStatus, w.Code)
}
})
}
}