docs: add custom section builder and backend testing to Carousel dashboard

- Add custom section builder functionality (Phase 9.3)
  - Template for creating filter-based sections with auto-assign rules
  - Dynamic rule builder UI (field, operator, value, priority)
  - Preview functionality to see matching books before creating
  - Integration with existing collections API

- Add TypeScript implementation (Phase 10.3)
  - web/src/custom-section-builder.ts
  - Procedural style with event delegation
  - Rule collection, preview, and form submission
  - No duplicate event listeners (delegation only)

- Add backend testing suite (Phase 12)
  - Unit tests for dashboard service (filter, reorder)
  - Unit tests for dashboard handler (buildSections, helpers)
  - Integration tests with test_helpers for API endpoints
  - Integration tests for custom collections with auto-assign
  - Coverage requirements (>80%)

- Add collections preview endpoint
  - POST /api/collections/preview
  - Evaluates auto-assign rules against library items
  - Returns matching books for preview

- Add /custom-section route
  - GET route for custom section builder page
  - SSR rendering with libraries selector
  - Linked from dashboard settings modal

- Update database schema
  - Keep smart_section_types table for 4 default smart sections
  - Add collection_items.excluded column for user overrides
  - Index on excluded items for performance

- Update verification checklist
  - Section 2.2: Add collection_items.excluded verification
  - Section 3.4: Add auto-assign rule evaluation verification
  - Section 6.3: Add collections preview endpoint verification
  - Section 8.5: Add custom section builder template verification
  - Section 9.4: Add custom section builder TypeScript verification
  - Section 14.4: Add backend tests verification

- Fix duplicate event listener issue
  - Removed duplicate change listener for library selector
  - Rely on event delegation only for consistency

- Fix buildJSONSections type safety
  - Now reuses buildSections() instead of map[string]interface{}
  - Better type safety and code reuse

Timeline: 3-4 days dashboard implementation + comprehensive testing
This commit is contained in:
2026-02-18 21:39:37 -05:00
parent 8d37df249d
commit 6d9b1e065e
2 changed files with 1774 additions and 1604 deletions
+1284 -1546
View File
File diff suppressed because it is too large Load Diff
+490 -58
View File
@@ -182,6 +182,14 @@ dropdb test_bookhoard
- [ ] Default value is `false`
- [ ] Index created on `(user_id, show_on_dashboard) WHERE show_on_dashboard = true`
**For `collection_items.excluded` column:**
- [ ] Column added with `ALTER TABLE collection_items ADD COLUMN`
- [ ] `IF NOT EXISTS` clause included
- [ ] Default value is `false`
- [ ] Index created on `(collection_id, excluded) WHERE excluded = true`
- [ ] Allows users to exclude auto-assigned items from filter-based collections
**For `smart_section_types`:**
- [ ] All required columns exist:
@@ -335,6 +343,79 @@ rg "filterHiddenSections|reorderSections" internal/services/dashboard_service.go
rg "if len.*== 0" internal/services/dashboard_service.go
```
### 3.4 Verify Auto-Assign Rule Evaluation
**For `getCollectionSections` method:**
- [ ] Fetches collections with `show_on_dashboard = true`
- [ ] Parses `auto_assign_rules` JSONB from collection
- [ ] Fetches all library items via `GetLibraryItems`
- [ ] Evaluates rules for each library item using `collectionService.EvaluateRules`
- [ ] Merges manual items (not excluded) + auto-matched items
- [ ] Filters out excluded items (where `excluded = true`)
- [ ] Applies limit after merging
- [ ] Only adds collection if it has items in current library
**Excluded items handling:**
- [ ] Manual items query returns `excluded` column from `collection_items`
- [ ] Filters out items where `excluded = true`
- [ ] Auto-matched items checked against manual items to avoid duplicates
- [ ] Excluded items not added even if they match rules
**Rule evaluation logic:**
- [ ] Calls `collectionService.EvaluateRules(item, rules)`
- [ ] Checks if `eval.Matches && eval.Confidence > 0.7`
- [ ] Adds matching items to collection
- [ ] Respects priority (higher priority rules evaluated first)
**Merge logic:**
```go
// Pseudo-code for merge logic
manualItems := getManualItems(collectionID) // with excluded=false filter
autoItems := evaluateAutoAssignRules(libraryItems, rules)
finalItems := merge(manualItems, autoItems)
finalItems = applyLimit(finalItems, limit)
```
**Verification:**
```bash
# Check getCollectionSections implementation
rg "func.*getCollectionSections" internal/services/dashboard_service.go -A 100
# Verify auto-assign rules parsing
rg "json.Unmarshal.*AutoAssignRules" internal/services/dashboard_service.go
# Verify EvaluateRules usage
rg "collectionService.EvaluateRules" internal/services/dashboard_service.go
# Check GetLibraryItems query
rg "GetLibraryItems" internal/services/dashboard_service.go
# Verify excluded items filtering
rg "Excluded.*Bool" internal/services/dashboard_service.go
rg "!item.Excluded.Valid || !item.Excluded.Bool" internal/services/dashboard_service.go
# Check manual + auto merge logic
rg "manualNonExcluded.*append.*autoItems" internal/services/dashboard_service.go
# Verify limit applied after merge
rg "len.*finalItems.*limit" internal/services/dashboard_service.go
```
**Edge case verification:**
- [ ] Collection with no auto-assign rules (manual only)
- [ ] Collection with auto-assign rules but no matches
- [ ] Collection where all manual items are excluded
- [ ] Collection with auto-assign rules + manual additions
- [ ] Collection with excluded items that match rules
- [ ] Large library (performance check)
**Integration with CollectionService:**
- [ ] DashboardService has collectionService dependency
- [ ] NewDashboardService creates CollectionService instance
- [ ] Reuses existing EvaluateRules from collection_service.go
- [ ] No duplicate rule evaluation logic
---
## 4. Database Queries Verification
@@ -593,7 +674,86 @@ rg 'GET.*"/settings"' internal/router/frontend.go -A 20
rg 'POST.*"/settings"' internal/router/frontend.go -A 40
```
### 6.3 Verify Config Setup
**For `/custom-section` route:**
- [ ] GET `/custom-section` → renders custom section builder form
- [ ] Passes libraries for selector
- [ ] Renders `templates.CustomSectionBuilder`
- [ ] Links from dashboard settings modal
- [ ] No POST route (form submits via JSON to `/api/collections`)
**Verification:**
```bash
# Check custom-section route
rg 'GET.*"/custom-section"' internal/router/frontend.go -A 20
# Verify template rendering
rg "templates.CustomSectionBuilder" internal/router/frontend.go
# Verify libraries passed to template
rg "CustomSectionBuilder.*libraries" internal/router/frontend.go
```
### 6.3 Verify Collections Preview Endpoint
**For `internal/handlers/collections.go`:**
- [ ] `PreviewAutoAssignRules` method exists
- [ ] POST `/api/collections/preview` route registered
- [ ] Accepts library_id, rules, limit in request body
- [ ] Evaluates rules against library items
- [ ] Returns matching books with count
- [ ] Uses existing `collectionService.EvaluateRules()`
- [ ] Returns `handlers.BookInfo` format
**Request format:**
```json
{
"library_id": "uuid",
"rules": [
{
"id": "rule1",
"field": "genre",
"operator": "equals",
"value": "Sci-Fi",
"priority": 5
}
],
"limit": 20
}
```
**Response format:**
```json
{
"books": [
{
"id": "uuid",
"title": "Dune",
"author": "Frank Herbert",
"cover_image_path": "/path/to/cover.jpg"
}
],
"count": 2
}
```
**Verification:**
```bash
# Check handler method exists
rg "func.*PreviewAutoAssignRules" internal/handlers/collections.go -A 30
# Check route registration
rg 'POST.*"/preview"' internal/router/collections.go
# Verify EvaluateRules usage
rg "collectionService.EvaluateRules" internal/handlers/collections.go
# Check BookInfo conversion
rg "handlers.BookInfo" internal/handlers/collections.go
```
### 6.4 Verify Config Setup
**For `internal/router/router.go`:**
@@ -853,29 +1013,36 @@ rg 'aria-|role=|tabindex' templates/
rg 'data-(action|section|book)=' templates/
```
### 8.3 Verify HTMX Integration
### 8.3 Verify Library Selector (TypeScript, Not HTMX)
**HTMX attributes:**
**Library selector attributes:**
- [ ] Library selector uses `hx-get="/dashboard/sections"`
- [ ] `hx-target="#sections-container"`
- [ ] `hx-indicator="#loading-spinner"`
- [ ] `hx-swap="innerHTML"`
- [ ] Forms have fallback `action` and `method`
- [ ] Select element has `id="library-select"` and `name="library_id"`
- [ ] NO HTMX attributes (no `hx-get`, `hx-target`, etc.)
- [ ] Uses `data-action="switch-library"` or change event listener
- [ ] Options rendered from SSR data (`libraries` parameter)
- [ ] Current library selected by default
**Partial template:**
**Loading indicator:**
- [ ] `DashboardSectionsPartial` exists
- [ ] Renders only sections (no full page)
- [ ] Used by HTMX swap
- [ ] `#loading-spinner` element exists (hidden by default)
- [ ] Used by TypeScript during library switching
- [ ] Shows/hides via CSS class manipulation
**Verification:**
```bash
# Check HTMX attributes
rg 'hx-(get|post|target|swap|indicator)=' templates/
# Check library selector (should NOT have HTMX)
rg 'id="library-select"' templates/dashboard.templ -A 5
# Verify partial template
rg "templ DashboardSectionsPartial" templates/ -A 10
# Verify NO HTMX attributes on library selector
rg 'id="library-select"' templates/dashboard.templ -A 5 | rg 'hx-'
# Should return nothing
# Check loading indicator exists
rg 'loading-spinner' templates/dashboard.templ
# Verify data-action for library switching
rg 'data-action="switch-library"' templates/dashboard.templ
```
### 8.4 Verify Settings Template
@@ -904,6 +1071,100 @@ rg 'data-action="save-settings"' templates/settings.templ
rg "database\.(Users|UserDashboardPreferences)" templates/settings.templ
```
### 8.5 Verify Custom Section Builder Template
**For `templates/custom_section.templ`:**
- [ ] SSR page for creating filter-based custom sections
- [ ] Uses **handler types** (libraries, user)
- [ ] Section details form (name, description, library selector)
- [ ] Auto-assign rules section with dynamic rule addition
- [ ] Preview section showing matching books
- [ ] Form uses `data-action="create-custom-section"`
- [ ] Cancel button uses `data-action="cancel-create-section"`
- [ ] TailwindCSS only
- [ ] Event delegation
- [ ] Includes required scripts (custom-section-builder.js)
- [ ] Links to `/custom-section` route
**Form structure verification:**
- [ ] Name input (required)
- [ ] Description textarea (optional)
- [ ] Library selector (required, populated from SSR)
- [ ] Rules container with `id="rules-container"`
- [ ] "Add Rule" button with `data-action="add-rule"`
- [ ] Preview button with `data-action="preview-section"`
- [ ] Submit button with `data-action="create-custom-section"`
- [ ] Cancel button with `data-action="cancel-create-section"`
**Rule fields (dynamically added via JavaScript):**
- [ ] Field selector (genre, author, series, language, publisher, copyright_year, tags)
- [ ] Operator selector (equals, contains, starts_with, ends_with, greater_than, less_than)
- [ ] Value input (text)
- [ ] Priority input (number, 1-10)
- [ ] Remove button with `data-action="remove-rule"` and `data-rule-id`
**Preview section:**
- [ ] Preview container with `id="preview-container"`
- [ ] Shows loading state while fetching
- [ ] Displays matching books in grid layout
- [ ] Shows count of matching books
- [ ] Handles empty results gracefully
**Dashboard settings modal enhancement:**
- [ ] "Create Custom Section" button in settings modal
- [ ] Links to `/custom-section` page
- [ ] Has description explaining what custom sections are
- [ ] Styled with accent color
**Verification:**
```bash
# Check template exists
ls -la templates/custom_section.templ
# Check template structure
rg "templ CustomSectionBuilder" templates/custom_section.templ -A 100
# Verify SSR data (libraries parameter)
rg "CustomSectionBuilder.*libraries" templates/custom_section.templ
# Verify data-action attributes
rg 'data-action="(add-rule|remove-rule|preview-section|create-custom-section|cancel-create-section)"' templates/custom_section.templ
# Verify form fields
rg 'name="name".*required' templates/custom_section.templ
rg 'name="library_id".*required' templates/custom_section.templ
rg 'name="description"' templates/custom_section.templ
# Verify rules container
rg 'id="rules-container"' templates/custom_section.templ
# Verify preview container
rg 'id="preview-container"' templates/custom_section.templ
# Verify TailwindCSS only (no custom CSS)
rg '<style' templates/custom_section.templ
# Should return nothing
# Verify event delegation (no inline onclick)
rg 'onclick=' templates/custom_section.templ
# Should return nothing
# Verify script includes
rg 'custom-section-builder.js' templates/custom_section.templ
# Check dashboard settings modal has Create Custom Section button
rg 'Create Custom Section' templates/dashboard.templ
rg '/custom-section' templates/dashboard.templ
```
**JavaScript integration verification:**
- [ ] No duplicate change event listener (library select uses delegation only)
- [ ] Event delegation handles all form actions
- [ ] Rules dynamically added via `insertAdjacentHTML`
- [ ] Form submission prevented, JSON sent via API
- [ ] Preview updates DOM without page reload
---
## 9. TypeScript Implementation Verification
@@ -938,15 +1199,20 @@ rg "import type " web/src/dashboard.ts
rg "api\.|showToast\.|events\." web/src/dashboard.ts
```
### 9.2 Verify Carousel Functionality
### 9.2 Verify TypeScript Dashboard Functions
**Required functions:**
- [ ] `scrollCarousel(sectionId: string, direction: number): void`
- [ ] `switchLibrary(libraryId: string): Promise<void>` - Fetches JSON and re-renders sections
- [ ] `renderSections(sections: SectionData[]): void` - Renders sections from JSON
- [ ] `renderBookCard(book: BookInfo): string` - Renders single book card HTML
- [ ] `openDashboardSettings(): void`
- [ ] `closeDashboardSettings(): void`
- [ ] `saveDashboardSettings(): void`
- [ ] `saveDashboardSettings(): Promise<void>`
- [ ] `toggleSectionVisibility(sectionId: string): void`
- [ ] `viewBook(bookId: string): Promise<void>`
- [ ] `reloadPage(): void`
- [ ] `updateItemsCount(count: number): void`
- [ ] `viewBook(bookId: string): void`
- [ ] `reloadPage(): void`
@@ -980,16 +1246,18 @@ rg "showToast\.(error|success|info)" web/src/dashboard.ts
**Event listeners:**
- [ ] Single click listener for all dashboard actions
- [ ] Library select change event listener (for `switchLibrary`)
- [ ] Uses `data-action` attributes
- [ ] Handles:
- [ ] `scroll-carousel`
- [ ] `open-dashboard-settings`
- [ ] `close-dashboard-settings`
- [ ] `save-dashboard-settings`
- [ ] `toggle-section-visibility`
- [ ] `update-items-count`
- [ ] `view-book`
- [ ] `reload-page`
- [ ] `switch-library` (library select changes)
- [ ] `scroll-carousel`
- [ ] `open-dashboard-settings`
- [ ] `close-dashboard-settings`
- [ ] `save-dashboard-settings`
- [ ] `toggle-section-visibility`
- [ ] `update-items-count`
- [ ] `view-book`
- [ ] `reload-page`
**Verification:**
```bash
@@ -1025,6 +1293,75 @@ rg "addEventListener\('submit'" web/src/settings.ts
rg "validity|checkValidity" web/src/settings.ts
```
### 9.4 Verify Custom Section Builder TypeScript
**For `web/src/custom-section-builder.ts`:**
- [ ] Procedural style (no classes, no `this`)
- [ ] Functions exported to `window` object
- [ ] Event delegation via `data-action` attributes
- [ ] Dynamic rule addition/removal
- [ ] Rule interface matches Go services.Rule
- [ ] Uses shared API client and toast
- [ ] Preview functionality with loading states
**Required functions:**
- [ ] `addRule(): void` - Adds new rule row to form
- [ ] `removeRule(ruleId: string): void` - Removes rule row
- [ ] `collectRules(): Rule[]` - Collects all rules from form
- [ ] `previewSection(): Promise<void>` - Calls preview API, displays results
- [ ] `createCustomSection(): Promise<void>` - Creates collection with rules
- [ ] `cancelCreateSection(): void` - Navigates back to dashboard
- [ ] `initializeCustomSectionBuilder(): void` - Sets up event delegation
**Rule structure verification:**
- [ ] Rule interface has: id, field, operator, value, priority
- [ ] Field options: genre, author, series, language, publisher, copyright_year, tags
- [ ] Operator options: equals, contains, starts_with, ends_with, greater_than, less_than
- [ ] Priority: 1-10 (default 5)
**Preview functionality:**
- [ ] Validates library_id is selected
- [ ] Validates at least one rule exists
- [ ] Calls `/api/collections/preview` endpoint
- [ ] Displays matching books in grid
- [ ] Shows count of matches
- [ ] Handles loading state
- [ ] Handles errors with toast notification
**Verification:**
```bash
# Check file exists
ls -la web/src/custom-section-builder.ts
# Check for OOP patterns (should find nothing)
rg "class |this\." web/src/custom-section-builder.ts
# Verify Rule interface
rg "interface Rule" web/src/custom-section-builder.ts -A 10
# Check required functions exist
rg "^function (addRule|removeRule|collectRules|previewSection|createCustomSection|cancelCreateSection)" web/src/custom-section-builder.ts
# Verify API integration
rg "api\.post.*collections/preview" web/src/custom-section-builder.ts
# Verify toast usage
rg "showToast\.(error|success|info)" web/src/custom-section-builder.ts
# Verify rule HTML generation
rg "ruleHTML|insertAdjacentHTML" web/src/custom-section-builder.ts
# Verify event delegation
rg "addEventListener\('click'" web/src/custom-section-builder.ts -A 50
```
**Form verification:**
- [ ] No duplicate change event listener (only delegation handles library select)
- [ ] Event delegation handles all actions
- [ ] Actions: add-rule, remove-rule, preview-section, create-custom-section, cancel-create-section
### 9.5 Verify TypeScript Compilation
**Build verification:**
@@ -1347,56 +1684,79 @@ rg 'overflow-x-auto' templates/
---
## 12. Progressive Enhancement Verification
## 12. SSR + TypeScript Hybrid Verification
### 12.1 Verify Works Without JavaScript
### 12.1 Verify SSR Initial Load
**Critical paths:**
- [ ] Dashboard loads with SSR data
- [ ] Library switcher uses HTMX (works without custom JS)
- [ ] Settings form submits with full page reload
- [ ] All data visible on initial load
- [ ] No content hidden behind JavaScript
- [ ] Dashboard loads with pre-populated SSR data (sections rendered by server)
- [ ] All sections and books visible in initial HTML (view source to verify)
- [ ] No client-side fetching on initial page load
- [ ] Library selector options rendered server-side
- [ ] Current library pre-selected in HTML
**Test procedure:**
1. Open DevTools → Disable JavaScript
2. Navigate to `/dashboard`
3. Verify:
- [ ] All sections render
- [ ] All books visible
- [ ] Library selector works (HTMX)
- [ ] Settings form submits
4. Re-enable JavaScript
5. Verify enhanced behavior works
1. Open DevTools → Network tab
2. Navigate to `/dashboard?library_id=<id>`
3. Verify in HTML response:
- [ ] Sections present in initial HTML (not added via JS)
- [ ] Books present in initial HTML
- [ ] Library selector has all options
4. View page source (Ctrl+U) and verify:
- [ ] Section HTML present in source
- [ ] Book cards present in source
**Verification:**
```bash
# Check template has SSR data
rg "templ Dashboard" templates/dashboard.templ -A 5
# Should have sections parameter
# Check template has sections parameter (SSR)
rg "templ Dashboard.*sections" templates/dashboard.templ -A 5
# Check for client-side data fetching (should be minimal)
rg "fetch\(" web/src/dashboard.ts
# Should only be for updates, not initial load
# Verify frontend route passes data to template
rg "templates.Dashboard.*sections" internal/router/frontend.go -A 5
# Check for initial load fetching (should be NONE)
rg "fetch.*onload|DOMContentLoaded.*fetch" web/src/dashboard.ts
# Should return nothing - no fetch on initial load
```
### 12.2 Verify HTMX Fallbacks
### 12.2 Verify TypeScript Updates
**All HTMX attributes:**
**Critical paths:**
- [ ] Forms have `action` and `method` (non-JS fallback)
- [ ] Links have `href` (non-JS fallback)
- [ ] HTMX attributes enhance but don't replace
- [ ] No content requires JavaScript to function
- [ ] Library switching triggers JSON fetch
- [ ] Loading indicator shows during fetch
- [ ] Sections re-render after library switch
- [ ] Settings save via API, then page reload
- [ ] Error handling with toast notifications
**Test procedure:**
1. Load dashboard (verify SSR data present)
2. Change library selector
3. Verify:
- [ ] Loading spinner appears
- [ ] Network tab shows `/api/dashboard/sections?library_id=<new>` request
- [ ] Sections update without full page reload
- [ ] URL updates with `?library_id=<new>`
4. Open settings, change items per section
5. Verify:
- [ ] API POST to `/dashboard/settings`
- [ ] Success toast appears
- [ ] Page reloads with new settings applied
**Verification:**
```bash
# Check HTMX forms have standard attributes
rg '<form' templates/ -A 3 | rg 'action=|method='
# Check TypeScript has switchLibrary function
rg "function switchLibrary|async switchLibrary" web/src/dashboard.ts -A 10
# Check links have href
rg '<a ' templates/ | rg 'href='
# Verify it fetches JSON
rg "fetch.*api/dashboard/sections" web/src/dashboard.ts
# Verify renderSections function exists
rg "function renderSections" web/src/dashboard.ts -A 10
# Check settings API call
rg "api.post.*settings|fetch.*settings" web/src/dashboard.ts
```
---
@@ -1603,6 +1963,78 @@ rg "backdrop-filter|aspect-ratio|snap" templates/
# Verify fallbacks if needed
```
### 14.4 Verify Backend Tests Exist
**Unit tests for dashboard service:**
- [ ] `internal/services/dashboard_service_test.go` exists
- [ ] Tests for:
- [ ] `TestDashboardService_FilterHiddenSections` - Tests section filtering
- [ ] `TestDashboardService_ReorderSections` - Tests custom ordering
- [ ] `TestDashboardService_GetSectionItems` - Validates method signature
- [ ] Uses testify/assert and testify/require
- [ ] Tests cover edge cases (empty arrays, nil values, duplicates)
**Unit tests for dashboard handler:**
- [ ] `internal/handlers/dashboard_handler_test.go` exists
- [ ] Tests for:
- [ ] `TestBuildSections` - Tests conversion from service to handler types
- [ ] `TestGetSectionHelpers` - Tests helper functions
- [ ] getSectionType, getSectionTitle, getSectionIcon
- [ ] Validates pgtype field conversion
**Integration tests with test_helpers:**
- [ ] `internal/handlers/dashboard_integration_test.go` exists
- [ ] Uses `test_helpers.TestSuite` for database setup/teardown
- [ ] Tests for:
- [ ] `TestGetSections` - Full dashboard sections API
- [ ] `TestGetSections_UserPreferences` - Preferences (hidden, order, limits)
- [ ] `TestGetSections_CustomCollections` - Auto-assign rules
- [ ] `TestGetSections_Validation` - Error handling (400 errors)
- [ ] Creates test fixtures (users, libraries, media items)
- [ ] Tests reading progress (in progress, completed, unread)
- [ ] Tests manual + auto-matched items merging
- [ ] Tests excluded items filtering
**Integration tests for collections:**
- [ ] `internal/handlers/collections_integration_test.go` modified
- [ ] Adds:
- [ ] `TestPreviewAutoAssignRules` - Tests rule preview endpoint
- [ ] `TestCreateCollectionWithAutoAssign` - Tests collection creation
- [ ] Validates genre filtering works
- [ ] Validates auto-assign rules persist to database
**Verification:**
```bash
# Check test files exist
ls -la internal/services/dashboard_service_test.go
ls -la internal/handlers/dashboard_handler_test.go
ls -la internal/handlers/dashboard_integration_test.go
# Run tests
go test ./internal/services/dashboard_service_test.go -v
go test ./internal/handlers/dashboard_handler_test.go -v
go test ./internal/handlers/dashboard_integration_test.go -v
# Run with coverage
go test ./internal/... -cover -coverprofile=coverage.out
go tool cover -html=coverage.out
# Verify test helpers usage
rg "test_helpers.TestSuite" internal/handlers/dashboard_integration_test.go
rg "CreateTestUser|CreateTestLibrary|CreateTestMediaItem" internal/handlers/dashboard_integration_test.go
```
**Test coverage requirements:**
- [ ] Unit tests for all service methods (filterHiddenSections, reorderSections)
- [ ] Unit tests for all helper functions
- [ ] Integration tests for all API endpoints
- [ ] Integration tests use test_helpers
- [ ] Coverage > 80% for new code
---
## 15. Cross-Reference Verification