registry/internal/importer/importer_test.go
adam jones ef4607720d
Fix _meta field format for MCP spec compliance (#367)
## Summary

Updates the `_meta` field structure to be compliant with the MCP
specification by using proper `prefix/name` format for vendor
extensions.

### Changes

- **Registry metadata**: `io.modelcontextprotocol.registry` →
`io.modelcontextprotocol.registry/official`
- **Publisher metadata**: `publisher` →
`io.modelcontextprotocol.registry/publisher-provided`
- **Database migration**: Updates indexes to use new field paths
- **Updated all code**: Service layer, validators, tests, and seed data

### Format Before
```json
{
  "_meta": {
    "publisher": { "license": "MIT" },
    "io.modelcontextprotocol.registry": { "id": "...", "is_latest": true }
  }
}
```

### Format After
```json
{
  "_meta": {
    "io.modelcontextprotocol.registry/publisher-provided": { "license": "MIT" },
    "io.modelcontextprotocol.registry/official": { "id": "...", "is_latest": true }
  }
}
```

This resolves the naming collision with the planned top-level
`publisher` field and ensures compliance with MCP specification
requirements.

Fixes #356
2025-09-08 11:40:04 -07:00

201 lines
5.3 KiB
Go

package importer_test
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"os"
"testing"
"time"
"github.com/modelcontextprotocol/registry/internal/database"
"github.com/modelcontextprotocol/registry/internal/importer"
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 TestImportService_LocalFile(t *testing.T) {
// Create a temporary seed file
tempFile := "/tmp/test_import_seed.json"
seedData := []apiv0.ServerJSON{
{
Name: "io.github.test/test-server-1",
Description: "Test server 1",
Repository: model.Repository{
URL: "https://github.com/test/repo1",
Source: "github",
ID: "123",
},
VersionDetail: model.VersionDetail{
Version: "1.0.0",
},
Meta: &apiv0.ServerMeta{
Official: &apiv0.RegistryExtensions{
ID: "test-id-1",
PublishedAt: time.Now(),
UpdatedAt: time.Now(),
IsLatest: true,
},
},
},
}
jsonData, err := json.Marshal(seedData)
require.NoError(t, err)
err = os.WriteFile(tempFile, jsonData, 0600)
require.NoError(t, err)
defer os.Remove(tempFile)
memDB := database.NewMemoryDB()
// Create importer service and test import
service := importer.NewService(memDB)
err = service.ImportFromPath(context.Background(), tempFile)
require.NoError(t, err)
// Verify the server was imported
servers, _, err := memDB.List(context.Background(), nil, "", 10)
require.NoError(t, err)
assert.Len(t, servers, 1)
assert.Equal(t, "io.github.test/test-server-1", servers[0].Name)
}
func TestImportService_HTTPFile(t *testing.T) {
// Create a test HTTP server
seedData := []apiv0.ServerJSON{
{
Name: "io.github.test/http-test-server",
Description: "HTTP test server",
Repository: model.Repository{
URL: "https://github.com/test/repo1",
Source: "github",
ID: "123",
},
VersionDetail: model.VersionDetail{
Version: "2.0.0",
},
Meta: &apiv0.ServerMeta{
Official: &apiv0.RegistryExtensions{
ID: "test-id-2",
PublishedAt: time.Now(),
UpdatedAt: time.Now(),
IsLatest: true,
},
},
},
}
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(seedData)
}))
defer server.Close()
memDB := database.NewMemoryDB()
// Create importer service and test import
service := importer.NewService(memDB)
err := service.ImportFromPath(context.Background(), server.URL+"/seed.json")
require.NoError(t, err)
// Verify the server was imported
servers, _, err := memDB.List(context.Background(), nil, "", 10)
require.NoError(t, err)
assert.Len(t, servers, 1)
assert.Equal(t, "io.github.test/http-test-server", servers[0].Name)
}
func TestImportService_RegistryAPI(t *testing.T) {
// Create test data
testServers := []apiv0.ServerJSON{
{
Name: "io.github.test/api-server-1",
Description: "API server 1",
Repository: model.Repository{
URL: "https://github.com/test/repo1",
Source: "github",
ID: "123",
},
VersionDetail: model.VersionDetail{
Version: "1.0.0",
},
Meta: &apiv0.ServerMeta{
Official: &apiv0.RegistryExtensions{
ID: "api-test-id-1",
PublishedAt: time.Now(),
UpdatedAt: time.Now(),
IsLatest: true,
},
},
},
{
Name: "io.github.test/api-server-2",
Description: "API server 2",
Repository: model.Repository{
URL: "https://github.com/test/repo2",
Source: "github",
ID: "456",
},
VersionDetail: model.VersionDetail{
Version: "2.0.0",
},
Meta: &apiv0.ServerMeta{
Official: &apiv0.RegistryExtensions{
ID: "api-test-id-2",
PublishedAt: time.Now(),
UpdatedAt: time.Now(),
IsLatest: true,
},
},
},
}
// Create test server that responds with paginated results
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
cursor := r.URL.Query().Get("cursor")
var response struct {
Servers []apiv0.ServerJSON `json:"servers"`
Metadata *struct {
NextCursor string `json:"next_cursor,omitempty"`
} `json:"metadata,omitempty"`
}
switch cursor {
case "":
// First page
response.Servers = []apiv0.ServerJSON{testServers[0]}
response.Metadata = &struct {
NextCursor string `json:"next_cursor,omitempty"`
}{NextCursor: "page2"}
case "page2":
// Second page
response.Servers = []apiv0.ServerJSON{testServers[1]}
// No next cursor means end of results
}
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(response)
}))
defer server.Close()
memDB := database.NewMemoryDB()
// Create importer service and test import
service := importer.NewService(memDB)
err := service.ImportFromPath(context.Background(), server.URL+"/v0/servers")
require.NoError(t, err)
// Verify both servers were imported
servers, _, err := memDB.List(context.Background(), nil, "", 10)
require.NoError(t, err)
assert.Len(t, servers, 2)
names := []string{servers[0].Name, servers[1].Name}
assert.Contains(t, names, "io.github.test/api-server-1")
assert.Contains(t, names, "io.github.test/api-server-2")
}