update tests and memory implementation to follow real DB implementations as much as possible. Fix the tests to let the DB generate the server uuids rather than setting them up in the test.

This commit is contained in:
Avinash Sridhar 2025-05-27 10:55:48 -04:00 • committed by Toby Padilla
commit f9ab0bc49b
2 changed files with 118 additions and 125 deletions

View file

@ -8,9 +8,7 @@ import (
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/google/uuid"
v0 "github.com/modelcontextprotocol/registry/internal/api/handlers/v0"
"github.com/modelcontextprotocol/registry/internal/auth"
"github.com/modelcontextprotocol/registry/internal/model"
@ -59,53 +57,52 @@ func TestPublishIntegration(t *testing.T) {
handler := v0.PublishHandler(registryService, authService)
t.Run("successful publish with GitHub auth", func(t *testing.T) {
serverDetail := &model.ServerDetail{
Server: model.Server{
ID: uuid.New().String(),
Name: "io.github.testuser/test-mcp-server",
Description: "A test MCP server for integration testing",
Repository: model.Repository{
URL: "https://github.com/testuser/test-mcp-server",
Source: "github",
ID: "testuser/test-mcp-server",
publishReq := model.PublishRequest{
ServerDetail: model.ServerDetail{
Server: model.Server{
Name: "io.github.testuser/test-mcp-server",
Description: "A test MCP server for integration testing",
Repository: model.Repository{
URL: "https://github.com/testuser/test-mcp-server",
Source: "github",
ID: "testuser/test-mcp-server",
},
VersionDetail: model.VersionDetail{
Version: "1.0.0",
},
},
VersionDetail: model.VersionDetail{
Version: "1.0.0",
ReleaseDate: time.Now().Format(time.RFC3339),
IsLatest: true,
},
},
Packages: []model.Package{
{
RegistryName: "npm",
Name: "test-mcp-server",
Version: "1.0.0",
RunTimeHint: "node",
RuntimeArguments: []model.Argument{
{
Type: model.ArgumentTypeNamed,
Name: "config",
InputWithVariables: model.InputWithVariables{
Input: model.Input{
Description: "Configuration file path",
Format: model.FormatFilePath,
IsRequired: true,
Packages: []model.Package{
{
RegistryName: "npm",
Name: "test-mcp-server",
Version: "1.0.0",
RunTimeHint: "node",
RuntimeArguments: []model.Argument{
{
Type: model.ArgumentTypeNamed,
Name: "config",
InputWithVariables: model.InputWithVariables{
Input: model.Input{
Description: "Configuration file path",
Format: model.FormatFilePath,
IsRequired: true,
},
},
},
},
},
},
},
Remotes: []model.Remote{
{
TransportType: "http",
URL: "http://localhost:3000/mcp",
Remotes: []model.Remote{
{
TransportType: "http",
URL: "http://localhost:3000/mcp",
},
},
},
}
// Marshal the server detail to JSON
jsonData, err := json.Marshal(serverDetail)
jsonData, err := json.Marshal(publishReq)
require.NoError(t, err)
// Create a request
@ -127,38 +124,37 @@ func TestPublishIntegration(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, "Server publication successful", response["message"])
assert.Equal(t, serverDetail.ID, response["id"])
assert.NotEmpty(t, response["id"], "Server ID should be generated")
// Verify the server was actually published by retrieving it
publishedServer, err := registryService.GetByID(serverDetail.ID)
publishedServer, err := registryService.GetByID(response["id"])
require.NoError(t, err)
assert.Equal(t, serverDetail.Name, publishedServer.Name)
assert.Equal(t, serverDetail.Description, publishedServer.Description)
assert.Equal(t, serverDetail.VersionDetail.Version, publishedServer.VersionDetail.Version)
assert.Equal(t, publishReq.ServerDetail.Name, publishedServer.Name)
assert.Equal(t, publishReq.ServerDetail.Description, publishedServer.Description)
assert.Equal(t, publishReq.ServerDetail.VersionDetail.Version, publishedServer.VersionDetail.Version)
assert.Len(t, publishedServer.Packages, 1)
assert.Len(t, publishedServer.Remotes, 1)
})
t.Run("successful publish without auth (no prefix)", func(t *testing.T) {
serverDetail := &model.ServerDetail{
Server: model.Server{
ID: uuid.New().String(),
Name: "custom-mcp-server",
Description: "A custom MCP server without auth",
Repository: model.Repository{
URL: "https://example.com/custom-server",
Source: "custom",
ID: "custom/custom-server",
},
VersionDetail: model.VersionDetail{
Version: "2.0.0",
ReleaseDate: time.Now().Format(time.RFC3339),
IsLatest: true,
publishReq := &model.PublishRequest{
ServerDetail: model.ServerDetail{
Server: model.Server{
Name: "custom-mcp-server",
Description: "A custom MCP server without auth",
Repository: model.Repository{
URL: "https://example.com/custom-server",
Source: "custom",
ID: "custom/custom-server",
},
VersionDetail: model.VersionDetail{
Version: "2.0.0",
},
},
},
}
jsonData, err := json.Marshal(serverDetail)
jsonData, err := json.Marshal(publishReq)
require.NoError(t, err)
req := httptest.NewRequest(http.MethodPost, "/v0/publish", bytes.NewBuffer(jsonData))
@ -175,24 +171,23 @@ func TestPublishIntegration(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, "Server publication successful", response["message"])
assert.Equal(t, serverDetail.ID, response["id"])
assert.NotEmpty(t, response["id"], "Server ID should be generated")
})
t.Run("publish fails with missing name", func(t *testing.T) {
serverDetail := &model.ServerDetail{
Server: model.Server{
ID: uuid.New().String(),
Name: "", // Missing name
Description: "A test server",
VersionDetail: model.VersionDetail{
Version: "1.0.0",
ReleaseDate: time.Now().Format(time.RFC3339),
IsLatest: true,
publishReq := &model.PublishRequest{
ServerDetail: model.ServerDetail{
Server: model.Server{
Name: "", // Missing name
Description: "A test server",
VersionDetail: model.VersionDetail{
Version: "1.0.0",
},
},
},
}
jsonData, err := json.Marshal(serverDetail)
jsonData, err := json.Marshal(publishReq)
require.NoError(t, err)
req := httptest.NewRequest(http.MethodPost, "/v0/publish", bytes.NewBuffer(jsonData))
@ -209,13 +204,10 @@ func TestPublishIntegration(t *testing.T) {
t.Run("publish fails with missing version", func(t *testing.T) {
serverDetail := &model.ServerDetail{
Server: model.Server{
ID: uuid.New().String(),
Name: "test-server",
Description: "A test server",
VersionDetail: model.VersionDetail{
Version: "", // Missing version
ReleaseDate: time.Now().Format(time.RFC3339),
IsLatest: true,
Version: "", // Missing version
},
},
}
@ -237,13 +229,10 @@ func TestPublishIntegration(t *testing.T) {
t.Run("publish fails with missing authorization header", func(t *testing.T) {
serverDetail := &model.ServerDetail{
Server: model.Server{
ID: uuid.New().String(),
Name: "test-server",
Description: "A test server",
VersionDetail: model.VersionDetail{
Version: "1.0.0",
ReleaseDate: time.Now().Format(time.RFC3339),
IsLatest: true,
Version: "1.0.0",
},
},
}
@ -291,7 +280,6 @@ func TestPublishIntegration(t *testing.T) {
// First, publish a server successfully
firstServerDetail := &model.ServerDetail{
Server: model.Server{
ID: uuid.New().String(),
Name: "io.github.duplicate/test-server",
Description: "First server for duplicate test",
Repository: model.Repository{
@ -300,9 +288,7 @@ func TestPublishIntegration(t *testing.T) {
ID: "duplicate/test-server",
},
VersionDetail: model.VersionDetail{
Version: "1.0.0",
ReleaseDate: time.Now().Format(time.RFC3339),
IsLatest: true,
Version: "1.0.0",
},
},
}
@ -317,12 +303,17 @@ func TestPublishIntegration(t *testing.T) {
recorder := httptest.NewRecorder()
handler(recorder, req)
var response map[string]string
err = json.Unmarshal(recorder.Body.Bytes(), &response)
require.NoError(t, err)
assert.Equal(t, http.StatusCreated, recorder.Code, "First publish should succeed")
firstServerDetail.ID = response["id"] // Store the ID for later verification
// Now try to publish another server with the same name and version
duplicateServerDetail := &model.ServerDetail{
Server: model.Server{
ID: uuid.New().String(), // Different ID
Name: "io.github.duplicate/test-server", // Same name
Description: "Duplicate server attempt",
Repository: model.Repository{
@ -331,9 +322,7 @@ func TestPublishIntegration(t *testing.T) {
ID: "duplicate/test-server-fork",
},
VersionDetail: model.VersionDetail{
Version: "1.0.0", // Same version
ReleaseDate: time.Now().Format(time.RFC3339),
IsLatest: true,
Version: "1.0.0", // Same version
},
},
}
@ -358,16 +347,12 @@ func TestPublishIntegration(t *testing.T) {
assert.Equal(t, firstServerDetail.Name, retrievedServer.Name)
assert.Equal(t, firstServerDetail.Description, retrievedServer.Description)
// Try to get the duplicate - it should not exist
_, err = registryService.GetByID(duplicateServerDetail.ID)
assert.Error(t, err, "Duplicate server should not have been stored")
})
t.Run("publish succeeds with same name but different version", func(t *testing.T) {
// Publish first version
firstVersionDetail := &model.ServerDetail{
Server: model.Server{
ID: uuid.New().String(),
Name: "io.github.versioned/test-server",
Description: "First version of the server",
Repository: model.Repository{
@ -376,9 +361,7 @@ func TestPublishIntegration(t *testing.T) {
ID: "versioned/test-server",
},
VersionDetail: model.VersionDetail{
Version: "1.0.0",
ReleaseDate: time.Now().Format(time.RFC3339),
IsLatest: false,
Version: "1.0.0",
},
},
}
@ -393,12 +376,17 @@ func TestPublishIntegration(t *testing.T) {
recorder := httptest.NewRecorder()
handler(recorder, req)
var response map[string]string
err = json.Unmarshal(recorder.Body.Bytes(), &response)
require.NoError(t, err)
firstVersionDetail.ID = response["id"] // Store the ID for later verification
assert.Equal(t, http.StatusCreated, recorder.Code, "First version should succeed")
require.NotEmpty(t, firstVersionDetail.ID, "Server ID should be generated")
// Publish second version with same name but different version
secondVersionDetail := &model.ServerDetail{
Server: model.Server{
ID: uuid.New().String(),
Name: "io.github.versioned/test-server", // Same name
Description: "Second version of the server",
Repository: model.Repository{
@ -407,9 +395,7 @@ func TestPublishIntegration(t *testing.T) {
ID: "versioned/test-server",
},
VersionDetail: model.VersionDetail{
Version: "2.0.0", // Different version
ReleaseDate: time.Now().Format(time.RFC3339),
IsLatest: true,
Version: "2.0.0", // Different version
},
},
}
@ -424,8 +410,14 @@ func TestPublishIntegration(t *testing.T) {
secondRecorder := httptest.NewRecorder()
handler(secondRecorder, secondReq)
var secondResponse map[string]string
err = json.Unmarshal(secondRecorder.Body.Bytes(), &secondResponse)
require.NoError(t, err)
secondVersionDetail.ID = secondResponse["id"] // Store the ID for later verification
// The second version should succeed
assert.Equal(t, http.StatusCreated, secondRecorder.Code)
require.NotEmpty(t, secondVersionDetail.ID, "Server ID for second version should be generated")
// Verify both versions exist
firstRetrieved, err := registryService.GetByID(firstVersionDetail.ID)
@ -441,7 +433,6 @@ func TestPublishIntegration(t *testing.T) {
// First, publish a newer version (2.0.0)
newerVersionDetail := &model.ServerDetail{
Server: model.Server{
ID: uuid.New().String(),
Name: "io.github.versioning/version-order-test",
Description: "Newer version published first",
Repository: model.Repository{
@ -450,9 +441,7 @@ func TestPublishIntegration(t *testing.T) {
ID: "versioning/version-order-test",
},
VersionDetail: model.VersionDetail{
Version: "2.0.0",
ReleaseDate: time.Now().Format(time.RFC3339),
IsLatest: true,
Version: "2.0.0",
},
},
}
@ -467,12 +456,17 @@ func TestPublishIntegration(t *testing.T) {
recorder := httptest.NewRecorder()
handler(recorder, req)
var response map[string]string
err = json.Unmarshal(recorder.Body.Bytes(), &response)
require.NoError(t, err)
newerVersionDetail.ID = response["id"] // Store the ID for later verification
assert.Equal(t, http.StatusCreated, recorder.Code, "Newer version should be published successfully")
require.NotEmpty(t, newerVersionDetail.ID, "Server ID for newer version should be generated")
// Now try to publish an older version (1.0.0) of the same package
olderVersionDetail := &model.ServerDetail{
Server: model.Server{
ID: uuid.New().String(),
Name: "io.github.versioning/version-order-test", // Same name
Description: "Older version published after newer",
Repository: model.Repository{
@ -481,9 +475,7 @@ func TestPublishIntegration(t *testing.T) {
ID: "versioning/version-order-test",
},
VersionDetail: model.VersionDetail{
Version: "1.0.0", // Older version
ReleaseDate: time.Now().Format(time.RFC3339),
IsLatest: false,
Version: "1.0.0", // Older version
},
},
}
@ -522,7 +514,6 @@ func TestPublishIntegrationWithComplexPackages(t *testing.T) {
t.Run("publish with complex package configuration", func(t *testing.T) {
serverDetail := &model.ServerDetail{
Server: model.Server{
ID: uuid.New().String(),
Name: "io.github.complex/advanced-mcp-server",
Description: "An advanced MCP server with complex configuration",
Repository: model.Repository{
@ -531,18 +522,20 @@ func TestPublishIntegrationWithComplexPackages(t *testing.T) {
ID: "complex/advanced-mcp-server",
},
VersionDetail: model.VersionDetail{
Version: "2.1.0",
ReleaseDate: time.Now().Format(time.RFC3339),
IsLatest: true,
Version: "2.1.0",
},
},
Packages: []model.Package{
{
RegistryName: "npm",
Name: "advanced-mcp-server",
Version: "2.1.0",
RunTimeHint: "node --experimental-modules",
Name: "@example/advanced-mcp-server",
Version: "43.1.0",
RunTimeHint: "node",
RuntimeArguments: []model.Argument{
{
Type: model.ArgumentTypeNamed,
Name: "experimental-modules",
},
{
Type: model.ArgumentTypeNamed,
Name: "config",
@ -620,10 +613,6 @@ func TestPublishIntegrationWithComplexPackages(t *testing.T) {
},
},
},
{
TransportType: "websocket",
URL: "ws://localhost:8081/mcp",
},
},
}
@ -643,8 +632,9 @@ func TestPublishIntegrationWithComplexPackages(t *testing.T) {
err = json.Unmarshal(recorder.Body.Bytes(), &response)
require.NoError(t, err)
serverDetail.ID = response["id"] // Store the ID for later verification
assert.Equal(t, "Server publication successful", response["message"])
assert.Equal(t, serverDetail.ID, response["id"])
assert.NotEmpty(t, response["id"], "Server ID should be generated")
// Verify the complex server was published correctly
publishedServer, err := registryService.GetByID(serverDetail.ID)
@ -654,16 +644,14 @@ func TestPublishIntegrationWithComplexPackages(t *testing.T) {
require.Len(t, publishedServer.Packages, 1)
pkg := publishedServer.Packages[0]
assert.Equal(t, "npm", pkg.RegistryName)
assert.Equal(t, "advanced-mcp-server", pkg.Name)
assert.Equal(t, "node --experimental-modules", pkg.RunTimeHint)
assert.Len(t, pkg.RuntimeArguments, 2)
assert.Equal(t, "@example/advanced-mcp-server", pkg.Name)
assert.Len(t, pkg.RuntimeArguments, 3)
assert.Len(t, pkg.PackageArguments, 1)
assert.Len(t, pkg.EnvironmentVariables, 2)
// Verify remotes
require.Len(t, publishedServer.Remotes, 2)
require.Len(t, publishedServer.Remotes, 1)
assert.Equal(t, "http", publishedServer.Remotes[0].TransportType)
assert.Equal(t, "websocket", publishedServer.Remotes[1].TransportType)
assert.Len(t, publishedServer.Remotes[0].Headers, 1)
})
}
@ -683,7 +671,6 @@ func TestPublishIntegrationEndToEnd(t *testing.T) {
// Step 2: Publish a new server
serverDetail := &model.ServerDetail{
Server: model.Server{
ID: uuid.New().String(),
Name: "io.github.e2e/end-to-end-server",
Description: "End-to-end test server",
Repository: model.Repository{
@ -692,9 +679,7 @@ func TestPublishIntegrationEndToEnd(t *testing.T) {
ID: "e2e/end-to-end-server",
},
VersionDetail: model.VersionDetail{
Version: "1.0.0",
ReleaseDate: time.Now().Format(time.RFC3339),
IsLatest: true,
Version: "1.0.0",
},
},
}
@ -709,6 +694,11 @@ func TestPublishIntegrationEndToEnd(t *testing.T) {
recorder := httptest.NewRecorder()
handler(recorder, req)
var response map[string]string
err = json.Unmarshal(recorder.Body.Bytes(), &response)
require.NoError(t, err)
serverDetail.ID = response["id"] // Store the ID for later verification
require.Equal(t, http.StatusCreated, recorder.Code)
// Step 3: Verify the count increased

View file

@ -7,6 +7,7 @@ import (
"strings"
"sync"
"github.com/google/uuid"
"github.com/modelcontextprotocol/registry/internal/model"
)
@ -231,6 +232,8 @@ func (db *MemoryDB) Publish(ctx context.Context, serverDetail *model.ServerDetai
return ErrInvalidInput
}
// Generate a new ID for the server detail
serverDetail.ID = uuid.New().String()
// Store a copy of the entire ServerDetail
serverDetailCopy := *serverDetail
db.entries[serverDetail.ID] = &serverDetailCopy