71d9258c58
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)
176 lines
4.0 KiB
Go
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)
|
|
}
|
|
})
|
|
}
|
|
}
|