diff --git a/internal/app/builder.go b/internal/app/builder.go index 7cba49dd..be7ecced 100644 --- a/internal/app/builder.go +++ b/internal/app/builder.go @@ -57,7 +57,7 @@ type registryAppConfig struct { storageManager sources.StorageManager statusPersistence status.StatusPersistence syncManager pkgsync.Manager - registryProvider service.RegistryDataProvider + registryProvider inmemory.RegistryDataProvider // HTTP server options address string @@ -260,7 +260,7 @@ func WithSyncManager(sm pkgsync.Manager) RegistryAppOptions { } // WithRegistryProvider allows injecting a custom registry provider (for testing) -func WithRegistryProvider(provider service.RegistryDataProvider) RegistryAppOptions { +func WithRegistryProvider(provider inmemory.RegistryDataProvider) RegistryAppOptions { return func(cfg *registryAppConfig) error { cfg.registryProvider = provider return nil @@ -358,13 +358,7 @@ func buildServiceComponents( case config.StorageTypeFile: // Build registry provider (reads from synced data via StorageManager) if b.registryProvider == nil { - // StorageManager was already built in buildSyncComponents - factory := service.NewRegistryProviderFactory(b.storageManager) - provider, err := factory.CreateProvider(b.config) - if err != nil { - return nil, fmt.Errorf("failed to create registry provider: %w", err) - } - b.registryProvider = provider + b.registryProvider = inmemory.NewFileRegistryDataProvider(b.storageManager, b.config) slog.Info("Created registry data provider using storage manager") } diff --git a/internal/app/builder_test.go b/internal/app/builder_test.go index f9d5a486..d4b6e9de 100644 --- a/internal/app/builder_test.go +++ b/internal/app/builder_test.go @@ -15,7 +15,9 @@ import ( "github.com/stacklok/toolhive-registry-server/internal/config" "github.com/stacklok/toolhive-registry-server/internal/kubernetes" "github.com/stacklok/toolhive-registry-server/internal/service" - "github.com/stacklok/toolhive-registry-server/internal/service/mocks" + "github.com/stacklok/toolhive-registry-server/internal/service/inmemory" + "github.com/stacklok/toolhive-registry-server/internal/service/inmemory/mocks" + mocksvc "github.com/stacklok/toolhive-registry-server/internal/service/mocks" "github.com/stacklok/toolhive-registry-server/internal/sources" "github.com/stacklok/toolhive-registry-server/internal/status" pkgsync "github.com/stacklok/toolhive-registry-server/internal/sync" @@ -313,7 +315,7 @@ func TestWithRegistryProvider(t *testing.T) { t.Parallel() cfg := ®istryAppConfig{} // Use nil registry provider for testing - we're just verifying the field is set - var testRegistryProvider service.RegistryDataProvider + var testRegistryProvider inmemory.RegistryDataProvider opt := WithRegistryProvider(testRegistryProvider) err := opt(cfg) @@ -329,7 +331,7 @@ func TestBuildHTTPServer(t *testing.T) { tests := []struct { name string config *registryAppConfig - setupMock func(*mocks.MockRegistryService) + setupMock func(*mocksvc.MockRegistryService) wantAddr string wantReadTO time.Duration wantWriteTO time.Duration @@ -346,7 +348,7 @@ func TestBuildHTTPServer(t *testing.T) { writeTimeout: 15 * time.Second, idleTimeout: 60 * time.Second, }, - setupMock: func(_ *mocks.MockRegistryService) {}, + setupMock: func(_ *mocksvc.MockRegistryService) {}, wantAddr: ":8080", wantReadTO: 10 * time.Second, wantWriteTO: 15 * time.Second, @@ -365,7 +367,7 @@ func TestBuildHTTPServer(t *testing.T) { writeTimeout: 10 * time.Second, idleTimeout: 30 * time.Second, }, - setupMock: func(_ *mocks.MockRegistryService) {}, + setupMock: func(_ *mocksvc.MockRegistryService) {}, wantAddr: ":9090", wantReadTO: 5 * time.Second, wantWriteTO: 10 * time.Second, @@ -382,7 +384,7 @@ func TestBuildHTTPServer(t *testing.T) { writeTimeout: 30 * time.Second, idleTimeout: 120 * time.Second, }, - setupMock: func(_ *mocks.MockRegistryService) {}, + setupMock: func(_ *mocksvc.MockRegistryService) {}, wantAddr: "127.0.0.1:3000", wantReadTO: 20 * time.Second, wantWriteTO: 30 * time.Second, @@ -397,7 +399,7 @@ func TestBuildHTTPServer(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockSvc := mocks.NewMockRegistryService(ctrl) + mockSvc := mocksvc.NewMockRegistryService(ctrl) tt.setupMock(mockSvc) // Set auth middleware in config for tests @@ -441,7 +443,7 @@ func TestBuildServiceComponents(t *testing.T) { *testing.T, service.RegistryService, *registryAppConfig, - service.RegistryDataProvider, + inmemory.RegistryDataProvider, ) }{ { @@ -463,7 +465,7 @@ func TestBuildServiceComponents(t *testing.T) { t *testing.T, _ service.RegistryService, config *registryAppConfig, - originalProvider service.RegistryDataProvider, + originalProvider inmemory.RegistryDataProvider, ) { assert.NotNil( t, @@ -501,7 +503,7 @@ func TestBuildServiceComponents(t *testing.T) { t *testing.T, _ service.RegistryService, config *registryAppConfig, - originalProvider service.RegistryDataProvider, + originalProvider inmemory.RegistryDataProvider, ) { assert.Equal( t, @@ -534,7 +536,7 @@ func TestBuildServiceComponents(t *testing.T) { t *testing.T, _ service.RegistryService, config *registryAppConfig, - originalProvider service.RegistryDataProvider, + originalProvider inmemory.RegistryDataProvider, ) { assert.Equal( t, diff --git a/internal/service/file_provider.go b/internal/service/inmemory/file_provider.go similarity index 97% rename from internal/service/file_provider.go rename to internal/service/inmemory/file_provider.go index 3d451bb8..14aead44 100644 --- a/internal/service/file_provider.go +++ b/internal/service/inmemory/file_provider.go @@ -1,5 +1,5 @@ -// Package service provides the business logic for the MCP registry API -package service +// Package inmemory provides the business logic for the MCP registry API +package inmemory import ( "context" diff --git a/internal/service/file_provider_test.go b/internal/service/inmemory/file_provider_test.go similarity index 99% rename from internal/service/file_provider_test.go rename to internal/service/inmemory/file_provider_test.go index 40ad884e..e2138414 100644 --- a/internal/service/file_provider_test.go +++ b/internal/service/inmemory/file_provider_test.go @@ -1,4 +1,4 @@ -package service +package inmemory import ( "context" diff --git a/internal/service/inmemory/impl.go b/internal/service/inmemory/impl.go index e180502a..a67f5ef8 100644 --- a/internal/service/inmemory/impl.go +++ b/internal/service/inmemory/impl.go @@ -22,7 +22,7 @@ import ( // regSvc implements the RegistryService interface type regSvc struct { mu sync.RWMutex // Protects registryData, lastFetch - registryProvider service.RegistryDataProvider + registryProvider RegistryDataProvider config *config.Config // Config for registry validation // Map of registry name -> registry data @@ -58,7 +58,7 @@ func WithConfig(cfg *config.Config) Option { // deploymentProvider can be nil if deployed servers functionality is not needed. func New( ctx context.Context, - registryProvider service.RegistryDataProvider, + registryProvider RegistryDataProvider, opts ...Option, ) (service.RegistryService, error) { if registryProvider == nil { diff --git a/internal/service/inmemory/impl_test.go b/internal/service/inmemory/impl_test.go index 55e722a0..31b3e76e 100644 --- a/internal/service/inmemory/impl_test.go +++ b/internal/service/inmemory/impl_test.go @@ -13,7 +13,7 @@ import ( "github.com/stacklok/toolhive-registry-server/internal/config" "github.com/stacklok/toolhive-registry-server/internal/service" - "github.com/stacklok/toolhive-registry-server/internal/service/mocks" + "github.com/stacklok/toolhive-registry-server/internal/service/inmemory/mocks" ) func TestValidateManagedRegistry(t *testing.T) { diff --git a/internal/service/mocks/mock_provider.go b/internal/service/inmemory/mocks/mock_provider.go similarity index 100% rename from internal/service/mocks/mock_provider.go rename to internal/service/inmemory/mocks/mock_provider.go diff --git a/internal/service/provider.go b/internal/service/inmemory/provider.go similarity index 92% rename from internal/service/provider.go rename to internal/service/inmemory/provider.go index aa0ed518..55e9098c 100644 --- a/internal/service/provider.go +++ b/internal/service/inmemory/provider.go @@ -1,5 +1,4 @@ -// Package service provides the business logic for the MCP registry API -package service +package inmemory import ( "context" diff --git a/internal/service/inmemory/service_test.go b/internal/service/inmemory/service_test.go index 5c80b683..78c92bc9 100644 --- a/internal/service/inmemory/service_test.go +++ b/internal/service/inmemory/service_test.go @@ -16,7 +16,7 @@ import ( "github.com/stacklok/toolhive-registry-server/internal/registry" "github.com/stacklok/toolhive-registry-server/internal/service" "github.com/stacklok/toolhive-registry-server/internal/service/inmemory" - "github.com/stacklok/toolhive-registry-server/internal/service/mocks" + "github.com/stacklok/toolhive-registry-server/internal/service/inmemory/mocks" ) // testManagedConfig creates a config with a managed registry for testing write operations diff --git a/internal/service/mocks/mock_provider_factory.go b/internal/service/mocks/mock_provider_factory.go deleted file mode 100644 index f7f1dd15..00000000 --- a/internal/service/mocks/mock_provider_factory.go +++ /dev/null @@ -1,57 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: provider_factory.go -// -// Generated by this command: -// -// mockgen -destination=mocks/mock_provider_factory.go -package=mocks -source=provider_factory.go RegistryProviderFactory -// - -// Package mocks is a generated GoMock package. -package mocks - -import ( - reflect "reflect" - - config "github.com/stacklok/toolhive-registry-server/internal/config" - service "github.com/stacklok/toolhive-registry-server/internal/service" - gomock "go.uber.org/mock/gomock" -) - -// MockRegistryProviderFactory is a mock of RegistryProviderFactory interface. -type MockRegistryProviderFactory struct { - ctrl *gomock.Controller - recorder *MockRegistryProviderFactoryMockRecorder - isgomock struct{} -} - -// MockRegistryProviderFactoryMockRecorder is the mock recorder for MockRegistryProviderFactory. -type MockRegistryProviderFactoryMockRecorder struct { - mock *MockRegistryProviderFactory -} - -// NewMockRegistryProviderFactory creates a new mock instance. -func NewMockRegistryProviderFactory(ctrl *gomock.Controller) *MockRegistryProviderFactory { - mock := &MockRegistryProviderFactory{ctrl: ctrl} - mock.recorder = &MockRegistryProviderFactoryMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockRegistryProviderFactory) EXPECT() *MockRegistryProviderFactoryMockRecorder { - return m.recorder -} - -// CreateProvider mocks base method. -func (m *MockRegistryProviderFactory) CreateProvider(cfg *config.Config) (service.RegistryDataProvider, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateProvider", cfg) - ret0, _ := ret[0].(service.RegistryDataProvider) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// CreateProvider indicates an expected call of CreateProvider. -func (mr *MockRegistryProviderFactoryMockRecorder) CreateProvider(cfg any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateProvider", reflect.TypeOf((*MockRegistryProviderFactory)(nil).CreateProvider), cfg) -} diff --git a/internal/service/provider_factory.go b/internal/service/provider_factory.go deleted file mode 100644 index 254f9064..00000000 --- a/internal/service/provider_factory.go +++ /dev/null @@ -1,53 +0,0 @@ -// Package service provides the business logic for the MCP registry API -package service - -import ( - "fmt" - "log/slog" - - "github.com/stacklok/toolhive-registry-server/internal/config" - "github.com/stacklok/toolhive-registry-server/internal/sources" -) - -//go:generate mockgen -destination=mocks/mock_provider_factory.go -package=mocks -source=provider_factory.go RegistryProviderFactory - -// RegistryProviderFactory creates registry data providers based on configuration -type RegistryProviderFactory interface { - // CreateProvider creates a registry data provider based on the provided configuration - CreateProvider(cfg *config.Config) (RegistryDataProvider, error) -} - -// defaultRegistryProviderFactory is the default implementation of RegistryProviderFactory -type defaultRegistryProviderFactory struct { - storageManager sources.StorageManager -} - -var _ RegistryProviderFactory = (*defaultRegistryProviderFactory)(nil) - -// NewRegistryProviderFactory creates a new default registry provider factory -func NewRegistryProviderFactory(storageManager sources.StorageManager) RegistryProviderFactory { - return &defaultRegistryProviderFactory{ - storageManager: storageManager, - } -} - -// CreateProvider implements RegistryProviderFactory.CreateProvider -func (f *defaultRegistryProviderFactory) CreateProvider(cfg *config.Config) (RegistryDataProvider, error) { - if cfg == nil { - return nil, fmt.Errorf("config cannot be nil") - } - - storageType := cfg.GetStorageType() - - switch storageType { - case config.StorageTypeFile: - return NewFileRegistryDataProvider(f.storageManager, cfg), nil - case config.StorageTypeDatabase: - // Database provider is not yet implemented - // When implemented, this will create a database-backed provider - slog.Warn("Database storage type not yet supported, falling back to file storage") - return NewFileRegistryDataProvider(f.storageManager, cfg), nil - default: - return nil, fmt.Errorf("unsupported storage type: %s", storageType) - } -} diff --git a/internal/service/provider_factory_test.go b/internal/service/provider_factory_test.go deleted file mode 100644 index 5b92bad6..00000000 --- a/internal/service/provider_factory_test.go +++ /dev/null @@ -1,141 +0,0 @@ -package service - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.uber.org/mock/gomock" - - "github.com/stacklok/toolhive-registry-server/internal/config" - sourcesmocks "github.com/stacklok/toolhive-registry-server/internal/sources/mocks" -) - -func TestNewRegistryProviderFactory(t *testing.T) { - t.Parallel() - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - mockStorageManager := sourcesmocks.NewMockStorageManager(ctrl) - factory := NewRegistryProviderFactory(mockStorageManager) - - require.NotNil(t, factory) - - // Verify that the factory has the storage manager injected - concreteFactory, ok := factory.(*defaultRegistryProviderFactory) - require.True(t, ok) - assert.NotNil(t, concreteFactory.storageManager) -} - -func TestRegistryProviderFactory_CreateProvider(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - config *config.Config - expectError bool - errorContains string - }{ - { - name: "success with single file registry", - config: &config.Config{ - RegistryName: "test-registry", - Registries: []config.RegistryConfig{ - { - Name: "registry-1", - File: &config.FileConfig{Path: "/path/to/file.json"}, - }, - }, - }, - expectError: false, - }, - { - name: "success with multiple registries", - config: &config.Config{ - RegistryName: "test-registry", - Registries: []config.RegistryConfig{ - { - Name: "registry-1", - File: &config.FileConfig{Path: "/path/to/file1.json"}, - }, - { - Name: "registry-2", - Git: &config.GitConfig{Repository: "https://github.com/test/repo.git"}, - }, - }, - }, - expectError: false, - }, - { - name: "success with git registry", - config: &config.Config{ - RegistryName: "test-registry", - Registries: []config.RegistryConfig{ - { - Name: "registry-1", - Git: &config.GitConfig{ - Repository: "https://github.com/test/repo.git", - Branch: "main", - }, - }, - }, - }, - expectError: false, - }, - { - name: "success with api registry", - config: &config.Config{ - RegistryName: "test-registry", - Registries: []config.RegistryConfig{ - { - Name: "registry-1", - API: &config.APIConfig{Endpoint: "https://api.example.com"}, - }, - }, - }, - expectError: false, - }, - { - name: "success with empty registries", - config: &config.Config{ - RegistryName: "test-registry", - Registries: []config.RegistryConfig{}, - }, - expectError: false, - }, - { - name: "error with nil config", - config: nil, - expectError: true, - errorContains: "config cannot be nil", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - mockStorageManager := sourcesmocks.NewMockStorageManager(ctrl) - factory := NewRegistryProviderFactory(mockStorageManager) - - provider, err := factory.CreateProvider(tt.config) - - if tt.expectError { - assert.Error(t, err) - if tt.errorContains != "" { - assert.Contains(t, err.Error(), tt.errorContains) - } - assert.Nil(t, provider) - } else { - assert.NoError(t, err) - require.NotNil(t, provider) - // Verify the provider is a file registry data provider - _, ok := provider.(*fileRegistryDataProvider) - assert.True(t, ok, "provider should be a fileRegistryDataProvider") - } - }) - } -}