80daed081d
Creates a new `GenerateCosmoRouterConfig` function to build and serialize a Cosmo Router configuration from subgraphs. Implements PubSub mechanism for managing schema updates, allowing subscription to updates. Adds Subscription resolver and updates existing structures to accommodate new functionalities. This enhances the system's capabilities for dynamic updates and configuration management.
259 lines
7.1 KiB
Go
259 lines
7.1 KiB
Go
package graph
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"gitlab.com/unboundsoftware/schemas/graph/model"
|
|
)
|
|
|
|
func TestGenerateCosmoRouterConfig(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
subGraphs []*model.SubGraph
|
|
wantErr bool
|
|
validate func(t *testing.T, config string)
|
|
}{
|
|
{
|
|
name: "single subgraph with all fields",
|
|
subGraphs: []*model.SubGraph{
|
|
{
|
|
Service: "test-service",
|
|
URL: stringPtr("http://localhost:4001/query"),
|
|
WsURL: stringPtr("ws://localhost:4001/query"),
|
|
Sdl: "type Query { test: String }",
|
|
},
|
|
},
|
|
wantErr: false,
|
|
validate: func(t *testing.T, config string) {
|
|
var result map[string]interface{}
|
|
err := json.Unmarshal([]byte(config), &result)
|
|
require.NoError(t, err, "Config should be valid JSON")
|
|
|
|
assert.Equal(t, "1", result["version"], "Version should be 1")
|
|
|
|
subgraphs, ok := result["subgraphs"].([]interface{})
|
|
require.True(t, ok, "subgraphs should be an array")
|
|
require.Len(t, subgraphs, 1, "Should have 1 subgraph")
|
|
|
|
sg := subgraphs[0].(map[string]interface{})
|
|
assert.Equal(t, "test-service", sg["name"])
|
|
assert.Equal(t, "http://localhost:4001/query", sg["routing_url"])
|
|
assert.Equal(t, "type Query { test: String }", sg["sdl"])
|
|
|
|
subscription, ok := sg["subscription"].(map[string]interface{})
|
|
require.True(t, ok, "Should have subscription config")
|
|
assert.Equal(t, "ws://localhost:4001/query", subscription["url"])
|
|
assert.Equal(t, "ws", subscription["protocol"])
|
|
assert.Equal(t, "graphql-ws", subscription["websocket_subprotocol"])
|
|
},
|
|
},
|
|
{
|
|
name: "multiple subgraphs",
|
|
subGraphs: []*model.SubGraph{
|
|
{
|
|
Service: "service-1",
|
|
URL: stringPtr("http://localhost:4001/query"),
|
|
Sdl: "type Query { field1: String }",
|
|
},
|
|
{
|
|
Service: "service-2",
|
|
URL: stringPtr("http://localhost:4002/query"),
|
|
Sdl: "type Query { field2: String }",
|
|
},
|
|
{
|
|
Service: "service-3",
|
|
URL: stringPtr("http://localhost:4003/query"),
|
|
WsURL: stringPtr("ws://localhost:4003/query"),
|
|
Sdl: "type Subscription { updates: String }",
|
|
},
|
|
},
|
|
wantErr: false,
|
|
validate: func(t *testing.T, config string) {
|
|
var result map[string]interface{}
|
|
err := json.Unmarshal([]byte(config), &result)
|
|
require.NoError(t, err)
|
|
|
|
subgraphs := result["subgraphs"].([]interface{})
|
|
assert.Len(t, subgraphs, 3, "Should have 3 subgraphs")
|
|
|
|
// Check first service has no subscription
|
|
sg1 := subgraphs[0].(map[string]interface{})
|
|
assert.Equal(t, "service-1", sg1["name"])
|
|
_, hasSubscription := sg1["subscription"]
|
|
assert.False(t, hasSubscription, "service-1 should not have subscription config")
|
|
|
|
// Check third service has subscription
|
|
sg3 := subgraphs[2].(map[string]interface{})
|
|
assert.Equal(t, "service-3", sg3["name"])
|
|
subscription, hasSubscription := sg3["subscription"]
|
|
assert.True(t, hasSubscription, "service-3 should have subscription config")
|
|
assert.NotNil(t, subscription)
|
|
},
|
|
},
|
|
{
|
|
name: "subgraph with no URL",
|
|
subGraphs: []*model.SubGraph{
|
|
{
|
|
Service: "test-service",
|
|
URL: nil,
|
|
WsURL: nil,
|
|
Sdl: "type Query { test: String }",
|
|
},
|
|
},
|
|
wantErr: false,
|
|
validate: func(t *testing.T, config string) {
|
|
var result map[string]interface{}
|
|
err := json.Unmarshal([]byte(config), &result)
|
|
require.NoError(t, err)
|
|
|
|
subgraphs := result["subgraphs"].([]interface{})
|
|
sg := subgraphs[0].(map[string]interface{})
|
|
|
|
// Should not have routing_url or subscription fields if URLs are nil
|
|
_, hasRoutingURL := sg["routing_url"]
|
|
assert.False(t, hasRoutingURL, "Should not have routing_url when URL is nil")
|
|
|
|
_, hasSubscription := sg["subscription"]
|
|
assert.False(t, hasSubscription, "Should not have subscription when WsURL is nil")
|
|
},
|
|
},
|
|
{
|
|
name: "empty subgraphs",
|
|
subGraphs: []*model.SubGraph{},
|
|
wantErr: false,
|
|
validate: func(t *testing.T, config string) {
|
|
var result map[string]interface{}
|
|
err := json.Unmarshal([]byte(config), &result)
|
|
require.NoError(t, err)
|
|
|
|
subgraphs := result["subgraphs"].([]interface{})
|
|
assert.Len(t, subgraphs, 0, "Should have empty subgraphs array")
|
|
},
|
|
},
|
|
{
|
|
name: "nil subgraphs",
|
|
subGraphs: nil,
|
|
wantErr: false,
|
|
validate: func(t *testing.T, config string) {
|
|
var result map[string]interface{}
|
|
err := json.Unmarshal([]byte(config), &result)
|
|
require.NoError(t, err)
|
|
|
|
subgraphs := result["subgraphs"].([]interface{})
|
|
assert.Len(t, subgraphs, 0, "Should handle nil subgraphs as empty array")
|
|
},
|
|
},
|
|
{
|
|
name: "complex SDL with multiple types",
|
|
subGraphs: []*model.SubGraph{
|
|
{
|
|
Service: "complex-service",
|
|
URL: stringPtr("http://localhost:4001/query"),
|
|
Sdl: `
|
|
type Query {
|
|
user(id: ID!): User
|
|
users: [User!]!
|
|
}
|
|
|
|
type User {
|
|
id: ID!
|
|
name: String!
|
|
email: String!
|
|
}
|
|
`,
|
|
},
|
|
},
|
|
wantErr: false,
|
|
validate: func(t *testing.T, config string) {
|
|
var result map[string]interface{}
|
|
err := json.Unmarshal([]byte(config), &result)
|
|
require.NoError(t, err)
|
|
|
|
subgraphs := result["subgraphs"].([]interface{})
|
|
sg := subgraphs[0].(map[string]interface{})
|
|
sdl := sg["sdl"].(string)
|
|
|
|
assert.Contains(t, sdl, "type Query")
|
|
assert.Contains(t, sdl, "type User")
|
|
assert.Contains(t, sdl, "email: String!")
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
config, err := GenerateCosmoRouterConfig(tt.subGraphs)
|
|
|
|
if tt.wantErr {
|
|
assert.Error(t, err)
|
|
return
|
|
}
|
|
|
|
require.NoError(t, err)
|
|
assert.NotEmpty(t, config, "Config should not be empty")
|
|
|
|
if tt.validate != nil {
|
|
tt.validate(t, config)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestConvertSubGraphsToCosmo(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
subGraphs []*model.SubGraph
|
|
wantLen int
|
|
validate func(t *testing.T, result []map[string]interface{})
|
|
}{
|
|
{
|
|
name: "preserves subgraph order",
|
|
subGraphs: []*model.SubGraph{
|
|
{Service: "alpha", URL: stringPtr("http://a"), Sdl: "a"},
|
|
{Service: "beta", URL: stringPtr("http://b"), Sdl: "b"},
|
|
{Service: "gamma", URL: stringPtr("http://c"), Sdl: "c"},
|
|
},
|
|
wantLen: 3,
|
|
validate: func(t *testing.T, result []map[string]interface{}) {
|
|
assert.Equal(t, "alpha", result[0]["name"])
|
|
assert.Equal(t, "beta", result[1]["name"])
|
|
assert.Equal(t, "gamma", result[2]["name"])
|
|
},
|
|
},
|
|
{
|
|
name: "includes SDL exactly as provided",
|
|
subGraphs: []*model.SubGraph{
|
|
{
|
|
Service: "test",
|
|
URL: stringPtr("http://test"),
|
|
Sdl: "type Query { special: String! }",
|
|
},
|
|
},
|
|
wantLen: 1,
|
|
validate: func(t *testing.T, result []map[string]interface{}) {
|
|
assert.Equal(t, "type Query { special: String! }", result[0]["sdl"])
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := convertSubGraphsToCosmo(tt.subGraphs)
|
|
assert.Len(t, result, tt.wantLen)
|
|
|
|
if tt.validate != nil {
|
|
tt.validate(t, result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// Helper function for tests
|
|
func stringPtr(s string) *string {
|
|
return &s
|
|
}
|