Testing Strategy
Relevant source files
The following files were used as context for generating this wiki page:
- internal/dokku-api/ssh_integration_test.go
- internal/server-plugins/app/domain/application-validation.service_test.go
- internal/dokku-api/client_blacklist_test.go
- scripts/setup-dokku-local.sh
- CONTRIBUTING.md
- README.md
- internal/dokku-api/ssh_auth_test.go
- PHASE1_IMPLEMENTATION.md
- scripts/cleanup-test-apps.sh
The project employs a multi-layered testing strategy to ensure code quality, correctness, and reliability. This strategy combines unit tests for isolated logic validation with integration tests that verify interactions with a live Dokku instance. The entire process is heavily automated using make, shell scripts, and Git hooks to provide a consistent and efficient development workflow.
The testing framework of choice is Ginkgo with the Gomega matcher library, which enables a Behavior-Driven Development (BDD) style. This approach results in descriptive and readable test suites that clearly articulate the behavior being tested.
Sources: CONTRIBUTING.md:23-25, README.md:173-176, internal/dokku-api/ssh_integration_test.go:10-11
Frameworks and Tooling
The project standardizes on a set of tools to maintain a consistent testing environment.
| Tool / Framework | Purpose |
|---|---|
| Go Test | The underlying engine for executing all tests. |
| Ginkgo/Gomega | A BDD framework for writing expressive and structured tests. Test suites are defined with Describe and Context blocks. |
| Makefile | Provides a centralized set of commands (make test, make check) to simplify running tests and quality checks. |
| Docker/Docker Compose | Used to create and manage a local Dokku instance for integration testing, ensuring a clean and reproducible environment. |
| Git Hooks | Pre-commit hooks automatically run formatters and static analysis to catch issues before code is committed. |
Sources: internal/dokku-api/ssh_integration_test.go:10-11, README.md:183-186, scripts/setup-dokku-local.sh:10-15, CONTRIBUTING.md:43-46
Test Types
The testing strategy is divided into two primary categories: unit tests and integration tests.
Unit Testing
Unit tests are designed to be fast and isolated. They validate individual components like domain services, value objects, and client logic without external dependencies. These tests are tagged to run by default with make test.
Key characteristics of unit tests in this repository include:
- Isolation: They do not require a running Dokku instance or network access.
- BDD Structure: Organized using Ginkgo's
Describe,Context, andItblocks. - Parameterized Tests:
DescribeTableis frequently used to test a function against a wide range of inputs and expected outcomes, making tests concise and comprehensive. - Focus Areas:
- Domain Logic: Validating business rules, such as application name constraints or scaling parameters.
- Value Objects: Ensuring that value objects correctly enforce their invariants (e.g., a negative scale value is rejected).
- Security Validations: Testing security features like command blacklisting and detection of dangerous characters in input.
The following snippet shows a DescribeTable used for validating application names.
// Source: internal/server-plugins/app/domain/application-validation.service_test.go:23-45
DescribeTable("application name validation",
func(appName string, expectValid bool, expectErrorCount int, expectWarningCount int, errorCodes []string, warningCodes []string) {
result := service.ValidateApplicationName(ctx, appName)
Expect(result.IsValid).To(Equal(expectValid))
// ... more assertions
},
Entry("valid simple name", "myapp", true, 0, 0, []string{}, []string{}),
Entry("valid name with hyphen", "my-app", true, 0, 0, []string{}, []string{}),
Entry("empty name - VO validation should catch this", "", false, 1, 0, []string{"INVALID_APPLICATION_NAME"}, []string{}),
Entry("reserved name - should pass VO validation but add warning", "dokku", false, 1, 0, []string{"INVALID_APPLICATION_NAME"}, []string{}),
)
Sources: internal/server-plugins/app/domain/application-validation.service_test.go, internal/dokku-api/client_blacklist_test.go:27-44, internal/shared/process/process_scale.vo_test.go:13-31, PHASE1_IMPLEMENTATION.md:73-86
Integration Testing
Integration tests verify the interaction between the dokku-mcp server and a live Dokku instance. They are crucial for ensuring that the SSH commands sent to Dokku behave as expected.
- Build Tag: Integration tests are separated from unit tests using the
//go:build integrationbuild tag. This prevents them from running during a standardmake testexecution. - Execution: They are run explicitly with
make test-integration-local. - Environment: These tests rely on a local Dokku instance managed via Docker, which is set up using the
make setup-dokkucommand. - Focus Areas:
- SSH Connection: Validating the construction of SSH commands and arguments.
- Authentication: Testing different SSH authentication methods, including
ssh-agentand private key files. - Command Execution: Ensuring that commands sent to Dokku execute and produce the expected output or side effects.
// Source: internal/dokku-api/ssh_integration_test.go:1-3
//go:build integration
Sources: internal/dokku-api/ssh_integration_test.go:1-3, 100-111, README.md:178-181, internal/dokku-api/ssh_auth_test.go:1-3
Test Environment
A consistent and reproducible test environment is critical for reliable integration testing. The project uses Docker and a collection of scripts to manage this.
Local Dokku Instance
A local Dokku server can be provisioned using Docker Compose. This is managed by a Makefile target.
make setup-dokku: Downloads the necessary Docker images and configures the local Dokku instance. This only needs to be run once.make dokku-start: Starts the local Dokku container.make dokku-stop: Stops the local Dokku container.
Sources: README.md:135-149
Automated Setup and Teardown
Scripts are provided to automate the configuration and cleanup of the test environment, ensuring a clean state for each test run.
scripts/setup-dokku-local.sh: This script automates the entire setup process for local integration testing.scripts/cleanup-test-apps.sh: This script removes any test applications and SSH keys created during testing, both on the local Dokku instance and in the local user's SSH configuration.
The diagram below illustrates the workflow of the setup-dokku-local.sh script.
Test Execution and Workflow
The development workflow is streamlined through Makefile commands and automated pre-commit checks.
Makefile Commands
The Makefile is the primary entry point for running tests and quality checks.
| Command | Description |
|---|---|
make test | Runs all unit tests (those without the integration build tag) and generates an HTML coverage report at coverage.html. |
make test-integration-local | Runs the integration tests against a running local Dokku container. |
make check | A comprehensive quality check that runs linting, formatting, static analysis, and complexity checks. |
make setup-dev | Installs development tools and sets up Git hooks for automated pre-commit checks. |
Sources: README.md:183-186, CONTRIBUTING.md:25, CONTRIBUTING.md:33-35
Pre-commit Hooks
To enforce code quality automatically, the project uses Git hooks configured via make setup-dev. The pre-commit hook runs on every commit to perform several actions.
This diagram shows the steps executed by the pre-commit hook.
Test Suite Structure
Tests are organized into suites using Ginkgo. Each suite corresponds to a specific package or feature.
- Suite Definition: A test suite is initialized in a
_suite_test.gofile, which contains theRunSpecscall. - Structure:
Describeblocks group tests for a specific function or component.Contextblocks further refine the conditions for a set of tests.Itblocks contain the actual test logic and assertions. - Setup/Teardown:
BeforeEachandAfterEachblocks are used to set up preconditions and perform cleanup for each test, ensuring test isolation.
The following example shows the typical structure of an integration test suite.
// Source: internal/dokku-api/ssh_integration_test.go:13-21
var _ = Describe("SSH Integration", func() {
var (
logger *slog.Logger
)
BeforeEach(func() {
logger = slog.New(slog.NewTextHandler(GinkgoWriter, &slog.HandlerOptions{
Level: slog.LevelDebug,
}))
})
// ... Describe and Context blocks follow
})
Sources: internal/dokku-api/ssh_integration_test.go:13-21, PHASE1_IMPLEMENTATION.md:73-86, internal/dokku-api/unit_suite_test.go
Conclusion
The testing strategy for dokku-mcp is comprehensive, covering code at multiple levels from isolated unit validation to full-stack integration. By leveraging the Go testing toolchain, Ginkgo/Gomega, Docker, and robust automation via make and Git hooks, the project ensures a high standard of quality and developer productivity. This structured approach allows for confident refactoring and feature development by providing a strong safety net against regressions.