Skip to content
This repository was archived by the owner on Oct 10, 2025. It is now read-only.

Commit db45c94

Browse files
committed
refactor: [#14] separate testing concerns across three-layer architecture
- Refactor infrastructure/tests/test-ci.sh to only test infrastructure concerns - Remove global 'make lint' calls from infrastructure layer - Create new application/tests/test-ci.sh for application-only testing - Create new tests/test-ci.sh for project-wide orchestration - Update Makefile with proper layer separation (infra-test-ci, app-test-ci, test-ci) - Update GitHub workflow to use 'make test-ci' for complete validation - Fix application tests to be CI-friendly (optional environment files) - Add comprehensive documentation in .github/copilot-instructions.md - Document three-layer testing architecture with clear boundaries - Add AI assistant guidelines to prevent future violations Testing Hierarchy: - make test-ci: Project-wide orchestrator (global + infra + app) - make infra-test-ci: Infrastructure layer only - make app-test-ci: Application layer only Prevents mixing concerns like calling global commands from layer-specific tests.
1 parent d3fe01e commit db45c94

File tree

9 files changed

+424
-47
lines changed

9 files changed

+424
-47
lines changed

.github/copilot-instructions.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,66 @@ The project includes a comprehensive linting script that validates all file type
348348
- **No secrets**: Never commit SSH keys, passwords, or tokens
349349
- **Documentation**: Update docs for any infrastructure changes
350350

351+
#### Three-Layer Testing Architecture
352+
353+
**CRITICAL**: This project uses a strict three-layer testing architecture.
354+
**Never mix concerns across layers**:
355+
356+
**1. Project-Wide/Global Layer** (`tests/` folder)
357+
358+
- **Command**: `make test-ci`
359+
- **Scope**: Cross-cutting concerns that span all layers
360+
- **Responsibilities**:
361+
- Global syntax validation (`make lint` / `./scripts/lint.sh`)
362+
- Project structure validation (`tests/test-unit-project.sh`)
363+
- Makefile validation
364+
- Orchestrates infrastructure and application layer tests
365+
366+
**2. Infrastructure Layer** (`infrastructure/` folder)
367+
368+
- **Command**: `make infra-test-ci`
369+
- **Scope**: Infrastructure-specific validation ONLY
370+
- **Responsibilities**:
371+
- Terraform/OpenTofu syntax validation
372+
- Cloud-init template validation
373+
- Infrastructure script validation
374+
- Infrastructure configuration validation
375+
- **NEVER**: Call global commands like `make lint` from infrastructure tests
376+
377+
**3. Application Layer** (`application/` folder)
378+
379+
- **Command**: `make app-test-ci`
380+
- **Scope**: Application-specific validation ONLY
381+
- **Responsibilities**:
382+
- Docker Compose syntax validation
383+
- Application configuration validation
384+
- Deployment script validation
385+
- Grafana dashboard validation
386+
- **NEVER**: Call global commands like `make lint` from application tests
387+
388+
**Testing Hierarchy:**
389+
390+
```text
391+
make test-ci (Project-wide orchestrator)
392+
├── Global concerns (syntax, structure, Makefile)
393+
├── make infra-test-ci (Infrastructure layer only)
394+
└── make app-test-ci (Application layer only)
395+
```
396+
397+
**Common Mistake to Avoid:**
398+
399+
- ❌ Adding `make lint` calls inside `infrastructure/tests/test-ci.sh`
400+
- ❌ Testing application concerns from infrastructure tests
401+
- ❌ Testing infrastructure concerns from application tests
402+
- ✅ Keep each layer focused on its own responsibilities only
403+
- ✅ Use `make test-ci` for complete validation that orchestrates all layers
404+
405+
**GitHub Actions Integration:**
406+
407+
- Uses `make test-ci` (not `make infra-test-ci`) to ensure all layers are tested
408+
- Runs without virtualization (CI-compatible)
409+
- Maintains separation of concerns for maintainable testing
410+
351411
#### End-to-End Smoke Testing
352412

353413
For verifying the functionality of the tracker from an end-user's perspective (e.g., simulating announce/scrape requests), refer to the **Smoke Testing Guide**. This guide explains how to use the official `torrust-tracker-client` tools to perform black-box testing against a running tracker instance without needing a full BitTorrent client.
@@ -466,6 +526,27 @@ When providing assistance:
466526
- Test infrastructure changes locally before suggesting them
467527
- Provide clear explanations and documentation
468528
- Consider the migration to Hetzner infrastructure in suggestions
529+
- **CRITICAL**: Respect the three-layer testing architecture (see Testing Requirements above)
530+
531+
#### Testing Layer Separation (CRITICAL)
532+
533+
**NEVER** mix testing concerns across the three layers:
534+
535+
- **Infrastructure tests** (`infrastructure/tests/`) should ONLY test infrastructure concerns
536+
- **Application tests** (`application/tests/`) should ONLY test application concerns
537+
- **Global tests** (`tests/`) handle cross-cutting concerns and orchestration
538+
539+
**Common violations to avoid:**
540+
541+
- ❌ Adding `make lint` calls in `infrastructure/tests/test-ci.sh`
542+
- ❌ Testing Docker Compose from infrastructure layer
543+
- ❌ Testing Terraform from application layer
544+
- ❌ Any layer calling commands from other layers directly
545+
546+
**Always use the proper orchestrator:**
547+
548+
- Use `make test-ci` for complete testing (orchestrates all layers)
549+
- Use layer-specific commands only when targeting that specific layer
469550

470551
#### Command Execution Context
471552

.github/workflows/testing.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,4 @@ jobs:
2727
sudo ./install-opentofu.sh --install-method deb
2828
2929
- name: Run CI test suite
30-
run: make infra-test-ci
30+
run: make test-ci

Makefile

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,10 @@ help: ## Show this help message
3535
@echo "🖥️ VM ACCESS:"
3636
@awk 'BEGIN {FS = ":.*?## "} /^vm-.*:.*?## / {printf " %-20s %s\n", $$1, $$2}' $(MAKEFILE_LIST)
3737
@echo ""
38-
@echo "🧪 GLOBAL TESTING:"
38+
@echo "🧪 TESTING (3-LAYER ARCHITECTURE):"
3939
@awk 'BEGIN {FS = ":.*?## "} /^test.*:.*?## / {printf " %-20s %s\n", $$1, $$2}' $(MAKEFILE_LIST)
40+
@awk 'BEGIN {FS = ":.*?## "} /^infra-test.*:.*?## / {printf " %-20s %s\n", $$1, $$2}' $(MAKEFILE_LIST)
41+
@awk 'BEGIN {FS = ":.*?## "} /^app-test.*:.*?## / {printf " %-20s %s\n", $$1, $$2}' $(MAKEFILE_LIST)
4042
@echo ""
4143
@echo "⚙️ SYSTEM SETUP:"
4244
@awk 'BEGIN {FS = ":.*?## "} /^(install-deps|clean).*:.*?## / {printf " %-20s %s\n", $$1, $$2}' $(MAKEFILE_LIST)
@@ -99,9 +101,10 @@ infra-test-prereq: ## Test system prerequisites for development
99101
@echo "Testing prerequisites..."
100102
$(INFRA_TESTS_DIR)/test-unit-infrastructure.sh vm-prereq
101103

102-
infra-test-ci: ## Run infrastructure CI-compatible tests
103-
@echo "Running infrastructure CI-compatible tests..."
104-
$(INFRA_TESTS_DIR)/test-ci.sh
104+
infra-test-ci: ## Run infrastructure-only CI tests (no global concerns)
105+
@echo "Running infrastructure-only CI tests..."
106+
$(INFRA_TESTS_DIR)/test-unit-config.sh
107+
$(INFRA_TESTS_DIR)/test-unit-scripts.sh
105108

106109
infra-test-local: ## Run local-only infrastructure tests (requires virtualization)
107110
@echo "Running local-only infrastructure tests..."
@@ -135,6 +138,10 @@ app-test-services: ## Test application services
135138
@echo "Testing application services..."
136139
$(TESTS_DIR)/test-unit-application.sh services
137140

141+
app-test-ci: ## Run application-only CI tests (no global concerns)
142+
@echo "Running application-only CI tests..."
143+
application/tests/test-ci.sh
144+
138145
# =============================================================================
139146
# VM ACCESS AND DEBUGGING
140147
# =============================================================================
@@ -212,6 +219,16 @@ test-e2e: ## Run comprehensive end-to-end test (follows integration guide)
212219
@echo "Running comprehensive end-to-end test..."
213220
$(TESTS_DIR)/test-e2e.sh $(ENVIRONMENT)
214221

222+
test-ci: ## Run project-wide CI tests (global concerns)
223+
@echo "Running project-wide CI tests..."
224+
@echo "1. Global concerns (syntax, structure, Makefile)..."
225+
tests/test-ci.sh
226+
@echo "2. Infrastructure layer tests..."
227+
@make infra-test-ci
228+
@echo "3. Application layer tests..."
229+
@make app-test-ci
230+
@echo "✅ All CI tests passed!"
231+
215232
test-unit: ## Run unit tests (configuration, scripts, syntax)
216233
@echo "Running unit tests..."
217234
@echo "1. Configuration and syntax validation..."
@@ -228,3 +245,4 @@ clean: ## Clean up temporary files and caches
228245
@rm -rf $(TERRAFORM_DIR)/.terraform
229246
@rm -f $(TERRAFORM_DIR)/terraform.tfstate.backup
230247
@echo "Clean completed"
248+

application/tests/test-ci.sh

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#!/bin/bash
2+
# Application-only CI tests - Run application-specific tests that work in GitHub runners
3+
# Focus: Docker Compose validation, application configuration, deployment scripts
4+
# Scope: No virtualization, no global repo concerns, application layer only
5+
6+
set -euo pipefail
7+
8+
# Configuration
9+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
10+
APPLICATION_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
11+
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
12+
TEST_LOG_FILE="/tmp/torrust-application-ci-test.log"
13+
14+
# Source shared shell utilities
15+
# shellcheck source=../../scripts/shell-utils.sh
16+
source "${PROJECT_ROOT}/scripts/shell-utils.sh"
17+
18+
# Set log file for tee output
19+
export SHELL_UTILS_LOG_FILE="${TEST_LOG_FILE}"
20+
21+
# Initialize test log
22+
init_test_log() {
23+
init_log_file "${TEST_LOG_FILE}" "Application-only CI Tests"
24+
log_info "Environment: CI (no deployment)"
25+
log_info "Scope: Application layer only"
26+
log_info "Application Root: ${APPLICATION_ROOT}"
27+
}
28+
29+
# Test execution summary
30+
show_test_summary() {
31+
local start_time=$1
32+
local end_time
33+
local duration
34+
end_time=$(date +%s)
35+
duration=$((end_time - start_time))
36+
37+
log_section "APPLICATION CI TEST SUMMARY"
38+
log_info "Application CI tests completed in ${duration} seconds"
39+
log_success "All application-specific tests passed!"
40+
log ""
41+
log_info "Note: This only validates application layer concerns."
42+
log_info "Run 'make test-ci' for complete project validation."
43+
log ""
44+
log_info "Test log saved to: ${TEST_LOG_FILE}"
45+
}
46+
47+
# Main test execution
48+
main() {
49+
local test_start_time
50+
test_start_time=$(date +%s)
51+
52+
init_test_log
53+
54+
log_section "APPLICATION-ONLY CI TESTS"
55+
log_info "Running application-specific tests for GitHub runners"
56+
57+
cd "${PROJECT_ROOT}"
58+
59+
# Test 1: Application unit tests
60+
log_section "TEST 1: APPLICATION CONFIGURATION & CONTAINERS"
61+
log_info "Running application unit tests..."
62+
if ! "${APPLICATION_ROOT}/tests/test-unit-application.sh"; then
63+
log_error "Application unit tests failed"
64+
exit 1
65+
fi
66+
log_success "Application unit tests passed"
67+
68+
show_test_summary "${test_start_time}"
69+
}
70+
71+
# Run main function
72+
main "$@"

application/tests/test-unit-application.sh

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,19 @@ test_application_config() {
5959

6060
local failed=0
6161
local config_files=(
62-
".env.production"
6362
"compose.yaml"
6463
)
6564

65+
# Optional files (for development/local testing)
66+
local optional_files=(
67+
".env"
68+
".env.production"
69+
".env.local"
70+
)
71+
6672
cd "${APPLICATION_ROOT}"
6773

74+
# Check required configuration files
6875
for config_file in "${config_files[@]}"; do
6976
if [[ ! -f "${config_file}" ]]; then
7077
log_error "Required configuration file missing: ${config_file}"
@@ -74,6 +81,19 @@ test_application_config() {
7481
fi
7582
done
7683

84+
# Check optional configuration files (info only, no failure)
85+
local env_file_found=false
86+
for config_file in "${optional_files[@]}"; do
87+
if [[ -f "${config_file}" ]]; then
88+
log_info "Found optional configuration file: ${config_file}"
89+
env_file_found=true
90+
fi
91+
done
92+
93+
if [[ "$env_file_found" = false ]]; then
94+
log_info "No environment files found (normal for CI, generated during deployment)"
95+
fi
96+
7797
# Test that configuration templates exist
7898
local template_dir="${APPLICATION_ROOT}/config/templates"
7999
if [[ -d "${template_dir}" ]]; then
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# GitHub Copilot Instructions Update Summary
2+
3+
## Changes Made
4+
5+
Updated `.github/copilot-instructions.md` to include comprehensive guidance on the three-layer
6+
testing architecture to prevent future violations of separation of concerns.
7+
8+
### 1. New Section: "Three-Layer Testing Architecture"
9+
10+
Added a detailed section under "Testing Requirements" that explains:
11+
12+
- **Critical importance** of maintaining layer separation
13+
- **Definition of each layer**:
14+
- Project-Wide/Global Layer (`tests/` folder)
15+
- Infrastructure Layer (`infrastructure/` folder)
16+
- Application Layer (`application/` folder)
17+
- **Specific responsibilities** for each layer
18+
- **Clear hierarchy** showing orchestration relationships
19+
- **Common mistakes to avoid** with explicit examples
20+
- **GitHub Actions integration** guidance
21+
22+
### 2. Enhanced AI Assistant Guidelines
23+
24+
Added a specific "Testing Layer Separation (CRITICAL)" section in the AI assistants guidance that:
25+
26+
- **Reinforces the architecture** with strong warnings
27+
- **Lists common violations** that should never happen
28+
- **Provides clear guidance** on proper command usage
29+
- **Emphasizes orchestration** through `make test-ci`
30+
31+
## Key Benefits
32+
33+
1. **Prevents regression** - Future contributors and AI assistants will understand the architecture
34+
2. **Clear guidance** - Explicit examples of what NOT to do
35+
3. **Proper orchestration** - Emphasizes using `make test-ci` for complete testing
36+
4. **Maintainable architecture** - Each layer stays focused on its responsibilities
37+
38+
## Impact
39+
40+
This documentation update ensures that the three-layer testing architecture violation we just
41+
fixed will not happen again. Contributors and AI assistants now have clear, explicit guidance
42+
on maintaining proper separation of concerns in the testing infrastructure.
43+
44+
The documentation is strategically placed in both the general testing requirements and the AI
45+
assistant-specific guidelines to ensure maximum visibility and adherence to the architecture.

0 commit comments

Comments
 (0)