Files
CaddyManager/CaddyManager.Tests
eboloandClaude Opus 5 c484014752
Caddy Manager CI build / docker (push) Failing after 1m2s
Harden the read path, forwarded headers and security docs
Found while investigating an unrelated Gitea compromise: CaddyManager itself
was not involved, but reviewing it turned up three things worth closing.

Reading a configuration was the only file operation that did not validate the
name. Saving, renaming and deleting all reject `..`, `/` and `\`, so the read
path was the one way to leave the configuration directory and pull in any
`*.caddy` file on the host. The HTTP API happened to be covered, because GET
checks the name against the directory listing first, but the UI calls the
service directly and nothing stopped it.

Forwarded headers were trusted from any peer. That is correct only while the
container port is unreachable except through the proxy; the moment it is
published, a caller dictates the scheme, host and client address the app
believes in. Loopback and private space cover a proxy on a Docker network or on
the host, which is the documented deployment, and ignore everyone else.

The README never said that the `X-Api-Key` check guards `/api/*` and nothing
else, so the UI - which rewrites Caddyfiles and holds the Docker socket - reads
as protected when it is not. It now says so, and warns about the specific shape
that bit us: a second hostname added for machine callers whose only extra
directive is a `tls` line, which serves the unauthenticated UI to anyone who
can resolve it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-12 17:05:52 +07:00
..
2026-07-26 15:55:11 +07:00

CaddyManager Tests

This project contains comprehensive unit tests for the CaddyManager application.

Test Structure

The tests are organized to mirror the main project structure:

Services Tests

  • CaddyConfigurationParsingService: Tests for parsing Caddyfile content and extracting hostnames, reverse proxy targets, and ports
  • CaddyService: Tests for managing Caddy configuration files (CRUD operations)
  • ConfigurationsService: Tests for configuration management and dependency injection
  • DockerService: Tests for Docker container management functionality

Models Tests

  • CaddyConfigurationInfo: Tests for configuration information model
  • CaddyOperationResponse: Tests for operation response model
  • CaddyDeleteOperationResponse: Tests for delete operation response model
  • CaddySaveConfigurationRequest: Tests for save configuration request model

Configuration Tests

  • CaddyServiceConfigurations: Tests for Caddy service configuration class
  • DockerServiceConfiguration: Tests for Docker service configuration class

Test Utilities

  • TestHelper: Common utilities for creating test data, temporary files, and configurations

Running Tests

Run all tests

dotnet test

Run tests with coverage

dotnet test --collect:"XPlat Code Coverage" --settings coverlet.runsettings

Run specific test class

dotnet test --filter "ClassName=CaddyServiceTests"

Run tests with verbose output

dotnet test --verbosity normal

Test Coverage

The project is configured to generate code coverage reports in multiple formats:

  • OpenCover
  • Cobertura
  • JSON
  • LCOV
  • TeamCity
  • HTML

Coverage reports exclude:

  • Test projects ([*.Tests]*)
  • Program entry points ([*]*.Program)
  • Migration files ([*]*Migrations*)
  • Generated code (marked with appropriate attributes)

Test Frameworks and Libraries

  • xUnit: Primary testing framework
  • AwesomeAssertions: For readable assertions
  • Moq: For mocking dependencies
  • Coverlet: For code coverage analysis

Test Patterns

The tests follow these patterns:

Arrange-Act-Assert (AAA)

All tests use the AAA pattern for clarity:

[Fact]
public void Method_Condition_ExpectedResult()
{
    // Arrange
    var input = "test input";
    
    // Act
    var result = service.Method(input);
    
    // Assert
    result.Should().Be("expected output");
}

Theory Tests

For testing multiple scenarios:

[Theory]
[InlineData("input1", "output1")]
[InlineData("input2", "output2")]
public void Method_WithVariousInputs_ReturnsExpectedOutputs(string input, string expected)
{
    // Test implementation
}

Mocking

Dependencies are mocked using Moq:

var mockService = new Mock<IService>();
mockService.Setup(x => x.Method(It.IsAny<string>())).Returns("mocked result");

Test Data

Common test data is provided through the TestHelper class:

  • Sample Caddyfile configurations
  • Temporary file and directory creation
  • Configuration builders

Best Practices

  1. Test Naming: Use descriptive names that indicate the method being tested, the condition, and the expected result
  2. Single Responsibility: Each test should verify one specific behavior
  3. Independence: Tests should not depend on each other and should be able to run in any order
  4. Cleanup: Use IDisposable or cleanup methods to remove temporary resources
  5. Readable Assertions: Use AwesomeAssertions for more readable test assertions
  6. Mock Verification: Verify that mocked methods are called as expected when relevant

Continuous Integration

These tests are designed to run in CI/CD environments and include:

  • Fast execution times
  • No external dependencies (except for Docker integration tests which are skipped)
  • Comprehensive coverage of business logic
  • Clear failure messages for debugging