testutil

package
v0.2.0-alpha.1 Latest Latest
Warning

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

Go to latest
Published: Jul 5, 2026 License: Apache-2.0 Imports: 9 Imported by: 0

README

pkg/controller/testutil

Shared test helpers for pkg/controller/* package tests.

Overview

Most controller-package tests need the same handful of pieces: a fresh *EventBus, a quiet logger, generic helpers for waiting on a typed event, and consistent timing constants so the test suite isn't a graveyard of magic numbers. This package consolidates them so individual _test.go files stay focused on the behaviour under test.

This package is only consumed from tests. Production code must not import it.

Helpers

Function Purpose
NewTestBus() *EventBus with a 100-slot buffer
NewTestLogger() *slog.Logger writing to stderr at ERROR level (quiet during runs)
NewTestBusAndLogger() Both of the above in one call (the most common setup)
WaitForEvent[T](t, ch, timeout) Drain ch until an event of type T arrives, fail test on timeout
WaitForEventWithPredicate[T](t, ch, timeout, pred) Same plus a predicate filter
AssertNoEvent[T](t, ch, timeout) Fail if an event of type T arrives within timeout
DrainChannel(ch) Non-blocking drain (helpful for "ignore everything queued" before triggering the action under test)
RunComponentStartStop(t, bus, start, stop) Standard lifecycle test for a Start/Stop component

Timing Constants

Name Value Use for
StartupDelay 50ms Brief settling pause after starting components
DebounceWait 100ms Long enough for debounce timers (default is 2s in production but tests override down)
EventTimeout 500ms Default waiting timeout for a single event
LongTimeout 1s Operations that may take longer (graceful stop)
VeryLongTimeout 2s Integration-style tests inside unit test files
NoEventTimeout 200ms Shorter timeout for "verify nothing arrives"

Use these instead of inline time.Second / 2 literals — when CI runners get slower, one constant moves and every test gets the headroom.

Quick Start

import (
    "testing"

    "gitlab.com/haproxy-haptic/haptic/pkg/controller/events"
    "gitlab.com/haproxy-haptic/haptic/pkg/controller/testutil"
)

func TestMyComponent_Foo(t *testing.T) {
    bus, logger := testutil.NewTestBusAndLogger()
    eventChan := bus.Subscribe("test", 10)
    bus.Start()

    component := New(bus, logger)
    go component.Start(t.Context()) // Go 1.24+ test-scoped context

    bus.Publish(events.NewSomeRequest("input"))
    got := testutil.WaitForEvent[*events.SomeResponse](t, eventChan, testutil.EventTimeout)
    assert.Equal(t, "expected", got.Result)
}

License

Apache-2.0 — see root LICENSE.

Documentation

Overview

Package testutil provides shared test helpers for controller package tests. These helpers reduce code duplication across test files by providing: - Standard EventBus and Logger creation - Event waiting utilities with timeout - Component lifecycle testing helpers - Timing constants to replace magic numbers

Index

Constants

View Source
const (
	// StartupDelay is the time to wait for components to initialize after starting.
	StartupDelay = 50 * time.Millisecond

	// DebounceWait is the time to wait for debounce timers to fire.
	DebounceWait = 100 * time.Millisecond

	// EventTimeout is the default timeout for waiting on a single event.
	EventTimeout = 500 * time.Millisecond

	// LongTimeout is used for operations that may take longer (e.g., component stop).
	LongTimeout = 1 * time.Second

	// VeryLongTimeout is used for integration-style tests within unit test files.
	VeryLongTimeout = 2 * time.Second

	// NoEventTimeout is a shorter timeout for verifying no event is received.
	NoEventTimeout = 200 * time.Millisecond
)

Timing constants to replace magic numbers in tests. These provide consistent, documented values for test synchronization.

View Source
const ValidHAProxyConfigTemplate = `` /* 266-byte string literal not displayed */

ValidHAProxyConfigTemplate is a minimal valid HAProxy configuration template that passes HAProxy syntax validation. Use this in tests that need to render and validate HAProxy configurations.

Variables

This section is empty.

Functions

func AssertNoEvent

func AssertNoEvent[T any](t *testing.T, eventChan <-chan busevents.Event, timeout time.Duration)

AssertNoEvent verifies that no event of type T is received within the timeout. This is useful for testing that certain events are NOT published.

Example:

testutil.AssertNoEvent[*events.ConfigParsedEvent](t, eventChan, testutil.NoEventTimeout)

func CreateTestSecretWithStringData

func CreateTestSecretWithStringData(name, namespace, resourceVersion string, data map[string]string) *unstructured.Unstructured

CreateTestSecretWithStringData creates an unstructured Secret with string data. This is useful for credentials secrets that don't need base64 encoding in tests.

Example:

secret := testutil.CreateTestSecretWithStringData("creds", "default", "12345",
    map[string]string{"username": "admin", "password": "secret"})

func DrainChannel

func DrainChannel(eventChan <-chan busevents.Event)

DrainChannel empties all pending events from the channel without blocking. Useful for clearing events between test phases.

func NewTestBus

func NewTestBus() *busevents.EventBus

NewTestBus creates an EventBus with a standard buffer size for tests.

func NewTestBusAndLogger

func NewTestBusAndLogger() (*busevents.EventBus, *slog.Logger)

NewTestBusAndLogger creates both an EventBus and Logger for tests. This is the most common setup pattern across controller tests.

func NewTestLogger

func NewTestLogger() *slog.Logger

NewTestLogger creates a logger that writes to stderr with error level. This minimizes noise in test output while still capturing errors.

func RunComponentContextCancel

func RunComponentContextCancel(t *testing.T, bus *busevents.EventBus, startFunc func(context.Context) error)

RunComponentContextCancel tests that a component stops cleanly when context is cancelled. This reduces boilerplate for the common test pattern in controller components.

Example:

func TestMyComponent_StartWithContextCancel(t *testing.T) {
    bus, logger := testutil.NewTestBusAndLogger()
    component := NewMyComponent(bus, logger)
    testutil.RunComponentContextCancel(t, bus, component.Start)
}

func RunComponentStartStop

func RunComponentStartStop(t *testing.T, bus *busevents.EventBus, startFunc func(context.Context) error, stopFunc func())

RunComponentStartStop tests that a component starts and stops cleanly. This reduces boilerplate for the common test pattern in controller components.

Example:

func TestMyComponent_StartAndStop(t *testing.T) {
    bus, logger := testutil.NewTestBusAndLogger()
    component := NewMyComponent(bus, logger)
    testutil.RunComponentStartStop(t, bus, component.Start, component.Stop)
}

func WaitForEvent

func WaitForEvent[T any](t *testing.T, eventChan <-chan busevents.Event, timeout time.Duration) T

WaitForEvent waits for an event of type T on the channel, failing the test on timeout. It skips over events of other types until finding a matching one.

Example:

event := testutil.WaitForEvent[*events.ConfigParsedEvent](t, eventChan, testutil.EventTimeout)
assert.Equal(t, "v1", event.Version)

func WaitForEventWithPredicate

func WaitForEventWithPredicate[T any](t *testing.T, eventChan <-chan busevents.Event, timeout time.Duration, predicate func(T) bool) T

WaitForEventWithPredicate waits for an event of type T that satisfies the predicate. Useful when you need to match on event fields, not just type.

Example:

event := testutil.WaitForEventWithPredicate(t, eventChan, testutil.EventTimeout,
    func(e *events.ConfigParsedEvent) bool { return e.Version == "v2" })

Types

This section is empty.

Jump to

Keyboard shortcuts

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