Add backend detection for import directives with actionable error message Display warning banner for unsupported features (file_server, redirects) Ensure multi-file import button always visible in upload form Add accessibility attributes (role, aria-labelledby) to multi-site modal Fix 12 frontend unit tests with outdated hook mock interfaces Add data-testid attributes for E2E test reliability Fix JSON syntax in 4 translation files (missing commas) Create 6 diagnostic E2E tests covering import edge cases Addresses Reddit feedback on Caddy import UX confusion
21 KiB
Caddy Import Debug Test Suite - Full Execution Report
Date: January 30, 2026 Configuration: Production-like (Setup → Security Tests → Caddy Tests) Total Execution Time: 4.2 minutes Environment: Chromium, Docker container @ localhost:8080
Executive Summary
Executed complete Caddy Import Debug test suite with full production dependencies (87 security tests + 6 diagnostic tests). 3 critical user-facing issues discovered that prevent users from understanding import failures and limitations.
Critical Finding: Backend correctly parses and flags problematic Caddyfiles, but frontend fails to display all warnings/errors to users, creating a silent failure experience.
Test Results Overview
| Test | Status | Duration | Issue Found | Severity |
|---|---|---|---|---|
| 1: Simple Valid Caddyfile | ✅ PASS | 1.4s | None - baseline working | 🟢 N/A |
| 2: Import Directives | ❌ FAIL | 6.5s | Import directives silently ignored | 🔴 CRITICAL |
| 3: File Server Only | ❌ FAIL | 6.4s | Warnings not displayed to user | 🔴 CRITICAL |
| 4: Invalid Syntax | ✅ PASS | 1.4s | None - errors shown correctly | 🟢 N/A |
| 5: Mixed Content | ✅ PASS | 1.4s | None - mixed parsing works | 🟢 N/A |
| 6: Multi-File Upload | ❌ FAIL | 6.7s | Multi-file UI uses textareas, not file uploads | 🟡 HIGH |
Pass Rate: 50% (3/6) Critical Issues: 3 User-Facing Bugs: 3
Detailed Test Analysis
✅ Test 1: Simple Valid Caddyfile (PASSED)
Objective: Validate baseline happy path functionality Result: ✅ PASS - Import pipeline working correctly
API Response:
{
"preview": {
"hosts": [
{
"domain_names": "test-simple.example.com",
"forward_scheme": "https",
"forward_host": "localhost",
"forward_port": 3000,
"ssl_forced": true,
"websocket_support": false,
"warnings": null
}
]
}
}
Observations:
- ✅ Backend successfully parsed Caddyfile
- ✅ Caddy CLI adaptation successful (200 OK)
- ✅ Host extracted with correct forward target
- ✅ UI displayed domain and target correctly
- ✅ No errors or warnings generated
Conclusion: Core import functionality is working as designed.
❌ Test 2: Import Directives (FAILED)
Objective: Verify backend detects import directives and provides actionable error
Result: ❌ FAIL - Import directives silently processed, no user feedback
Input:
import sites.d/*.caddy
admin.example.com {
reverse_proxy localhost:9090
}
API Response:
{
"preview": {
"hosts": [
{
"domain_names": "admin.example.com",
"forward_scheme": "https",
"forward_host": "localhost",
"forward_port": 9090
}
]
}
}
What Happened:
- ❌ Backend did NOT detect import directives (responseBody.imports was undefined)
- ✅ Backend successfully parsed the non-import host
- ❌ API returned 200 OK (should be 400 or include warning)
- ❌ UI showed no error message (test expected
.bg-red-900element)
Root Cause:
The import directive is being silently ignored by caddy adapt. The backend doesn't detect or flag it, so users think their import worked correctly when it actually didn't.
What Users See:
- ✅ Success response with 1 host
- ❌ No indication that
import sites.d/*.caddywas ignored - ❌ No guidance to use multi-file upload
What Users Should See:
⚠️ WARNING: Import directives detected
Your Caddyfile contains "import" directives which cannot be processed in single-file mode.
Detected imports:
• import sites.d/*.caddy
To import multiple files:
1. Click "Multi-site Import" below
2. Add each file's content separately
3. Parse all files together
[Use Multi-site Import]
Backend Changes Required:
File: backend/internal/import/service.go
// After adapting, scan for import directives
func detectImports(content string) []string {
var imports []string
scanner := bufio.NewScanner(strings.NewReader(content))
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if strings.HasPrefix(line, "import ") {
imports = append(imports, line)
}
}
return imports
}
// In PreviewImport() function, add:
imports := detectImports(caddyfileContent)
if len(imports) > 0 {
return nil, &types.ImportError{
Message: "Import directives detected. Use multi-file upload.",
Imports: imports,
Code: "IMPORT_DIRECTIVE_FOUND",
}
}
Frontend Changes Required:
File: frontend/src/pages/tasks/ImportCaddyfile.tsx
// In handleUpload() error handler:
if (error.code === 'IMPORT_DIRECTIVE_FOUND') {
setError({
type: 'warning',
message: 'Import directives detected',
details: error.imports,
action: {
label: 'Use Multi-site Import',
onClick: () => setShowMultiSiteModal(true)
}
});
}
Severity: 🔴 CRITICAL Impact: Users unknowingly lose configuration when import directives are silently ignored Estimated Effort: 4 hours (Backend: 2h, Frontend: 2h)
❌ Test 3: File Server Only (FAILED)
Objective: Verify user receives feedback when all hosts are unsupported (file servers) Result: ❌ FAIL - Backend flags warnings, but frontend doesn't display them
Input:
static.example.com {
file_server
root * /var/www/html
}
docs.example.com {
file_server browse
root * /var/www/docs
}
API Response:
{
"preview": {
"hosts": [
{
"domain_names": "static.example.com",
"forward_scheme": "",
"forward_host": "",
"forward_port": 0,
"warnings": ["File server directives not supported"]
},
{
"domain_names": "docs.example.com",
"forward_scheme": "",
"forward_host": "",
"forward_port": 0,
"warnings": ["File server directives not supported"]
}
]
}
}
What Happened:
- ✅ Backend correctly parsed both hosts
- ✅ Backend correctly added warning: "File server directives not supported"
- ✅ Backend correctly set empty forward_host/forward_port (indicating no proxy)
- ❌ Frontend did NOT display any warning/error message
- ❌ Test expected yellow/red warning banner (
.bg-yellow-900or.bg-red-900)
Root Cause: Frontend receives hosts with warnings array, but the UI component either:
- Doesn't render warning banners at all
- Only renders warnings if zero hosts are returned
- Has incorrect CSS class names for warning indicators
What Users See:
- 2 hosts in preview table
- No indication these hosts won't work
- Users might try to import them (will fail silently or at next step)
What Users Should See:
⚠️ WARNING: Unsupported features detected
The following hosts contain directives that Charon cannot import:
• static.example.com - File server directives not supported
• docs.example.com - File server directives not supported
Charon only imports reverse proxy configurations. File servers, redirects,
and other Caddy features must be configured manually.
[Show Advanced] [Continue Anyway] [Cancel]
Frontend Changes Required:
File: frontend/src/pages/tasks/ImportCaddyfile.tsx
// After API response, check for warnings:
const hostsWithWarnings = preview.hosts.filter(h => h.warnings?.length > 0);
if (hostsWithWarnings.length > 0) {
return (
<Alert variant="warning" className="mb-4">
<AlertTriangle className="h-4 w-4" />
<AlertTitle>Unsupported Features Detected</AlertTitle>
<AlertDescription>
<p>The following hosts contain directives that cannot be imported:</p>
<ul className="mt-2 space-y-1">
{hostsWithWarnings.map(host => (
<li key={host.domain_names}>
<strong>{host.domain_names}</strong>
<ul className="ml-4 text-sm text-yellow-200">
{host.warnings.map((w, i) => <li key={i}>• {w}</li>)}
</ul>
</li>
))}
</ul>
</AlertDescription>
</Alert>
);
}
Additionally, if all hosts have warnings (none are importable):
if (preview.hosts.length > 0 && hostsWithWarnings.length === preview.hosts.length) {
return (
<Alert variant="destructive">
<XCircle className="h-4 w-4" />
<AlertTitle>No Importable Hosts Found</AlertTitle>
<AlertDescription>
All hosts in this Caddyfile use features that Charon cannot import.
Charon only imports reverse proxy configurations.
</AlertDescription>
</Alert>
);
}
Severity: 🔴 CRITICAL Impact: Users unknowingly attempt to import unsupported configurations Estimated Effort: 3 hours (Frontend warning display logic + UI components)
✅ Test 4: Invalid Syntax (PASSED)
Objective: Verify clear error message for invalid Caddyfile syntax Result: ✅ PASS - Errors displayed correctly (with minor improvement needed)
Input:
broken.example.com {
reverse_proxy localhost:3000
this is invalid syntax
another broken line
}
API Response:
{
"error": "import failed: caddy adapt failed: exit status 1 (output: )"
}
What Happened:
- ✅ Backend correctly rejected invalid syntax (400 Bad Request)
- ✅ Error message mentions "caddy adapt failed"
- ⚠️ Error does NOT include line number (would be helpful)
- ✅ UI displayed error message correctly
Observations:
- Error is functional but not ideal
- Would benefit from capturing
caddy adaptstderr output - Line numbers would help users locate the issue
Minor Improvement Recommended:
Capture stderr from caddy adapt command:
cmd := exec.Command("caddy", "adapt", "--config", tmpFile)
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
return &types.ImportError{
Message: "Caddyfile syntax error",
Details: stderr.String(), // Include caddy's error message
Code: "SYNTAX_ERROR",
}
}
Severity: 🟢 LOW (Working, but could be better) Estimated Effort: 1 hour (Backend stderr capture)
✅ Test 5: Mixed Content (PASSED)
Objective: Verify partial import (valid + unsupported hosts) provides detailed feedback Result: ✅ PASS - Backend correctly parsed mixed content, warnings included
Input:
# Valid reverse proxy
api.example.com {
reverse_proxy localhost:8080
}
# File server (should be skipped)
static.example.com {
file_server
root * /var/www
}
# Valid reverse proxy with WebSocket
ws.example.com {
reverse_proxy localhost:9000 {
header_up Upgrade websocket
}
}
# Redirect (should be warned)
redirect.example.com {
redir https://other.example.com{uri}
}
API Response:
{
"preview": {
"hosts": [
{
"domain_names": "redirect.example.com",
"forward_scheme": "",
"forward_port": 0,
"warnings": null
},
{
"domain_names": "static.example.com",
"forward_scheme": "",
"forward_port": 0,
"warnings": ["File server directives not supported"]
},
{
"domain_names": "api.example.com",
"forward_scheme": "https",
"forward_host": "localhost",
"forward_port": 8080,
"warnings": null
},
{
"domain_names": "ws.example.com",
"forward_scheme": "https",
"forward_host": "localhost",
"forward_port": 9000,
"warnings": null
}
]
}
}
What Happened:
- ✅ Backend parsed all 4 hosts
- ✅ Valid reverse proxies extracted correctly (api, ws)
- ✅ File server flagged with warning
- ⚠️ Redirect included with no warning (empty forward_host/port should trigger warning)
- ✅ UI displayed both valid hosts correctly
- ⚠️ Test found 0 warning indicators in UI (should be 1-2)
Analysis:
- Core parsing works correctly for mixed content
- Warnings array is present but not displayed (same root cause as Test 3)
- Redirect should probably be flagged as unsupported
Related to Test 3: Same frontend warning display issue
❌ Test 6: Multi-File Upload (FAILED)
Objective: Verify multi-file upload flow for import directives Result: ❌ FAIL - UI uses textareas for manual paste, not file upload
Expected Flow:
- Click "Multi-site Import" button
- Modal opens with file upload input
- Select multiple .caddy files
- Upload all files together
- Backend processes import directives
- Preview all hosts from all files
Actual Flow:
- Click "Multi-site Import" button ✅
- UI expands inline (not modal) ❌
- Shows textarea for "Site 1" ❌
- User must manually paste each file's content ❌
- Click "+ Add site" to add more textareas ❌
What Happened:
- Test expected:
<input type="file" multiple /> - UI provides:
<textarea>for manual paste - Modal selector didn't find anything
- Test timeout at
expect(modal).toBeVisible()
Frontend State:
- button "Parse and Review" [disabled]
- button "Multi-site Import" [active] # <-- Activated
- generic: # Multi-site UI (expanded inline)
- heading "Multi-site Import" [level=3]
- paragraph: "Add each site's Caddyfile content separately..."
- textbox "Site 1" [placeholder: "example.com { reverse_proxy ... }"]
- button "+ Add site"
- button "Cancel"
- button "Parse and Review"
Analysis: The current UI design requires users to:
- Open each .caddy file in a text editor
- Copy the content
- Paste into textarea
- Repeat for each file
This is tedious and error-prone for users migrating from Caddy with many import files.
Recommended Changes:
Option A: Add File Upload to Current UI
<div className="multi-site-section">
<h3>Multi-site Import</h3>
<p>Upload multiple Caddyfile files or paste content separately</p>
{/* File Upload */}
<div className="border-2 border-dashed p-4 mb-4">
<input
type="file"
multiple
accept=".caddy,.caddyfile"
onChange={handleFileUpload}
/>
<p className="text-sm text-gray-400">
Drop .caddy files here or click to browse
</p>
</div>
{/* OR divider */}
<div className="flex items-center my-4">
<div className="flex-1 border-t"></div>
<span className="px-2 text-gray-500">or paste manually</span>
<div className="flex-1 border-t"></div>
</div>
{/* Textarea UI (current) */}
{sites.map((site, i) => (
<textarea key={i} placeholder={`Site ${i+1}`} />
))}
</div>
Option B: Replace Textareas with File Upload
// Simplify to file-upload-only flow
<div className="multi-site-section">
<h3>Multi-site Import with Import Directives</h3>
<input
type="file"
multiple
accept=".caddy,.caddyfile"
onChange={handleFilesSelected}
className="hidden"
ref={fileInputRef}
/>
<button onClick={() => fileInputRef.current?.click()}>
📁 Select Caddyfiles
</button>
{/* Show selected files */}
<ul className="mt-4">
{selectedFiles.map(f => (
<li key={f.name}>
✅ {f.name} ({(f.size / 1024).toFixed(1)} KB)
</li>
))}
</ul>
<button onClick={handleUploadAll} disabled={selectedFiles.length === 0}>
Parse {selectedFiles.length} File(s)
</button>
</div>
Backend Requirements:
Endpoint: POST /api/v1/import/upload-multi
type MultiFileUploadRequest struct {
Files []struct {
Name string `json:"name"`
Content string `json:"content"`
} `json:"files"`
}
// Process import directives across files
// Merge into single Caddyfile, then adapt
Test Update:
// In test, change to:
const fileInput = page.locator('input[type="file"][multiple]');
await fileInput.setInputFiles([
{ name: 'Caddyfile', buffer: Buffer.from(mainCaddyfile) },
{ name: 'app.caddy', buffer: Buffer.from(siteCaddyfile) },
]);
Severity: 🟡 HIGH Impact: Multi-file import is unusable for users with many import files Estimated Effort: 8 hours (Frontend: 4h, Backend: 4h)
Priority Ranking
🔴 Critical (Fix Immediately)
-
Test 2 - Import Directives Silent Failure
- Risk: Data loss - users think import worked but it didn't
- Effort: 4 hours
- Dependencies: None
- Files:
backend/internal/import/service.go,frontend/src/pages/tasks/ImportCaddyfile.tsx
-
Test 3 - Warnings Not Displayed
- Risk: Users attempt to import unsupported configs, fail silently
- Effort: 3 hours
- Dependencies: None
- Files:
frontend/src/pages/tasks/ImportCaddyfile.tsx
🟡 High (Fix in Next Sprint)
- Test 6 - Multi-File Upload Missing
- Risk: Poor UX for users with multi-file Caddyfiles
- Effort: 8 hours
- Dependencies: Backend multi-file endpoint
- Files:
frontend/src/pages/tasks/ImportCaddyfile.tsx,backend/internal/import/handler.go
🟢 Low (Nice to Have)
- Test 4 - Better Error Messages
- Risk: Minimal - errors already shown
- Effort: 1 hour
- Files:
backend/internal/import/service.go
Implementation Order
Sprint 1: Critical Fixes (7 hours total)
Week 1:
-
Fix Import Directive Detection (4h)
- Backend: Add import directive scanner
- Backend: Return error with detected imports
- Frontend: Display import error with "Use Multi-site" CTA
- Test: Verify error shown, user can click CTA
-
Fix Warning Display (3h)
- Frontend: Add warning alert component
- Frontend: Render warnings for each host
- Frontend: Show critical error if all hosts have warnings
- Test: Verify warnings visible in UI
Sprint 2: Multi-File Support (8 hours)
Week 2:
-
Add File Upload UI (4h)
- Frontend: Add file input with drag-drop
- Frontend: Show selected files list
- Frontend: Read file contents via FileReader API
- Frontend: Call multi-file upload endpoint
-
Backend Multi-File Endpoint (4h)
- Backend: Create
/api/v1/import/upload-multiendpoint - Backend: Accept array of {name, content} files
- Backend: Write all files to temp directory
- Backend: Process
importdirectives by combining files - Backend: Return merged preview
- Backend: Create
Sprint 3: Polish (1 hour)
Week 3:
- Improve Error Messages (1h)
- Backend: Capture stderr from
caddy adapt - Backend: Parse line numbers from Caddy error output
- Frontend: Display line numbers in error message
- Backend: Capture stderr from
Production Readiness Assessment
Blocking Issues for Release
| Issue | Severity | Blocks Release? | Reason |
|---|---|---|---|
| Import directives silently ignored | 🔴 CRITICAL | YES | Data loss risk |
| Warnings not displayed | 🔴 CRITICAL | YES | Misleading UX |
| Multi-file upload missing | 🟡 HIGH | NO | Workaround exists (manual paste) |
| Error messages lack detail | 🟢 LOW | NO | Functional but not ideal |
Recommendation
DO NOT RELEASE until Tests 2 and 3 are fixed.
Rationale:
- Users will lose configuration data when import directives are ignored
- Users will attempt to import unsupported hosts and be confused by failures
- These are functional correctness issues, not just UX polish
Minimum Viable Fix:
- Add import directive detection (4h)
- Display warning messages (3h)
- Release with manual multi-file paste (document workaround)
Total Time to Release: 7 hours (1 developer day)
Action Items
For Backend Team:
- Implement import directive detection in
backend/internal/import/service.go - Return structured error when imports detected
- Add debug logging for import processing
- Capture stderr from
caddy adaptcommand
For Frontend Team:
- Add warning alert component to import preview
- Display per-host warnings in preview table
- Add "Use Multi-site Import" CTA to import error
- Design file upload UI for multi-site flow
For QA:
- Verify fixes with manual testing
- Add regression tests for import edge cases
- Document workaround for multi-file import (manual paste)
For Product:
- Decide: Block release or document known issues?
- Update user documentation with import limitations
- Consider adding "Import Guide" with screenshots
Report Generated By: Playwright E2E Test Suite v1.0 Test Execution: Complete (6/6 tests) Critical Issues Found: 3 Recommended Action: Fix critical issues before release