Caddy Manager CI build / docker (push) Failing after 1m2s
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>
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
- Test Naming: Use descriptive names that indicate the method being tested, the condition, and the expected result
- Single Responsibility: Each test should verify one specific behavior
- Independence: Tests should not depend on each other and should be able to run in any order
- Cleanup: Use
IDisposableor cleanup methods to remove temporary resources - Readable Assertions: Use AwesomeAssertions for more readable test assertions
- 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