Files
Charon/docs/reports/caddy_import_full_test_results.md
GitHub Actions fc2df97fe1 feat: improve Caddy import with directive detection and warnings
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
2026-01-30 15:29:49 +00:00

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:

  1. Backend did NOT detect import directives (responseBody.imports was undefined)
  2. Backend successfully parsed the non-import host
  3. API returned 200 OK (should be 400 or include warning)
  4. UI showed no error message (test expected .bg-red-900 element)

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/*.caddy was 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:

  1. Backend correctly parsed both hosts
  2. Backend correctly added warning: "File server directives not supported"
  3. Backend correctly set empty forward_host/forward_port (indicating no proxy)
  4. Frontend did NOT display any warning/error message
  5. Test expected yellow/red warning banner (.bg-yellow-900 or .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:

  1. Backend correctly rejected invalid syntax (400 Bad Request)
  2. Error message mentions "caddy adapt failed"
  3. ⚠️ Error does NOT include line number (would be helpful)
  4. UI displayed error message correctly

Observations:

  • Error is functional but not ideal
  • Would benefit from capturing caddy adapt stderr 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:

  1. Backend parsed all 4 hosts
  2. Valid reverse proxies extracted correctly (api, ws)
  3. File server flagged with warning
  4. ⚠️ Redirect included with no warning (empty forward_host/port should trigger warning)
  5. UI displayed both valid hosts correctly
  6. ⚠️ 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:

  1. Click "Multi-site Import" button
  2. Modal opens with file upload input
  3. Select multiple .caddy files
  4. Upload all files together
  5. Backend processes import directives
  6. Preview all hosts from all files

Actual Flow:

  1. Click "Multi-site Import" button
  2. UI expands inline (not modal)
  3. Shows textarea for "Site 1"
  4. User must manually paste each file's content
  5. 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:

  1. Open each .caddy file in a text editor
  2. Copy the content
  3. Paste into textarea
  4. 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)

  1. 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
  2. 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)

  1. 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)

  1. 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:

  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
  2. 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:

  1. 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
  2. Backend Multi-File Endpoint (4h)

    • Backend: Create /api/v1/import/upload-multi endpoint
    • Backend: Accept array of {name, content} files
    • Backend: Write all files to temp directory
    • Backend: Process import directives by combining files
    • Backend: Return merged preview

Sprint 3: Polish (1 hour)

Week 3:

  1. 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

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:

  1. Add import directive detection (4h)
  2. Display warning messages (3h)
  3. 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 adapt command

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