test/

directory
v1.6.10 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 28, 2026 License: Apache-2.0

README

Test Patterns and Conventions

This document describes the testing patterns and conventions used in the neuwerk test suite.

Directory Structure

test/
├── helpers/           # Shared test utilities
│   ├── bdd.go         # BaseBDDStage for BDD test stages
│   ├── client.go      # HTTP/network client helpers
│   ├── config.go      # Centralized test configuration
│   ├── debug.go       # Debug and tracing utilities
│   ├── helpers.go     # Common assertion helpers
│   ├── neuwerk.go     # Neuwerk instance management
│   ├── setup.go       # Network namespace setup
│   └── test_network.go # TestNetwork accessors
├── integration/       # Integration tests (require root)
│   ├── *_test.go      # Test files
│   └── *_stage_test.go # BDD stage implementations
└── e2e/               # End-to-end tests
    ├── azure/         # Azure-specific tests
    └── gcp/           # GCP-specific tests

Root Privilege Requirements

All integration tests require root privileges to create network namespaces and load BPF programs.

func TestSomething(t *testing.T) {
    if os.Getuid() != 0 {
        t.Skip("Integration tests require root privileges")
    }
    // ... test code
}

BDD Stage Pattern

Tests use a BDD (Behavior-Driven Development) stage pattern for readability:

func TestFeature(t *testing.T) {
    given, when, then := NewMyStage(t)

    given.
        a_test_environment().and().
        some_precondition()

    when.
        an_action_is_performed()

    then.
        the_expected_outcome_occurs()
}
Creating a New Stage

Stages should embed helpers.BaseBDDStage to reduce boilerplate:

type MyStage struct {
    helpers.BaseBDDStage
    // Stage-specific fields
    testNet *helpers.TestNetwork
    client  *helpers.Client
}

func NewMyStage(t *testing.T) (*MyStage, *MyStage, *MyStage) {
    s := &MyStage{
        BaseBDDStage: helpers.NewBaseBDDStage(t),
    }
    return s, s, s  // given, when, then all point to same instance
}

func (s *MyStage) and() *MyStage { return s }

Access fields via s.T, s.Require, s.Assert (capitalized).

Assert vs Require Conventions

Use require (stops on failure) for:

  • Setup preconditions that must succeed
  • Critical state that makes further testing meaningless
  • Resource allocation/cleanup
// Setup must succeed - use require
instance, err := helpers.StartNeuwerk(t, testNet, opts)
s.Require.NoError(err, "failed to start neuwerk")

// Resource must exist before testing
s.Require.FileExists(certPath, "certificate must be generated")

Use assert (continues on failure) for:

  • Non-critical assertions where you want to see all failures
  • Verification of multiple independent conditions
  • Soft checks that don't block further testing
// Check multiple properties - want to see all failures
s.Assert.Equal(expectedSerial, actualSerial, "serial should match")
s.Assert.True(cert.NotAfter.After(time.Now()), "cert should not be expired")

Test Naming Conventions

Test Functions
  • Use descriptive names: TestNetworkCRUD_CreateAndDelete
  • Group related tests with underscores: TestAuth_TokenLogin, TestAuth_Logout
Stage Methods
  • Use snake_case for readability: a_test_environment_with_bootstrap()
  • Start with appropriate BDD keywords:
    • given: a_, an_, the_
    • when: action verbs like traffic_is_sent_, certificate_rotation_is_triggered_
    • then: the_connection_succeeds(), all_certificates_are_valid()

Common Test Helpers

Network Setup
// Single node test environment
testNet := helpers.SetupSingleNodeWithCleanup(t)

// Multi-node HA environment
testNets := helpers.SetupMultiNodeWithCleanup(t, 3)
Starting Neuwerk
// Basic startup
instance, err := helpers.StartNeuwerk(t, testNet, helpers.NeuwerkOptions{})

// With client for API calls
instance, client := helpers.StartNeuwerkWithClient(t, testNet)
Waiting for Conditions
// Wait for health endpoint
helpers.WaitForHealthy(t, require, healthURL, "service should be healthy")

// Wait with custom timeout
helpers.WaitForHealthyWithTimeout(t, require, healthURL, 30*time.Second, "msg")

// Wait for arbitrary condition
helpers.WaitForCondition(t, require, func() bool {
    return checkSomeState()
}, "condition should be met")
Connection Assertions
// Assert connection succeeds
helpers.AssertConnectionSucceeds(t, require, client, ip, port, "should connect")

// Assert connection is blocked
helpers.AssertConnectionBlocked(t, require, client, ip, port, "should be blocked")

// Assert with retry
helpers.AssertEventuallyConnects(t, require, client, ip, port, "should eventually connect")

Configuration Constants

Use constants from test/helpers/config.go for network addresses:

import "github.com/moolen/neuwerk/test/helpers"

// Instead of hardcoding:
// ip := net.ParseIP("10.100.1.10")

// Use:
ip := helpers.DefaultNeuwerkIngressIP

Running Tests

# Run all integration tests (requires root)
sudo go test ./test/integration/... -v

# Run specific test
sudo go test ./test/integration/... -run TestNetworkCRUD -v

# Run with race detection
sudo go test ./test/integration/... -race -v

# Run e2e tests (require cloud credentials)
sudo go test ./test/e2e/gcp/... -v

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL