Skip to content

Add test utilities package for IService mocking #2981

@jrschumacher

Description

@jrschumacher

Problem

The IService interface in pkg/serviceregistry is complex (13 methods) and difficult to mock in downstream projects for testing purposes. Key challenges include:

  1. Internal package dependencies: RegisterConnectRPCServiceHandler requires *server.ConnectRPC from an internal package that external projects cannot import
  2. Interface complexity: Implementing all 13 methods for test mocks is tedious and error-prone
  3. Maintenance burden: When the interface evolves, all downstream projects must update their mocks

Current Workaround

Downstream projects either:

  • Skip testing functions that accept IService parameters
  • Create incomplete mocks that can't properly implement the interface
  • Work around the limitation by testing only what doesn't require service instances

Proposed Solution

Add a dedicated testing package with mock utilities:

github.com/opentdf/platform/service/pkg/serviceregistry/testing

Example Implementation

package testing

import (
    "context"
    "embed"
    
    "github.com/opentdf/platform/service/pkg/serviceregistry"
    "github.com/opentdf/platform/service/internal/server"
    // ... other imports
)

// MockService provides a configurable mock implementation of IService
type MockService struct {
    // Configurable return values
    NamespaceValue string
    VersionValue   string
    DBRequired     bool
    
    // Configurable errors
    StartError    error
    ShutdownError error
    
    // Call tracking for assertions
    StartCalled            int
    ShutdownCalled         int
    IsStartedCalled        int
    
    // Custom implementations (optional)
    StartFunc func(context.Context, serviceregistry.RegistrationParams) error
}

// NewMockService creates a mock with sensible defaults
func NewMockService(namespace string) *MockService {
    return &MockService{
        NamespaceValue: namespace,
        VersionVersion:   "v1.0.0",
        DBRequired:     false,
    }
}

// IsDBRequired implements IService
func (m *MockService) IsDBRequired() bool {
    return m.DBRequired
}

// DBMigrations implements IService
func (m *MockService) DBMigrations() *embed.FS {
    return nil
}

// GetNamespace implements IService
func (m *MockService) GetNamespace() string {
    return m.NamespaceValue
}

// ... implement all other interface methods ...

// Start implements IService
func (m *MockService) Start(ctx context.Context, params serviceregistry.RegistrationParams) error {
    m.StartCalled++
    if m.StartFunc != nil {
        return m.StartFunc(ctx, params)
    }
    return m.StartError
}

// ... etc

Benefits

  1. Easier testing: Downstream projects can easily test code that depends on IService
  2. Access to internal types: OpenTDF can properly implement methods requiring internal types
  3. Reduced duplication: One maintained implementation instead of many project-specific mocks
  4. Better documentation: Serves as reference for how to work with IService
  5. Consistent testing: All projects use the same well-tested mock implementation

Similar Patterns in Go Ecosystem

This follows established Go testing patterns:

  • net/http/httptest - testing utilities for HTTP
  • database/sql/driver test mocks
  • google.golang.org/grpc/test - gRPC testing utilities

Implementation Checklist

  • Create pkg/serviceregistry/testing package
  • Implement MockService struct with configurable behavior
  • Add helper constructors (NewMockService, NewMockServiceWithDefaults, etc.)
  • Include examples in package documentation
  • Add tests for the mock itself
  • Update main documentation to reference testing utilities

Related

This came up while writing tests for downstream projects that use WithServices() options and need to verify service registration behavior.

Priority

Medium - This is a quality-of-life improvement that would significantly help downstream projects and the broader OpenTDF ecosystem.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions