Add registry validators (#350)
Fixes #96 ## Motivation and Context See #96 ## How Has This Been Tested? Has some unit and integration tests, still needs proper testing of the different registry types ## Breaking Changes Yes, new package uploads will need extra verification ## Types of changes <!-- What types of changes does your code introduce? Put an `x` in all the boxes that apply: --> - [ ] Bug fix (non-breaking change which fixes an issue) - [x] New feature (non-breaking change which adds functionality) - [x] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Documentation update
This commit is contained in:
parent
6ea3828e3c
commit
694a4d8879
8 changed files with 1859 additions and 728 deletions
|
|
@ -11,7 +11,6 @@ import (
|
|||
"github.com/modelcontextprotocol/registry/internal/config"
|
||||
"github.com/modelcontextprotocol/registry/internal/database"
|
||||
"github.com/modelcontextprotocol/registry/internal/service"
|
||||
"github.com/modelcontextprotocol/registry/internal/validators"
|
||||
apiv0 "github.com/modelcontextprotocol/registry/pkg/api/v0"
|
||||
"github.com/modelcontextprotocol/registry/pkg/model"
|
||||
)
|
||||
|
|
@ -67,11 +66,6 @@ func RegisterEditEndpoints(api huma.API, registry service.RegistryService, cfg *
|
|||
return nil, huma.Error403Forbidden("You do not have edit permissions for this server")
|
||||
}
|
||||
|
||||
// Perform all schema validation
|
||||
if err := validators.ValidatePublishRequest(input.Body); err != nil {
|
||||
return nil, huma.Error400BadRequest(err.Error())
|
||||
}
|
||||
|
||||
// Prevent renaming servers
|
||||
if currentServer.Name != input.Body.Name {
|
||||
return nil, huma.Error400BadRequest("Cannot rename server")
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ import (
|
|||
|
||||
func TestEditServerEndpoint(t *testing.T) {
|
||||
// Create registry service and insert a common test server
|
||||
registryService := service.NewRegistryServiceWithDB(database.NewMemoryDB())
|
||||
|
||||
registryService := service.NewRegistryService(database.NewMemoryDB(), config.NewConfig())
|
||||
|
||||
// Publish a test server that will be used across test cases
|
||||
testServer := apiv0.ServerJSON{
|
||||
Name: "io.github.domdomegg/test-server",
|
||||
|
|
@ -43,9 +43,9 @@ func TestEditServerEndpoint(t *testing.T) {
|
|||
assert.NotNil(t, published)
|
||||
assert.NotNil(t, published.Meta)
|
||||
assert.NotNil(t, published.Meta.IOModelContextProtocolRegistry)
|
||||
|
||||
|
||||
testServerID := published.Meta.IOModelContextProtocolRegistry.ID
|
||||
|
||||
|
||||
// Publish a second server for permission testing
|
||||
otherServer := apiv0.ServerJSON{
|
||||
Name: "io.github.other/test-server",
|
||||
|
|
@ -65,9 +65,9 @@ func TestEditServerEndpoint(t *testing.T) {
|
|||
assert.NotNil(t, otherPublished)
|
||||
assert.NotNil(t, otherPublished.Meta)
|
||||
assert.NotNil(t, otherPublished.Meta.IOModelContextProtocolRegistry)
|
||||
|
||||
|
||||
otherServerID := otherPublished.Meta.IOModelContextProtocolRegistry.ID
|
||||
|
||||
|
||||
// Publish a deleted server for undelete testing
|
||||
deletedServer := apiv0.ServerJSON{
|
||||
Name: "io.github.domdomegg/deleted-server",
|
||||
|
|
@ -87,7 +87,7 @@ func TestEditServerEndpoint(t *testing.T) {
|
|||
assert.NotNil(t, deletedPublished)
|
||||
assert.NotNil(t, deletedPublished.Meta)
|
||||
assert.NotNil(t, deletedPublished.Meta.IOModelContextProtocolRegistry)
|
||||
|
||||
|
||||
deletedServerID := deletedPublished.Meta.IOModelContextProtocolRegistry.ID
|
||||
|
||||
testCases := []struct {
|
||||
|
|
@ -143,9 +143,9 @@ func TestEditServerEndpoint(t *testing.T) {
|
|||
Description: "Test server",
|
||||
VersionDetail: model.VersionDetail{Version: "1.0.0"},
|
||||
},
|
||||
serverID: testServerID,
|
||||
expectedStatus: http.StatusUnauthorized,
|
||||
expectedError: "Unauthorized",
|
||||
serverID: testServerID,
|
||||
expectedStatus: http.StatusUnauthorized,
|
||||
expectedError: "Unauthorized",
|
||||
},
|
||||
{
|
||||
name: "invalid token",
|
||||
|
|
@ -155,12 +155,12 @@ func TestEditServerEndpoint(t *testing.T) {
|
|||
Description: "Test server",
|
||||
VersionDetail: model.VersionDetail{Version: "1.0.0"},
|
||||
},
|
||||
serverID: testServerID,
|
||||
expectedStatus: http.StatusUnauthorized,
|
||||
expectedError: "Unauthorized",
|
||||
serverID: testServerID,
|
||||
expectedStatus: http.StatusUnauthorized,
|
||||
expectedError: "Unauthorized",
|
||||
},
|
||||
{
|
||||
name: "permission denied - no edit permissions",
|
||||
name: "permission denied - no edit permissions",
|
||||
authHeader: func() string {
|
||||
cfg := &config.Config{JWTPrivateKey: "bb2c6b424005acd5df47a9e2c87f446def86dd740c888ea3efb825b23f7ef47c"}
|
||||
token, _ := generateTestJWTToken(cfg, auth.JWTClaims{
|
||||
|
|
@ -182,7 +182,7 @@ func TestEditServerEndpoint(t *testing.T) {
|
|||
expectedError: "Forbidden",
|
||||
},
|
||||
{
|
||||
name: "permission denied - wrong resource",
|
||||
name: "permission denied - wrong resource",
|
||||
authHeader: func() string {
|
||||
cfg := &config.Config{JWTPrivateKey: "bb2c6b424005acd5df47a9e2c87f446def86dd740c888ea3efb825b23f7ef47c"}
|
||||
token, _ := generateTestJWTToken(cfg, auth.JWTClaims{
|
||||
|
|
@ -204,7 +204,7 @@ func TestEditServerEndpoint(t *testing.T) {
|
|||
expectedError: "Forbidden",
|
||||
},
|
||||
{
|
||||
name: "server not found",
|
||||
name: "server not found",
|
||||
authHeader: func() string {
|
||||
cfg := &config.Config{JWTPrivateKey: "bb2c6b424005acd5df47a9e2c87f446def86dd740c888ea3efb825b23f7ef47c"}
|
||||
token, _ := generateTestJWTToken(cfg, auth.JWTClaims{
|
||||
|
|
@ -226,7 +226,7 @@ func TestEditServerEndpoint(t *testing.T) {
|
|||
expectedError: "Not Found",
|
||||
},
|
||||
{
|
||||
name: "validation error - invalid server name",
|
||||
name: "validation error - invalid server name",
|
||||
authHeader: func() string {
|
||||
cfg := &config.Config{JWTPrivateKey: "bb2c6b424005acd5df47a9e2c87f446def86dd740c888ea3efb825b23f7ef47c"}
|
||||
token, _ := generateTestJWTToken(cfg, auth.JWTClaims{
|
||||
|
|
@ -248,7 +248,7 @@ func TestEditServerEndpoint(t *testing.T) {
|
|||
expectedError: "Bad Request",
|
||||
},
|
||||
{
|
||||
name: "cannot undelete server",
|
||||
name: "cannot undelete server",
|
||||
authHeader: func() string {
|
||||
cfg := &config.Config{JWTPrivateKey: "bb2c6b424005acd5df47a9e2c87f446def86dd740c888ea3efb825b23f7ef47c"}
|
||||
token, _ := generateTestJWTToken(cfg, auth.JWTClaims{
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import (
|
|||
"github.com/modelcontextprotocol/registry/internal/auth"
|
||||
"github.com/modelcontextprotocol/registry/internal/config"
|
||||
"github.com/modelcontextprotocol/registry/internal/service"
|
||||
"github.com/modelcontextprotocol/registry/internal/validators"
|
||||
apiv0 "github.com/modelcontextprotocol/registry/pkg/api/v0"
|
||||
)
|
||||
|
||||
|
|
@ -49,11 +48,6 @@ func RegisterPublishEndpoint(api huma.API, registry service.RegistryService, cfg
|
|||
return nil, huma.Error401Unauthorized("Invalid or expired Registry JWT token", err)
|
||||
}
|
||||
|
||||
// Perform all schema validation
|
||||
if err := validators.ValidatePublishRequest(input.Body); err != nil {
|
||||
return nil, huma.Error400BadRequest(err.Error())
|
||||
}
|
||||
|
||||
// Verify that the token has permission to publish the server
|
||||
if !jwtManager.HasPermission(input.Body.Name, auth.PermissionActionPublish, claims.Permissions) {
|
||||
return nil, huma.Error403Forbidden("You do not have permission to publish this server")
|
||||
|
|
|
|||
|
|
@ -36,17 +36,18 @@ func generateIntegrationTestJWTToken(cfg *config.Config, claims auth.JWTClaims)
|
|||
}
|
||||
|
||||
func TestPublishIntegration(t *testing.T) {
|
||||
// Setup fake service
|
||||
registryService := service.NewRegistryServiceWithDB(database.NewMemoryDB())
|
||||
|
||||
// Create test config with a valid Ed25519 seed
|
||||
// Create test config with a valid Ed25519 seed and validation disabled for testing
|
||||
testSeed := make([]byte, ed25519.SeedSize)
|
||||
_, err := rand.Read(testSeed)
|
||||
require.NoError(t, err)
|
||||
testConfig := &config.Config{
|
||||
JWTPrivateKey: hex.EncodeToString(testSeed),
|
||||
JWTPrivateKey: hex.EncodeToString(testSeed),
|
||||
EnableRegistryValidation: false, // Disable for integration tests
|
||||
}
|
||||
|
||||
// Setup fake service
|
||||
registryService := service.NewRegistryService(database.NewMemoryDB(), testConfig)
|
||||
|
||||
// Create a new ServeMux and Huma API
|
||||
mux := http.NewServeMux()
|
||||
api := humago.New(mux, huma.DefaultConfig("Test API", "1.0.0"))
|
||||
|
|
@ -219,4 +220,53 @@ func TestPublishIntegration(t *testing.T) {
|
|||
assert.Equal(t, http.StatusForbidden, rr.Code)
|
||||
assert.Contains(t, rr.Body.String(), "You do not have permission to publish this server")
|
||||
})
|
||||
|
||||
t.Run("publish succeeds with MCPB package", func(t *testing.T) {
|
||||
publishReq := apiv0.ServerJSON{
|
||||
Name: "io.github.domdomegg/airtable-mcp-server",
|
||||
Description: "A test server with MCPB package",
|
||||
VersionDetail: model.VersionDetail{
|
||||
Version: "1.7.2",
|
||||
},
|
||||
Status: model.StatusActive,
|
||||
Packages: []model.Package{
|
||||
{
|
||||
RegistryType: model.RegistryTypeMCPB,
|
||||
Identifier: "github.com/domdomegg/airtable-mcp-server/releases/download/v1.7.2/airtable-mcp-server.mcpb",
|
||||
Version: "1.7.2",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Generate valid JWT token with wildcard permission
|
||||
claims := auth.JWTClaims{
|
||||
AuthMethod: auth.MethodNone,
|
||||
Permissions: []auth.Permission{
|
||||
{Action: auth.PermissionActionPublish, ResourcePattern: "*"},
|
||||
},
|
||||
}
|
||||
token, err := generateIntegrationTestJWTToken(testConfig, claims)
|
||||
require.NoError(t, err)
|
||||
|
||||
body, err := json.Marshal(publishReq)
|
||||
require.NoError(t, err)
|
||||
|
||||
req := httptest.NewRequest(http.MethodPost, "/v0/publish", bytes.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
mux.ServeHTTP(rr, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, rr.Code)
|
||||
|
||||
var response apiv0.ServerJSON
|
||||
err = json.Unmarshal(rr.Body.Bytes(), &response)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, publishReq.Name, response.Name)
|
||||
assert.Equal(t, publishReq.VersionDetail.Version, response.VersionDetail.Version)
|
||||
assert.Len(t, response.Packages, 1)
|
||||
assert.Equal(t, model.RegistryTypeMCPB, response.Packages[0].RegistryType)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
225
internal/api/handlers/v0/publish_registry_validation_test.go
Normal file
225
internal/api/handlers/v0/publish_registry_validation_test.go
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
package v0_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/danielgtaylor/huma/v2"
|
||||
"github.com/danielgtaylor/huma/v2/adapters/humago"
|
||||
v0 "github.com/modelcontextprotocol/registry/internal/api/handlers/v0"
|
||||
"github.com/modelcontextprotocol/registry/internal/auth"
|
||||
"github.com/modelcontextprotocol/registry/internal/config"
|
||||
"github.com/modelcontextprotocol/registry/internal/database"
|
||||
"github.com/modelcontextprotocol/registry/internal/service"
|
||||
apiv0 "github.com/modelcontextprotocol/registry/pkg/api/v0"
|
||||
"github.com/modelcontextprotocol/registry/pkg/model"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPublishRegistryValidation(t *testing.T) {
|
||||
// Create test config with validation ENABLED
|
||||
testSeed := make([]byte, ed25519.SeedSize)
|
||||
_, err := rand.Read(testSeed)
|
||||
require.NoError(t, err)
|
||||
testConfig := &config.Config{
|
||||
JWTPrivateKey: hex.EncodeToString(testSeed),
|
||||
EnableRegistryValidation: true, // Enable validation for this test
|
||||
}
|
||||
|
||||
// Setup fake service
|
||||
registryService := service.NewRegistryService(database.NewMemoryDB(), testConfig)
|
||||
|
||||
// Create a new ServeMux and Huma API
|
||||
mux := http.NewServeMux()
|
||||
api := humago.New(mux, huma.DefaultConfig("Test API", "1.0.0"))
|
||||
|
||||
// Register the endpoint
|
||||
v0.RegisterPublishEndpoint(api, registryService, testConfig)
|
||||
|
||||
t.Run("publish fails with npm registry validation error", func(t *testing.T) {
|
||||
publishReq := apiv0.ServerJSON{
|
||||
Name: "com.example/test-server-with-npm",
|
||||
Description: "A test server with invalid npm package reference",
|
||||
VersionDetail: model.VersionDetail{
|
||||
Version: "1.0.0",
|
||||
},
|
||||
Packages: []model.Package{
|
||||
{
|
||||
RegistryType: model.RegistryTypeNPM,
|
||||
Identifier: "nonexistent-npm-package-xyz123",
|
||||
Version: "1.0.0",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Generate valid JWT token with wildcard permission
|
||||
claims := auth.JWTClaims{
|
||||
AuthMethod: auth.MethodNone,
|
||||
Permissions: []auth.Permission{
|
||||
{Action: auth.PermissionActionPublish, ResourcePattern: "*"},
|
||||
},
|
||||
}
|
||||
token, err := generateTestJWTToken(testConfig, claims)
|
||||
require.NoError(t, err)
|
||||
|
||||
body, err := json.Marshal(publishReq)
|
||||
require.NoError(t, err)
|
||||
|
||||
req := httptest.NewRequest(http.MethodPost, "/v0/publish", bytes.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
mux.ServeHTTP(rr, req)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, rr.Code)
|
||||
assert.Contains(t, rr.Body.String(), "registry validation failed")
|
||||
})
|
||||
|
||||
t.Run("publish succeeds with MCPB package (registry validation enabled)", func(t *testing.T) {
|
||||
publishReq := apiv0.ServerJSON{
|
||||
Name: "com.example/test-server-mcpb-validation",
|
||||
Description: "A test server with MCPB package and registry validation enabled",
|
||||
VersionDetail: model.VersionDetail{
|
||||
Version: "0.0.36",
|
||||
},
|
||||
Packages: []model.Package{
|
||||
{
|
||||
RegistryType: model.RegistryTypeMCPB,
|
||||
Identifier: "https://github.com/microsoft/playwright-mcp/releases/download/v0.0.36/playwright-mcp-extension-v0.0.36.zip",
|
||||
Version: "0.0.36",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Generate valid JWT token with wildcard permission
|
||||
claims := auth.JWTClaims{
|
||||
AuthMethod: auth.MethodNone,
|
||||
Permissions: []auth.Permission{
|
||||
{Action: auth.PermissionActionPublish, ResourcePattern: "*"},
|
||||
},
|
||||
}
|
||||
token, err := generateTestJWTToken(testConfig, claims)
|
||||
require.NoError(t, err)
|
||||
|
||||
body, err := json.Marshal(publishReq)
|
||||
require.NoError(t, err)
|
||||
|
||||
req := httptest.NewRequest(http.MethodPost, "/v0/publish", bytes.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
mux.ServeHTTP(rr, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, rr.Code)
|
||||
|
||||
var response apiv0.ServerJSON
|
||||
err = json.Unmarshal(rr.Body.Bytes(), &response)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, publishReq.Name, response.Name)
|
||||
assert.Equal(t, publishReq.VersionDetail.Version, response.VersionDetail.Version)
|
||||
assert.Len(t, response.Packages, 1)
|
||||
assert.Equal(t, model.RegistryTypeMCPB, response.Packages[0].RegistryType)
|
||||
})
|
||||
|
||||
t.Run("publish fails when second package fails npm validation", func(t *testing.T) {
|
||||
publishReq := apiv0.ServerJSON{
|
||||
Name: "com.example/test-server-multiple-packages",
|
||||
Description: "A test server with multiple packages where second fails",
|
||||
VersionDetail: model.VersionDetail{
|
||||
Version: "1.0.0",
|
||||
},
|
||||
Packages: []model.Package{
|
||||
{
|
||||
RegistryType: model.RegistryTypeMCPB,
|
||||
Identifier: "https://github.com/microsoft/playwright-mcp/releases/download/v0.0.36/playwright-mcp-extension-v0.0.36.zip",
|
||||
Version: "1.0.0",
|
||||
},
|
||||
{
|
||||
RegistryType: model.RegistryTypeNPM,
|
||||
Identifier: "nonexistent-second-package-abc123",
|
||||
Version: "1.0.0",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Generate valid JWT token with wildcard permission
|
||||
claims := auth.JWTClaims{
|
||||
AuthMethod: auth.MethodNone,
|
||||
Permissions: []auth.Permission{
|
||||
{Action: auth.PermissionActionPublish, ResourcePattern: "*"},
|
||||
},
|
||||
}
|
||||
token, err := generateTestJWTToken(testConfig, claims)
|
||||
require.NoError(t, err)
|
||||
|
||||
body, err := json.Marshal(publishReq)
|
||||
require.NoError(t, err)
|
||||
|
||||
req := httptest.NewRequest(http.MethodPost, "/v0/publish", bytes.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
mux.ServeHTTP(rr, req)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, rr.Code)
|
||||
assert.Contains(t, rr.Body.String(), "registry validation failed for package 1")
|
||||
assert.Contains(t, rr.Body.String(), "nonexistent-second-package-abc123")
|
||||
})
|
||||
|
||||
t.Run("publish fails when first package fails validation", func(t *testing.T) {
|
||||
publishReq := apiv0.ServerJSON{
|
||||
Name: "com.example/test-server-first-package-fails",
|
||||
Description: "A test server where first package fails",
|
||||
VersionDetail: model.VersionDetail{
|
||||
Version: "1.0.0",
|
||||
},
|
||||
Packages: []model.Package{
|
||||
{
|
||||
RegistryType: model.RegistryTypeNPM,
|
||||
Identifier: "nonexistent-first-package-xyz789",
|
||||
Version: "1.0.0",
|
||||
},
|
||||
{
|
||||
RegistryType: model.RegistryTypeMCPB,
|
||||
Identifier: "https://github.com/microsoft/playwright-mcp/releases/download/v0.0.36/playwright-mcp-extension-v0.0.36.zip",
|
||||
Version: "1.0.0",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Generate valid JWT token with wildcard permission
|
||||
claims := auth.JWTClaims{
|
||||
AuthMethod: auth.MethodNone,
|
||||
Permissions: []auth.Permission{
|
||||
{Action: auth.PermissionActionPublish, ResourcePattern: "*"},
|
||||
},
|
||||
}
|
||||
token, err := generateTestJWTToken(testConfig, claims)
|
||||
require.NoError(t, err)
|
||||
|
||||
body, err := json.Marshal(publishReq)
|
||||
require.NoError(t, err)
|
||||
|
||||
req := httptest.NewRequest(http.MethodPost, "/v0/publish", bytes.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
mux.ServeHTTP(rr, req)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, rr.Code)
|
||||
assert.Contains(t, rr.Body.String(), "registry validation failed for package 0")
|
||||
assert.Contains(t, rr.Body.String(), "nonexistent-first-package-xyz789")
|
||||
})
|
||||
}
|
||||
|
|
@ -24,7 +24,6 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
||||
// Helper function to generate a valid JWT token for testing
|
||||
func generateTestJWTToken(cfg *config.Config, claims auth.JWTClaims) (string, error) {
|
||||
jwtManager := auth.NewJWTManager(cfg)
|
||||
|
|
@ -41,17 +40,18 @@ func TestPublishEndpoint(t *testing.T) {
|
|||
_, err := rand.Read(testSeed)
|
||||
require.NoError(t, err)
|
||||
testConfig := &config.Config{
|
||||
JWTPrivateKey: hex.EncodeToString(testSeed),
|
||||
JWTPrivateKey: hex.EncodeToString(testSeed),
|
||||
EnableRegistryValidation: false, // Disable for unit tests
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
requestBody interface{}
|
||||
tokenClaims *auth.JWTClaims
|
||||
authHeader string
|
||||
name string
|
||||
requestBody interface{}
|
||||
tokenClaims *auth.JWTClaims
|
||||
authHeader string
|
||||
setupRegistryService func(service.RegistryService)
|
||||
expectedStatus int
|
||||
expectedError string
|
||||
expectedStatus int
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "successful publish with GitHub auth",
|
||||
|
|
@ -105,9 +105,9 @@ func TestPublishEndpoint(t *testing.T) {
|
|||
expectedStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "missing authorization header",
|
||||
requestBody: apiv0.ServerJSON{},
|
||||
authHeader: "", // Empty auth header
|
||||
name: "missing authorization header",
|
||||
requestBody: apiv0.ServerJSON{},
|
||||
authHeader: "", // Empty auth header
|
||||
setupRegistryService: func(_ service.RegistryService) {
|
||||
// Empty registry - no setup needed
|
||||
},
|
||||
|
|
@ -121,7 +121,7 @@ func TestPublishEndpoint(t *testing.T) {
|
|||
Description: "Test server",
|
||||
VersionDetail: model.VersionDetail{Version: "1.0.0"},
|
||||
},
|
||||
authHeader: "InvalidFormat",
|
||||
authHeader: "InvalidFormat",
|
||||
setupRegistryService: func(_ service.RegistryService) {
|
||||
// Empty registry - no setup needed
|
||||
},
|
||||
|
|
@ -137,7 +137,7 @@ func TestPublishEndpoint(t *testing.T) {
|
|||
Version: "1.0.0",
|
||||
},
|
||||
},
|
||||
authHeader: "Bearer invalidToken",
|
||||
authHeader: "Bearer invalidToken",
|
||||
setupRegistryService: func(_ service.RegistryService) {
|
||||
// Empty registry - no setup needed
|
||||
},
|
||||
|
|
@ -209,13 +209,38 @@ func TestPublishEndpoint(t *testing.T) {
|
|||
expectedStatus: http.StatusBadRequest,
|
||||
expectedError: "invalid version: cannot publish duplicate version",
|
||||
},
|
||||
{
|
||||
name: "package validation success - MCPB package",
|
||||
requestBody: apiv0.ServerJSON{
|
||||
Name: "com.example/test-server-mcpb",
|
||||
Description: "A test server with MCPB package",
|
||||
VersionDetail: model.VersionDetail{
|
||||
Version: "1.0.0",
|
||||
},
|
||||
Packages: []model.Package{
|
||||
{
|
||||
RegistryType: model.RegistryTypeMCPB,
|
||||
Identifier: "https://github.com/example/server/releases/download/v1.0.0/server.tar.gz",
|
||||
Version: "1.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
tokenClaims: &auth.JWTClaims{
|
||||
AuthMethod: auth.MethodNone,
|
||||
Permissions: []auth.Permission{
|
||||
{Action: auth.PermissionActionPublish, ResourcePattern: "*"},
|
||||
},
|
||||
},
|
||||
setupRegistryService: func(_ service.RegistryService) {},
|
||||
expectedStatus: http.StatusOK,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Create registry service
|
||||
registryService := service.NewRegistryServiceWithDB(database.NewMemoryDB())
|
||||
|
||||
registryService := service.NewRegistryService(database.NewMemoryDB(), testConfig)
|
||||
|
||||
// Setup registry service
|
||||
tc.setupRegistryService(registryService)
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/danielgtaylor/huma/v2/adapters/humago"
|
||||
"github.com/google/uuid"
|
||||
v0 "github.com/modelcontextprotocol/registry/internal/api/handlers/v0"
|
||||
"github.com/modelcontextprotocol/registry/internal/config"
|
||||
"github.com/modelcontextprotocol/registry/internal/database"
|
||||
"github.com/modelcontextprotocol/registry/internal/service"
|
||||
apiv0 "github.com/modelcontextprotocol/registry/pkg/api/v0"
|
||||
|
|
@ -131,7 +132,7 @@ func TestServersListEndpoint(t *testing.T) {
|
|||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Create mock registry service
|
||||
registryService := service.NewRegistryServiceWithDB(database.NewMemoryDB())
|
||||
registryService := service.NewRegistryService(database.NewMemoryDB(), config.NewConfig())
|
||||
tc.setupRegistryService(registryService)
|
||||
|
||||
// Create a new test API
|
||||
|
|
@ -202,7 +203,7 @@ func TestServersListEndpoint(t *testing.T) {
|
|||
|
||||
func TestServersDetailEndpoint(t *testing.T) {
|
||||
// Create mock registry service
|
||||
registryService := service.NewRegistryServiceWithDB(database.NewMemoryDB())
|
||||
registryService := service.NewRegistryService(database.NewMemoryDB(), config.NewConfig())
|
||||
|
||||
testServer, err := registryService.Publish(apiv0.ServerJSON{
|
||||
Name: "com.example/test-server",
|
||||
|
|
@ -284,7 +285,7 @@ func TestServersDetailEndpoint(t *testing.T) {
|
|||
// TestServersEndpointsIntegration tests the servers endpoints with actual HTTP requests
|
||||
func TestServersEndpointsIntegration(t *testing.T) {
|
||||
// Create mock registry service
|
||||
registryService := service.NewRegistryServiceWithDB(database.NewMemoryDB())
|
||||
registryService := service.NewRegistryService(database.NewMemoryDB(), config.NewConfig())
|
||||
|
||||
// Test data - publish a server and get its actual ID
|
||||
testServer := apiv0.ServerJSON{
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import (
|
|||
)
|
||||
|
||||
func TestPrometheusHandler(t *testing.T) {
|
||||
registryService := service.NewRegistryServiceWithDB(database.NewMemoryDB())
|
||||
registryService := service.NewRegistryService(database.NewMemoryDB(), config.NewConfig())
|
||||
server, err := registryService.Publish(apiv0.ServerJSON{
|
||||
Name: "io.github.example/test-server",
|
||||
Description: "Test server detail",
|
||||
|
|
|
|||
Loading…
Reference in a new issue