diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-02-07 15:42:07 -1000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-02-07 15:42:07 -1000 |
| commit | d793e16f336189d38a9d43310713dd13e3b7c438 (patch) | |
| tree | b1e53d42069619912e683ddd1a58ed67b1873190 /internal/handlers/handlers_test.go | |
| parent | 0620afc98fdc0f764e82807bb0090b78618ddb1d (diff) | |
Add build version footer, deploy ldflags, template test helper, and logs script
- Display build commit hash in unobtrusive footer overlay
- Inject buildCommit/buildTime via ldflags in deploy.sh
- Add assertTemplateContains test helper, refactor existing template tests
- Add scripts/logs for fetching production journalctl via SSH
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'internal/handlers/handlers_test.go')
| -rw-r--r-- | internal/handlers/handlers_test.go | 87 |
1 files changed, 65 insertions, 22 deletions
diff --git a/internal/handlers/handlers_test.go b/internal/handlers/handlers_test.go index 1842c38..0ad36b2 100644 --- a/internal/handlers/handlers_test.go +++ b/internal/handlers/handlers_test.go @@ -1901,6 +1901,18 @@ func TestHandleTimeline_RendersDataToTemplate(t *testing.T) { } } +// assertTemplateContains reads a template file and asserts it contains the expected string. +func assertTemplateContains(t *testing.T, templatePath, expected, errMsg string) { + t.Helper() + content, err := os.ReadFile(templatePath) + if err != nil { + t.Skipf("Cannot read template file: %v", err) + } + if !strings.Contains(string(content), expected) { + t.Error(errMsg) + } +} + // ============================================================================= // Dashboard Content Verification Tests // ============================================================================= @@ -1945,34 +1957,18 @@ func TestHandleDashboard_ContainsSettingsLink(t *testing.T) { // contains a link to /settings. This catches regressions where the settings // button is accidentally removed. func TestDashboardTemplate_HasSettingsLink(t *testing.T) { - // Read the actual template file and verify it contains a settings link - content, err := os.ReadFile("../../web/templates/index.html") - if err != nil { - t.Skipf("Cannot read template file (running from unexpected directory): %v", err) - } - - templateStr := string(content) - - if !strings.Contains(templateStr, `href="/settings"`) { - t.Error("index.html must contain a link to /settings (settings button missing from UI)") - } + assertTemplateContains(t, "../../web/templates/index.html", + `href="/settings"`, + "index.html must contain a link to /settings (settings button missing from UI)") } // TestSettingsTemplate_HasCSRFToken verifies the settings page exposes a CSRF // token in a meta tag so that JavaScript (e.g. passkey registration) can include // it in POST requests. Without this, passkey registration fails with 403. func TestSettingsTemplate_HasCSRFToken(t *testing.T) { - content, err := os.ReadFile("../../web/templates/settings.html") - if err != nil { - t.Skipf("Cannot read template file: %v", err) - } - tmpl := string(content) - - // The template must include a meta tag or hidden input with the Go template - // variable .CSRFToken so JS can retrieve it - if !strings.Contains(tmpl, ".CSRFToken") { - t.Error("settings.html must expose .CSRFToken (e.g. via meta tag) for JavaScript passkey registration") - } + assertTemplateContains(t, "../../web/templates/settings.html", + ".CSRFToken", + "settings.html must expose .CSRFToken (e.g. via meta tag) for JavaScript passkey registration") } // TestHandleSettingsPage_PassesCSRFToken verifies the settings handler includes @@ -2007,6 +2003,53 @@ func TestHandleSettingsPage_PassesCSRFToken(t *testing.T) { } } +// TestHandleDashboard_PassesBuildVersion verifies the dashboard handler includes +// BuildVersion in the template data. +func TestHandleDashboard_PassesBuildVersion(t *testing.T) { + db, cleanup := setupTestDB(t) + defer cleanup() + + mockTodoist := &mockTodoistClient{} + mockTrello := &mockTrelloClient{} + renderer := NewMockRenderer() + + h := &Handler{ + store: db, + todoistClient: mockTodoist, + trelloClient: mockTrello, + config: &config.Config{CacheTTLMinutes: 5}, + renderer: renderer, + BuildVersion: "abc123", + } + + req := httptest.NewRequest("GET", "/", nil) + w := httptest.NewRecorder() + + h.HandleDashboard(w, req) + + if w.Code != http.StatusOK { + t.Fatalf("Expected status 200, got %d", w.Code) + } + + if len(renderer.Calls) == 0 { + t.Fatal("Expected renderer to be called") + } + + lastCall := renderer.Calls[len(renderer.Calls)-1] + dataStr := fmt.Sprintf("%+v", lastCall.Data) + if !strings.Contains(dataStr, "abc123") { + t.Error("Dashboard template data must include BuildVersion value") + } +} + +// TestDashboardTemplate_HasBuildVersion verifies that index.html contains +// a build version placeholder so users can see the deployed version. +func TestDashboardTemplate_HasBuildVersion(t *testing.T) { + assertTemplateContains(t, "../../web/templates/index.html", + ".BuildVersion", + "index.html must contain .BuildVersion to display the build version in the footer") +} + // TestTimelineTemplate_CheckboxesTargetSelf verifies that completion checkboxes // in the timeline calendar grid target their parent item, not #tab-content. func TestTimelineTemplate_CheckboxesTargetSelf(t *testing.T) { |
