Version: 1.0
Date: 19 February 2026
Status: Design Phase
This document outlines a comprehensive end-to-end (E2E) testing framework for Mau, a P2P file synchronization system built on Kademlia DHT. The framework provides two complementary modes:
mau-e2e tool) - Manual control and exploratory testinggo test) - CI/CD integration and regression detectionBoth modes share the same core testenv library, ensuring consistency between manual exploration and automated validation.
Key Design Principles:
Primary Goals:
See Also: CLI_DESIGN.md for detailed interactive CLI specifications
What it does: Interoperability testing for libp2p implementations
Approach:
Key Learnings:
Applicable to Mau:
What it does: End-to-end test harness for Ethereum clients
Approach:
Key Learnings:
Applicable to Mau:
What it does: Programmatic container lifecycle management for tests
Approach:
deferKey Learnings:
Applicable to Mau:
GenericContainer with custom Mau imageWhat it does: Network condition simulation proxy
Approach:
Supported Toxics:
latency: Add delay ± jitterbandwidth: Rate limiting (KB/s)timeout: Drop data, optionally close connectionslow_close: Delay TCP FINreset_peer: TCP RST simulationslicer: Fragment packetslimit_data: Close after N bytesKey Learnings:
Applicable to Mau:
Source: principlesofchaos.org, Netflix Simian Army
Core Principles:
Advanced Principles:
Key Learnings:
Applicable to Mau:
| Component | Options Considered | Selected | Rationale |
|---|---|---|---|
| Container Orchestration | Docker Compose, Testcontainers-Go, Kubernetes | Testcontainers-Go | Native Go integration, programmatic control, automatic cleanup |
| Mau Instance Packaging | Binary in container, Docker image | Docker image | Consistent environment, easy version management |
| Network Simulation | Toxiproxy, tc (Linux), Pumba, Comcast | Toxiproxy | Cross-platform, programmable, well-documented |
| Test Framework | Go testing, Ginkgo/Gomega, Testify | Go testing + Testify | Minimal dependencies, familiar to Mau contributors |
| Logging | Container logs, Loki, ELK | Structured JSON logs + file export | Simple, parseable, CI-friendly |
| Tracing | OpenTelemetry, Jaeger, custom | Custom trace IDs in logs | Lightweight, no external dependencies |
| Metrics | Prometheus, InfluxDB, none | Test execution metrics in JSON | Easy CI integration, no infra overhead |
| Assertion Library | Standard Go, Testify, Gomega | Testify | Rich assertions, already used in Mau codebase |
┌────────────────────────────────────────────────────────────────┐
│ Mau E2E Framework │
│ │
│ ┌──────────────────────┐ ┌──────────────────────┐ │
│ │ Interactive Mode │ │ Automated Mode │ │
│ │ (mau-e2e CLI) │ │ (go test) │ │
│ │ │ │ │ │
│ │ - Manual control │ │ - CI/CD testing │ │
│ │ - Exploration │ │ - Regression detect │ │
│ │ - Debugging │ │ - Assertions │ │
│ │ - Demonstrations │ │ - Coverage tracking │ │
│ └──────────┬───────────┘ └──────────┬───────────┘ │
│ │ │ │
│ └────────┬──────────────────┘ │
│ ▼ │
│ ┌──────────────────────┐ │
│ │ Shared Core │ │
│ │ (testenv library) │ │
│ │ │ │
│ │ - Peer management │ │
│ │ - Network control │ │
│ │ - State persistence │ │
│ │ - Assertions │ │
│ └──────────┬───────────┘ │
└──────────────────────┼───────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Docker Network (mau-test-net) │
│ │
│ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │
│ │ Mau Peer 1 │ │ Mau Peer 2 │ │ Mau Peer 3 │ │
│ │ Container │ │ Container │ │ Container │ │
│ │ │ │ │ │ │ │
│ │ - Account │ │ - Account │ │ - Account │ │
│ │ - Server │ │ - Server │ │ - Server │ │
│ │ - DHT Node │ │ - DHT Node │ │ - DHT Node │ │
│ │ - Files │ │ - Files │ │ - Files │ │
│ └───────┬───────┘ └───────┬───────┘ └───────┬───────┘ │
│ │ │ │ │
│ └───────────────────┼───────────────────┘ │
│ │ │
│ ┌───────────────────────────┴────────────────────────────┐ │
│ │ Toxiproxy (Optional) │ │
│ │ - Latency injection │ │
│ │ - Bandwidth limiting │ │
│ │ - Network partitions │ │
│ └────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Bootstrap Node (Optional) │ │
│ │ - Kademlia DHT seed │ │
│ └────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Observability Layer │
│ │
│ - Container logs (JSON structured) │
│ - Test result artifacts (JSON) │
│ - File state snapshots (for debugging) │
│ - Network traffic traces (optional) │
└─────────────────────────────────────────────────────────────────┘
go test with Testcontainers-Gogolang:1.21-alpine (multi-stage build)Container Lifecycle:
Example Proxy Configuration:
1{
2 "name": "mau_peer_1",
3 "listen": "0.0.0.0:8080",
4 "upstream": "mau-peer-1:8080",
5 "enabled": true
6}
| Component | Technology | Version | Purpose |
|---|---|---|---|
| Container Runtime | Docker | 20.10+ | Peer isolation |
| Orchestration | Testcontainers-Go | v0.27+ | Programmatic container management |
| Test Framework | Go testing | 1.21+ | Test execution |
| Assertions | Testify | v1.8+ | Rich assertions (already in Mau) |
| Network Proxy | Toxiproxy | 2.5+ | Network condition simulation |
| Logging | Zerolog / Zap | Latest | Structured JSON logging |
| Tool | Purpose | When Used |
|---|---|---|
| Docker Compose | Manual test environment setup | Local development/debugging |
| jq | Log parsing/filtering | Debugging failed tests |
| Make | Build automation | CI/CD pipeline |
| GitHub Actions | CI/CD runner | Automated testing |
Objective: Verify two Mau peers can discover each other via Kademlia DHT
Setup:
Steps:
Assertions:
Objective: Verify friend relationship establishment and file synchronization
Setup:
Steps:
hello.txt encrypted for Peer Bhello.txtAssertions:
Objective: Verify synchronization across 5 peers in a friend graph
Friend Graph:
A
/|\
B C D
\ /
E
Setup:
Steps:
Assertions:
Objective: Test behavior when two peers edit same file concurrently
Setup:
shared.txt version 1Steps:
shared.txt → version 2ashared.txt → version 2bAssertions:
.versions/ directory)Objective: Verify resilience when peer crashes mid-synchronization
Setup:
Steps:
Assertions:
Objective: Test synchronization after network partition heals
Friend Graph:
Partition 1: A - B
Partition 2: C - D
Setup:
Steps:
Assertions:
Objective: Verify synchronization under high latency
Setup:
Steps:
Assertions:
Objective: Test large file sync under bandwidth constraints
Setup:
Steps:
Assertions:
Objective: Verify TCP retransmission handles packet loss
Setup:
slicer toxic with 10% loss simulationSteps:
Assertions:
Objective: Test scalability with 10 peers all friends with each other
Setup:
Steps:
Assertions:
Objective: Validate DHT performance with 100 peers
Friend Graph: Random graph, average degree = 5
Setup:
Steps:
Assertions:
Objective: Test DHT stability under peer churn
Setup:
Steps:
Assertions:
Objective: Verify encrypted files not accessible without decryption key
Setup:
Steps:
Assertions:
Objective: Test DHT behavior under Sybil attack (future work)
Setup:
Steps:
Assertions:
mau/
├── e2e/ # E2E test framework root
│ ├── PLAN.md # This document
│ ├── README.md # Quick start guide
│ ├── Makefile # Build and test automation
│ │
│ ├── framework/ # Core test framework
│ │ ├── testenv/ # Test environment setup
│ │ │ ├── testenv.go # Testcontainers orchestration
│ │ │ ├── peer.go # Mau peer container wrapper
│ │ │ ├── network.go # Docker network management
│ │ │ ├── toxiproxy.go # Toxiproxy integration
│ │ │ └── bootstrap.go # Bootstrap node management
│ │ │
│ │ ├── assertions/ # Custom assertions
│ │ │ ├── sync.go # File sync assertions
│ │ │ ├── dht.go # Kademlia DHT assertions
│ │ │ └── friend.go # Friend relationship assertions
│ │ │
│ │ ├── helpers/ # Utility functions
│ │ │ ├── pgp.go # PGP key generation/management
│ │ │ ├── files.go # File creation/comparison
│ │ │ ├── logs.go # Log collection/parsing
│ │ │ └── wait.go # Wait strategies
│ │ │
│ │ └── types/ # Shared types
│ │ ├── config.go # Test configuration
│ │ ├── peer.go # Peer metadata
│ │ └── result.go # Test result structures
│ │
│ ├── scenarios/ # Test scenarios
│ │ ├── basic/ # Level 1 tests
│ │ │ ├── discovery_test.go # TC-001: Two-peer discovery
│ │ │ ├── friend_sync_test.go # TC-002: Two-peer friend sync
│ │ │ ├── multi_peer_test.go # TC-003: Multi-peer sync
│ │ │ └── version_conflict_test.go # TC-004: Version conflicts
│ │ │
│ │ ├── resilience/ # Level 2 tests
│ │ │ ├── peer_crash_test.go # TC-101: Peer crash
│ │ │ ├── partition_test.go # TC-102: Network partition
│ │ │ ├── latency_test.go # TC-103: High latency
│ │ │ ├── bandwidth_test.go # TC-104: Bandwidth limits
│ │ │ └── packet_loss_test.go # TC-105: Packet loss
│ │ │
│ │ ├── stress/ # Level 3 tests
│ │ │ ├── full_mesh_test.go # TC-201: 10-peer mesh
│ │ │ ├── large_network_test.go # TC-202: 100-peer network
│ │ │ └── churn_test.go # TC-203: Peer churn
│ │ │
│ │ └── security/ # Level 4 tests
│ │ ├── unauthorized_access_test.go # TC-301
│ │ └── sybil_attack_test.go # TC-302 (future)
│ │
│ ├── docker/ # Docker configurations
│ │ ├── Dockerfile.mau # Mau peer image
│ │ ├── Dockerfile.bootstrap # Bootstrap node image
│ │ ├── docker-compose.yml # Manual test environment
│ │ └── entrypoint.sh # Container entrypoint script
│ │
│ ├── configs/ # Test configurations
│ │ ├── default.json # Default test config
│ │ ├── ci.json # CI-optimized config
│ │ └── stress.json # Stress test config
│ │
│ ├── scripts/ # Utility scripts
│ │ ├── build-images.sh # Build Docker images
│ │ ├── run-tests.sh # Run test suite
│ │ ├── parse-logs.sh # Extract logs from failed tests
│ │ └── generate-report.sh # Generate HTML test report
│ │
│ └── docs/ # Documentation
│ ├── writing-tests.md # Guide for adding new tests
│ ├── debugging.md # Debugging failed tests
│ ├── architecture.md # Framework architecture
│ └── toxiproxy-guide.md # Toxiproxy usage guide
│
├── go.mod # Add e2e dependencies
└── .github/
└── workflows/
└── e2e-tests.yml # GitHub Actions workflow
Goal: Basic framework with simple 2-peer tests + interactive CLI foundation
Deliverables:
e2e/docker/Dockerfile.mau)e2e/framework/testenv/)e2e/cmd/mau-e2e/)mau-e2e up/down commandsmau-e2e peer add/list commands~/.mau-e2e/)Success Criteria:
mau-e2e up --peers 2mau-e2e peer listmake test-e2etestenv library used by both CLI and testsKey Files:
e2e/docker/Dockerfile.mau
e2e/framework/testenv/testenv.go
e2e/framework/testenv/peer.go
e2e/scenarios/basic/discovery_test.go
e2e/scenarios/basic/friend_sync_test.go
e2e/Makefile
.github/workflows/e2e-tests.yml
Example testenv.go structure:
1package testenv
2
3import (
4 "context"
5 "testing"
6 "github.com/testcontainers/testcontainers-go"
7)
8
9type TestEnv struct {
10 ctx context.Context
11 network testcontainers.Network
12 peers []*MauPeer
13 t *testing.T
14}
15
16func NewTestEnv(t *testing.T) *TestEnv {
17 // Create isolated Docker network
18 // Return TestEnv instance
19}
20
21func (e *TestEnv) AddPeer(name string) (*MauPeer, error) {
22 // Create and start Mau peer container
23 // Wait for readiness
24 // Return MauPeer wrapper
25}
26
27func (e *TestEnv) Cleanup() {
28 // Stop all containers
29 // Remove network
30 // Collect logs
31}
Goal: Expand to multi-peer scenarios + file/friend CLI commands
Deliverables:
e2e/framework/assertions/)
AssertFilesSynced(peers []*MauPeer, filename string, timeout time.Duration)AssertDHTLookup(peer *MauPeer, targetFingerprint string, timeout time.Duration)AssertFriendRelationship(peer1, peer2 *MauPeer)mau-e2e friend add/list commandsmau-e2e file add/list/cat commandsmau-e2e peer inspect commanddocs/writing-tests.mdSuccess Criteria:
Goal: Introduce Toxiproxy and real-time observability
Deliverables:
e2e/framework/testenv/toxiproxy.go)mau-e2e file watch command (real-time sync events)mau-e2e status --watch command (live dashboard)mau-e2e net partition/heal commandsmau-e2e net latency/limit commandsdocs/toxiproxy-guide.mdSuccess Criteria:
Example Toxiproxy usage:
1func TestNetworkPartition(t *testing.T) {
2 env := testenv.NewTestEnv(t)
3 defer env.Cleanup()
4
5 // Create 4 peers
6 peers := env.AddPeers(4)
7
8 // Establish friend relationships
9 env.MakeFriends(peers[0], peers[1])
10 env.MakeFriends(peers[2], peers[3])
11
12 // Create network partition: {0,1} vs {2,3}
13 partition := env.CreatePartition([]int{0, 1}, []int{2, 3})
14
15 // Publish files on both sides
16 env.AddFile(peers[0], "fileA.txt", "content A")
17 env.AddFile(peers[2], "fileB.txt", "content B")
18
19 time.Sleep(5 * time.Second)
20
21 // Assert files don't cross partition
22 assert.NoFile(t, peers[2], "fileA.txt")
23 assert.NoFile(t, peers[0], "fileB.txt")
24
25 // Heal partition
26 partition.Heal()
27
28 // Assert files eventually sync
29 assertions.AssertFilesSynced(t, peers, "fileA.txt", 60*time.Second)
30 assertions.AssertFilesSynced(t, peers, "fileB.txt", 60*time.Second)
31}
Goal: Validate scalability and performance
Deliverables:
Success Criteria:
Resource Considerations:
Goal: Complete interactive feature set
Deliverables:
mau-e2e shell)mau-e2e scenario <name>)mau-e2e snapshot/restore)dht lookup/table)scripts/parse-logs.sh)scripts/generate-report.sh)docs/debugging.mdSuccess Criteria:
Example Log Format:
1{
2 "timestamp": "2026-02-19T14:30:00Z",
3 "level": "info",
4 "peer": "peer-1",
5 "fingerprint": "ABAF11C65A2970B130ABE3C479BE3E4300411886",
6 "trace_id": "test-tc002-abc123",
7 "component": "sync",
8 "event": "file_download_started",
9 "file": "hello.txt",
10 "source_peer": "peer-2",
11 "source_fingerprint": "BBAF11C65A2970B130ABE3C479BE3E4300411887"
12}
Goal: Production-ready framework with excellent docs
Deliverables:
Success Criteria:
GitHub Actions Workflow Structure:
1name: E2E Tests
2
3on:
4 pull_request:
5 push:
6 branches: [main]
7 schedule:
8 - cron: '0 2 * * *' # Nightly at 2 AM
9
10jobs:
11 basic-tests:
12 runs-on: ubuntu-latest
13 steps:
14 - uses: actions/checkout@v3
15 - uses: docker/setup-buildx-action@v2
16 - name: Build Mau image
17 run: make -C e2e build-image
18 - name: Run basic tests
19 run: make -C e2e test-basic
20 - name: Upload logs
21 if: failure()
22 uses: actions/upload-artifact@v3
23 with:
24 name: test-logs-basic
25 path: e2e/test-results/
26
27 chaos-tests:
28 runs-on: ubuntu-latest
29 steps:
30 # Similar structure
31
32 stress-tests:
33 runs-on: ubuntu-latest
34 if: github.event_name == 'schedule' # Only nightly
35 steps:
36 # Run TC-202 (100-peer test)
File: e2e/scenarios/basic/friend_sync_test.go
1package basic
2
3import (
4 "strings"
5 "testing"
6 "time"
7
8 "github.com/mau-network/mau/e2e/framework/assertions"
9 "github.com/mau-network/mau/e2e/framework/testenv"
10 "github.com/stretchr/testify/assert"
11 "github.com/stretchr/testify/require"
12)
13
14func TestTwoP eerFriendSync(t *testing.T) {
15 // Step 1: Create test environment
16 env := testenv.NewTestEnv(t)
17 defer env.Cleanup() // Ensures cleanup even on test failure
18
19 // Step 2: Start two Mau peers
20 peerA, err := env.AddPeer("peer-a")
21 require.NoError(t, err, "Failed to create peer A")
22
23 peerB, err := env.AddPeer("peer-b")
24 require.NoError(t, err, "Failed to create peer B")
25
26 // Step 3: Exchange public keys and establish friend relationship
27 err = env.MakeFriends(peerA, peerB)
28 require.NoError(t, err, "Failed to establish friendship")
29
30 // Step 4: Verify friend relationship from both sides
31 assertions.AssertFriendRelationship(t, peerA, peerB)
32 assertions.AssertFriendRelationship(t, peerB, peerA)
33
34 // Step 5: Peer A creates a file encrypted for Peer B
35 fileContent := "Hello from Peer A!"
36 err = peerA.AddFile("hello.txt", strings.NewReader(fileContent), []string{peerB.Fingerprint()})
37 require.NoError(t, err, "Failed to create file on peer A")
38
39 // Step 6: Wait for synchronization (with timeout)
40 syncTimeout := 30 * time.Second
41 err = assertions.WaitForFile(t, peerB, "hello.txt", syncTimeout)
42 require.NoError(t, err, "File did not sync to peer B within timeout")
43
44 // Step 7: Verify file content matches
45 content, err := peerB.ReadFile("hello.txt")
46 require.NoError(t, err, "Failed to read file from peer B")
47 assert.Equal(t, fileContent, content, "File content mismatch")
48
49 // Step 8: Verify file is encrypted (PGP format)
50 rawContent, err := peerB.ReadFileRaw("hello.txt")
51 require.NoError(t, err, "Failed to read raw file")
52 assert.Contains(t, rawContent, "-----BEGIN PGP MESSAGE-----", "File not encrypted")
53
54 // Step 9: Check synchronization logs for debugging
55 logs := peerB.GetLogs()
56 assert.Contains(t, logs, "file_download_completed", "Sync event not logged")
57}
How This Test Executes:
Test Environment Creation:
testenv.NewTestEnv(t) creates isolated Docker network mau-test-<uuid>Peer A Container Startup:
mau-e2e:latest imageGET /healthPeer B Container Startup:
Friend Relationship Setup:
GET /p2p/<peer-b-fpr>/account.pgpPOST /admin/friends (test-only endpoint).mau/<peer-fpr>.pgpFile Creation:
POST /admin/files
1{
2 "name": "hello.txt",
3 "content": "SGVsbG8gZnJvbSBQZWVyIEEh", // base64
4 "encrypt_for": ["BBAF..."] // Peer B fingerprint
5}
<peer-a-fpr>/hello.txt.pgpSynchronization:
GET /p2p/<peer-a-fpr> (If-Modified-Since header)hello.txt metadataGET /p2p/<peer-a-fpr>/hello.txtAssertion:
GET /admin/files/hello.txtCleanup:
defer env.Cleanup() triggerse2e/test-results/<test-name>/Execution Time: ~15 seconds (including container startup)
How it works:
docker-compose.ymldocker exec commandsPros:
Cons:
go testVerdict: ❌ Not Recommended - Good for manual exploration, bad for automated testing
How it works:
go testPros:
deferCons:
Verdict: ✅ Recommended - Best balance of control and maintainability
How it works:
Pros:
Cons:
Verdict: ❌ Not Recommended - Overkill, stick with Docker
How it works:
Pros:
Cons:
Verdict: ⚠️ Overkill Now, Revisit Later - Good if Mau gets multiple implementations
| Criterion | Docker Compose | Testcontainers-Go | Kubernetes | Custom Harness |
|---|---|---|---|---|
| Ease of Use | ★★★★☆ | ★★★☆☆ | ★☆☆☆☆ | ★★☆☆☆ |
| Programmatic Control | ★☆☆☆☆ | ★★★★★ | ★★★☆☆ | ★★★★★ |
| Test Isolation | ★★☆☆☆ | ★★★★★ | ★★★★★ | ★★★★☆ |
| CI/CD Integration | ★★☆☆☆ | ★★★★★ | ★★★☆☆ | ★★★☆☆ |
| Debugging | ★★★★☆ | ★★★★☆ | ★★☆☆☆ | ★★★☆☆ |
| Maintenance | ★★★☆☆ | ★★★★☆ | ★☆☆☆☆ | ★★☆☆☆ |
| Scalability (100+ peers) | ★★☆☆☆ | ★★★★☆ | ★★★★★ | ★★★★☆ |
| Setup Time | 5 min | 15 min | 60 min | 120 min |
Final Recommendation: Testcontainers-Go with Docker Compose for manual debugging
File: .github/workflows/e2e-tests.yml
1name: E2E Tests
2
3on:
4 pull_request:
5 paths:
6 - '**.go'
7 - 'e2e/**'
8 - '.github/workflows/e2e-tests.yml'
9 push:
10 branches: [main, develop]
11 schedule:
12 - cron: '0 2 * * *' # Nightly stress tests
13
14env:
15 GO_VERSION: '1.21'
16 DOCKER_BUILDKIT: 1
17
18jobs:
19 build-image:
20 name: Build Mau E2E Image
21 runs-on: ubuntu-latest
22 steps:
23 - uses: actions/checkout@v4
24
25 - name: Set up Docker Buildx
26 uses: docker/setup-buildx-action@v3
27
28 - name: Build and export
29 uses: docker/build-push-action@v5
30 with:
31 context: .
32 file: e2e/docker/Dockerfile.mau
33 tags: mau-e2e:${{ github.sha }}
34 outputs: type=docker,dest=/tmp/mau-e2e.tar
35
36 - name: Upload image artifact
37 uses: actions/upload-artifact@v4
38 with:
39 name: mau-e2e-image
40 path: /tmp/mau-e2e.tar
41 retention-days: 1
42
43 test-basic:
44 name: Basic Tests (Level 1)
45 needs: build-image
46 runs-on: ubuntu-latest
47 timeout-minutes: 15
48 steps:
49 - uses: actions/checkout@v4
50
51 - name: Set up Go
52 uses: actions/setup-go@v4
53 with:
54 go-version: ${{ env.GO_VERSION }}
55
56 - name: Download image
57 uses: actions/download-artifact@v4
58 with:
59 name: mau-e2e-image
60 path: /tmp
61
62 - name: Load image
63 run: docker load --input /tmp/mau-e2e.tar
64
65 - name: Run basic tests
66 run: |
67 cd e2e
68 go test -v -timeout 10m ./scenarios/basic/...
69 env:
70 MAU_E2E_IMAGE: mau-e2e:${{ github.sha }}
71
72 - name: Upload test results
73 if: always()
74 uses: actions/upload-artifact@v4
75 with:
76 name: test-results-basic
77 path: e2e/test-results/
78
79 test-resilience:
80 name: Resilience Tests (Level 2)
81 needs: build-image
82 runs-on: ubuntu-latest
83 timeout-minutes: 30
84 steps:
85 # Similar to test-basic
86 - name: Run resilience tests
87 run: |
88 cd e2e
89 go test -v -timeout 25m ./scenarios/resilience/...
90
91 test-stress:
92 name: Stress Tests (Level 3)
93 needs: build-image
94 runs-on: ubuntu-latest-8-cores # Larger runner
95 if: github.event_name == 'schedule' || contains(github.event.head_commit.message, '[stress]')
96 timeout-minutes: 60
97 steps:
98 # Similar to test-basic
99 - name: Run stress tests
100 run: |
101 cd e2e
102 go test -v -timeout 50m ./scenarios/stress/...
103
104 test-security:
105 name: Security Tests (Level 4)
106 needs: build-image
107 runs-on: ubuntu-latest
108 timeout-minutes: 20
109 steps:
110 - name: Run security tests
111 run: |
112 cd e2e
113 go test -v -timeout 15m ./scenarios/security/...
114
115 report:
116 name: Generate Test Report
117 needs: [test-basic, test-resilience, test-security]
118 if: always()
119 runs-on: ubuntu-latest
120 steps:
121 - uses: actions/checkout@v4
122
123 - name: Download all results
124 uses: actions/download-artifact@v4
125 with:
126 path: all-results
127
128 - name: Generate HTML report
129 run: |
130 cd e2e
131 ./scripts/generate-report.sh ../all-results
132
133 - name: Upload report
134 uses: actions/upload-artifact@v4
135 with:
136 name: test-report
137 path: e2e/report.html
138
139 - name: Comment PR
140 if: github.event_name == 'pull_request'
141 uses: actions/github-script@v7
142 with:
143 script: |
144 // Parse test results and post summary comment
Parallel Test Execution:
t.Parallel() in Go tests where safeImage Caching:
Test Result Caching:
Fast Failure:
Resource Management:
Structured Logs:
All Mau peers emit JSON logs to stdout:
1{
2 "timestamp": "2026-02-19T14:30:00Z",
3 "level": "info",
4 "peer_id": "peer-a",
5 "fingerprint": "ABAF11C65A2970B130ABE3C479BE3E4300411886",
6 "trace_id": "tc002-run42",
7 "component": "sync",
8 "event": "file_download_started",
9 "file": "hello.txt",
10 "source_peer": "peer-b",
11 "bytes": 1024
12}
Fields:
trace_id: Links all logs from one test executionpeer_id: Container name (e.g., peer-a)component: dht, sync, server, keyringevent: Structured event nameCollection:
e2e/test-results/<test-name>/logs/<peer-id>.jsonjq for filtering: jq '.component == "sync"' peer-a.jsonWhat to capture on test failure:
File System State:
.mau/, <fpr>/)tar -czf peer-a-files.tar.gz /dataDHT Routing Tables:
GET /admin/dht/routing-tableFriend Lists:
GET /admin/friendsContainer Stats:
docker stats snapshot (CPU/memory usage)Network State:
Automated Snapshot Script:
1func (e *TestEnv) CaptureSnapshot(testName string) error {
2 snapshotDir := filepath.Join("test-results", testName, "snapshots")
3 os.MkdirAll(snapshotDir, 0755)
4
5 for _, peer := range e.peers {
6 // Capture file tree
7 peer.ExecTar("/data", filepath.Join(snapshotDir, peer.Name+"-files.tar.gz"))
8
9 // Capture DHT state
10 dht, _ := peer.GetDHTState()
11 writeJSON(filepath.Join(snapshotDir, peer.Name+"-dht.json"), dht)
12
13 // Capture logs
14 logs := peer.GetLogs()
15 os.WriteFile(filepath.Join(snapshotDir, peer.Name+".log"), logs, 0644)
16 }
17 return nil
18}
When a test fails:
Check CI Artifacts:
test-results-<level>.zipRead Test Summary:
test-results/<test-name>/summary.jsonFilter Logs by Trace ID:
1cd test-results/TestTwoPeerFriendSync/logs
2jq '. | select(.trace_id == "tc002-run42")' peer-*.json | less
Inspect File State:
1tar -xzf snapshots/peer-a-files.tar.gz
2tree data/
Reproduce Locally:
1# Use Docker Compose for manual control
2cd e2e
3docker-compose -f docker/docker-compose.yml up
4# Manually trigger actions via API
5curl -X POST http://localhost:8080/admin/files -d '...'
Enable Verbose Logging:
1// In test file
2env.SetLogLevel("debug") // Enables DEBUG level logs
Pause Test on Failure:
1if t.Failed() {
2 fmt.Println("Test failed, containers still running. Press enter to cleanup...")
3 bufio.NewReader(os.Stdin).ReadString('\n')
4}
| Tool | Purpose | Integration |
|---|---|---|
| jq | Log filtering/analysis | Manual, CI scripts |
| Docker logs | Real-time log tailing | docker logs -f <container> |
| Docker stats | Resource monitoring | docker stats during test |
| Wireshark/tcpdump | Network traffic capture (advanced) | Manual debugging |
| Grafana/Loki | Log aggregation (future) | Optional for large test suites |
DHT Bootstrap Strategy:
Test Data Persistence:
Performance Baselines:
Chaos Test Reproducibility:
Security Test Scope:
Test Environment Variables:
Visual Test Reports:
Mutation Testing:
Fuzz Testing Integration:
go-fuzz to generate file contentPerformance Regression Detection:
Multi-Platform Testing:
Record/Replay:
Chaos Mesh Integration:
Contract Testing:
This E2E testing framework is designed to:
✅ Validate Mau’s core P2P functionality (discovery, sync, friend management)
✅ Detect regressions early via automated CI/CD integration
✅ Simulate real-world conditions (network failures, high latency, peer churn)
✅ Scale from 2 to 100+ peers with minimal test code changes
✅ Provide excellent debugging with rich logs, state snapshots, and artifacts
✅ Remain maintainable with clear structure and comprehensive documentation
After 6 months of use:
Document Version: 1.0
Last Updated: 19 February 2026
Reviewers: [To be assigned]
Status: Awaiting Review