testing/

directory
v2.8.0 Latest Latest
Warning

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

Go to latest
Published: Jan 1, 2026 License: GPL-3.0

README

Testing Helpers & Examples

This directory provides comprehensive testing utilities for Zaparoo Core, enabling hardware-independent testing with zero dependencies on physical readers, databases, or filesystems.

Quick Reference

Mock Components
Platform Mocking
  • mocks.NewMockPlatform() - Mock platform for cross-platform testing
  • platform.SetupBasicMock() - Standard mock configuration
Reader Mocking
  • mocks.NewMockReader() - Mock card reader for testing token scanning
  • reader.On("ReadTokens", ...) - Set up token reading expectations
WebSocket Testing
  • helpers.NewWebSocketTestServer(t, handler) - Test WebSocket server
  • server.NewClient(t) - WebSocket test client
Database Testing
Mock Database Interfaces
  • helpers.NewMockUserDBI() - In-memory user database mock
  • helpers.NewMockMediaDBI() - In-memory media database mock
Database Fixtures & Helpers
  • fixtures.SampleTokens() - Pre-defined test token data
  • fixtures.SampleMedia() - Pre-defined media entries
  • fixtures.SamplePlaylists() - Pre-defined playlist data
  • helpers.HistoryEntryMatcher() - Flexible matching for history entries
Filesystem Testing
In-Memory Filesystem
  • helpers.NewMemoryFS() - In-memory filesystem using afero
  • fs.WriteFile(path, content, perm) - Create test files
  • fs.ReadFile(path) - Read test files
  • fs.FileExists(path) - Check if file exists
  • fs.CreateConfigFile(path, map) - Create JSON config from map
  • fs.CreateMediaDirectory(basePath) - Create sample media directory structure
  • fs.CreateDirectoryStructure(structure) - Create complex directory trees from map
Configuration Helpers
  • helpers.NewTestConfig(fs, configDir) - Test configuration with random port
  • helpers.NewTestConfigWithPort(fs, configDir, port) - Test configuration with specific port
Time-Based Testing
Clockwork for Deterministic Time
  • clockwork.NewFakeClock() - Fake clock for fast, deterministic tests
  • fakeClock.Advance(duration) - Progress time instantly without waiting
  • fakeClock.BlockUntilContext(ctx, n) - Wait for n goroutines to block on clock
  • clock.Now() - Get current time (fake or real)
  • clock.Since(t) - Calculate elapsed time
  • clock.NewTicker(d) - Create ticker (fake or real)
Production Code Pattern
type MyService struct {
    clock clockwork.Clock  // Inject clock for testability
}

func NewMyService() *MyService {
    return &MyService{
        clock: clockwork.NewRealClock(),  // Real clock in production
    }
}
Test Pattern
func TestMyService(t *testing.T) {
    fakeClock := clockwork.NewFakeClock()
    service := &MyService{clock: fakeClock}  // Inject fake clock

    // Advance time instantly
    fakeClock.Advance(1 * time.Minute)

    // No time.Sleep() needed!
}

See TESTING.md for comprehensive examples and best practices.

Fuzz Testing
Native Go Fuzzing (Go 1.18+)
  • func FuzzFunctionName(f *testing.F) - Fuzz test function signature
  • f.Add(inputs...) - Seed corpus with known test cases
  • f.Fuzz(func(t *testing.T, inputs...) { ... }) - Fuzz target function
Running Fuzz Tests
# Run all tests (includes fuzz corpus) - FAST
task test

# Manual fuzzing (runs until failure or Ctrl+C)
go test -fuzz=FuzzParseVirtualPathStr ./pkg/helpers/

# Time-boxed fuzzing
go test -fuzz=FuzzName -fuzztime=30s ./pkg/helpers/
Example Fuzz Test
func FuzzParseURI(f *testing.F) {
    // Seed corpus
    f.Add("steam://123/Game")
    f.Add("") // Edge case

    f.Fuzz(func(t *testing.T, uri string) {
        result, err := ParseURI(uri)

        // Test properties (invariants)
        if err == nil && !utf8.ValidString(result) {
            t.Errorf("Invalid UTF-8: %q", result)
        }
    })
}

Example fuzz tests: pkg/helpers/uris_fuzz_test.go, pkg/helpers/paths_fuzz_test.go

API Testing
WebSocket & JSON-RPC
  • helpers.NewWebSocketTestServer(t, messageHandler) - WebSocket server for testing
  • client.SendMessage(data) - Send test messages
  • client.ReceiveMessage() - Receive and validate responses
HTTP Testing
  • Standard Go net/http/httptest integration
  • Context-aware HTTP client helpers

Complete Examples

The examples/ directory contains working examples demonstrating all testing patterns:

Core Testing Patterns
  • api_example_test.go - WebSocket communication, JSON-RPC testing, HTTP endpoints
  • database_example_test.go - Database operations, mock expectations, transaction testing
  • filesystem_example_test.go - File operations, configuration management, in-memory filesystem
  • mock_usage_example_test.go - Platform mocking, reader mocking, expectation setup
Service Layer Testing
  • service_token_processing_test.go - End-to-end token processing workflows
  • service_state_management_test.go - Application state management testing
  • service_zapscript_test.go - ZapScript execution and custom launcher testing

Common Patterns

Basic Test Setup
func TestYourFeature(t *testing.T) {
    t.Parallel()
    
    // Setup mocks
    platform := mocks.NewMockPlatform()
    platform.SetupBasicMock()
    
    // Setup database
    db := &database.Database{
        UserDB:  helpers.NewMockUserDBI(),
        MediaDB: helpers.NewMockMediaDBI(),
    }
    
    // Setup state
    st, notifications := state.NewState(platform)
    defer st.StopService()
    
    // Your test logic here
}
API Server Testing
func TestAPIEndpoint(t *testing.T) {
    // Setup test environment
    platform := mocks.NewMockPlatform()
    platform.SetupBasicMock()
    
    fs := helpers.NewMemoryFS()
    cfg, err := helpers.NewTestConfigWithPort(fs, t.TempDir(), 0)
    require.NoError(t, err)
    
    // Start server
    go api.Start(platform, cfg, st, tokenQueue, db, notifications)
    
    // Test API calls...
}
Token Processing Testing
func TestTokenProcessing(t *testing.T) {
    // Setup mock reader
    reader := mocks.NewMockReader()
    reader.On("ReadTokens", mock.Anything).Return([]tokens.Token{
        {UID: "test-uid", Data: "test-data"},
    }, nil)
    
    // Test processing...
}
Database Operation Testing
func TestDatabaseOperations(t *testing.T) {
    userDB := helpers.NewMockUserDBI()
    
    // Set expectations
    userDB.On("AddHistory", helpers.HistoryEntryMatcher()).Return(nil)
    
    // Test your function
    err := YourFunction(userDB)
    require.NoError(t, err)
    
    // Verify expectations
    userDB.AssertExpectations(t)
}

Test Data & Fixtures

The fixtures/ directory provides pre-built test data:

Token Fixtures
  • fixtures.SampleTokens() - Slice of common test tokens
  • fixtures.NewNFCToken(), NewMifareToken(), NewAmiiboToken() - Individual token types
  • fixtures.NewTokenCollection() - Collection with lookup methods
Media Fixtures
  • fixtures.SampleMedia() - Slice of common test media
  • fixtures.NewRetroGame(), NewModernGame(), NewArcadeGame() - Individual media types
  • fixtures.NewMediaCollection() - Collection with lookup methods
Other Fixtures
  • fixtures.SamplePlaylists() - Pre-defined playlist data
  • fixtures.HistoryEntries - Pre-populated history entries

Integration with TDD Guard

All tests are monitored by TDD Guard for strict test-driven development:

  • Use task test instead of go test for TDD integration
  • Write failing tests first, then implement features
  • TDD Guard ensures code changes are driven by test failures

Best Practices

  1. Always use t.Parallel() for independent tests
  2. Use unique ports for concurrent API server tests (8000 + testID pattern)
  3. Clean up resources with defer statements
  4. Use meaningful test data from fixtures rather than hardcoded values
  5. Test behavior, not implementation - focus on what the code does, not how
  6. Leverage mocks for hardware dependencies - no real readers or databases needed

Directories

Path Synopsis
Package helpers provides testing utilities for API operations.
Package helpers provides testing utilities for API operations.
Package sqlmock provides SQL mocking utilities for testing.
Package sqlmock provides SQL mocking utilities for testing.

Jump to

Keyboard shortcuts

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