-
Notifications
You must be signed in to change notification settings - Fork 24
Open
Labels
enhancementNew feature or requestNew feature or request
Description
Problem
The IService interface in pkg/serviceregistry is complex (13 methods) and difficult to mock in downstream projects for testing purposes. Key challenges include:
- Internal package dependencies:
RegisterConnectRPCServiceHandlerrequires*server.ConnectRPCfrom an internal package that external projects cannot import - Interface complexity: Implementing all 13 methods for test mocks is tedious and error-prone
- Maintenance burden: When the interface evolves, all downstream projects must update their mocks
Current Workaround
Downstream projects either:
- Skip testing functions that accept
IServiceparameters - 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
}
// ... etcBenefits
- Easier testing: Downstream projects can easily test code that depends on
IService - Access to internal types: OpenTDF can properly implement methods requiring internal types
- Reduced duplication: One maintained implementation instead of many project-specific mocks
- Better documentation: Serves as reference for how to work with
IService - 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 HTTPdatabase/sql/drivertest mocksgoogle.golang.org/grpc/test- gRPC testing utilities
Implementation Checklist
- Create
pkg/serviceregistry/testingpackage - Implement
MockServicestruct 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
Labels
enhancementNew feature or requestNew feature or request