Merge branch 'test/add-validation-event-tests' into 'main'
test: add validation and event tests for organization and API key See merge request unboundsoftware/schemas!634
This commit was merged in pull request #638.
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
.testCoverage.txt
|
.testCoverage.txt
|
||||||
.testCoverage.txt.tmp
|
.testCoverage.txt.tmp
|
||||||
coverage.html
|
coverage.html
|
||||||
|
coverage.out
|
||||||
/exported
|
/exported
|
||||||
/release
|
/release
|
||||||
/schemactl
|
/schemactl
|
||||||
|
|||||||
@@ -7,10 +7,68 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
"gitlab.com/unboundsoftware/eventsourced/eventsourced"
|
||||||
|
|
||||||
"gitlab.com/unboundsoftware/schemas/hash"
|
"gitlab.com/unboundsoftware/schemas/hash"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// AddOrganization tests
|
||||||
|
|
||||||
|
func TestAddOrganization_Validate_Success(t *testing.T) {
|
||||||
|
cmd := AddOrganization{
|
||||||
|
Name: "Test Org",
|
||||||
|
Initiator: "user@example.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
org := &Organization{} // New organization with no identity
|
||||||
|
err := cmd.Validate(context.Background(), org)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddOrganization_Validate_AlreadyExists(t *testing.T) {
|
||||||
|
cmd := AddOrganization{
|
||||||
|
Name: "Test Org",
|
||||||
|
Initiator: "user@example.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
org := &Organization{
|
||||||
|
BaseAggregate: eventsourced.BaseAggregateFromString("existing-org-id"),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := cmd.Validate(context.Background(), org)
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "already exists")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddOrganization_Validate_EmptyName(t *testing.T) {
|
||||||
|
cmd := AddOrganization{
|
||||||
|
Name: "",
|
||||||
|
Initiator: "user@example.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
org := &Organization{}
|
||||||
|
err := cmd.Validate(context.Background(), org)
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "name is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddOrganization_Event(t *testing.T) {
|
||||||
|
cmd := AddOrganization{
|
||||||
|
Name: "Test Org",
|
||||||
|
Initiator: "user@example.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
event := cmd.Event(context.Background())
|
||||||
|
require.NotNil(t, event)
|
||||||
|
|
||||||
|
orgEvent, ok := event.(*OrganizationAdded)
|
||||||
|
require.True(t, ok)
|
||||||
|
assert.Equal(t, "Test Org", orgEvent.Name)
|
||||||
|
assert.Equal(t, "user@example.com", orgEvent.Initiator)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddAPIKey tests
|
||||||
|
|
||||||
func TestAddAPIKey_Event(t *testing.T) {
|
func TestAddAPIKey_Event(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
Name string
|
Name string
|
||||||
@@ -74,3 +132,335 @@ func TestAddAPIKey_Event(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAddAPIKey_Validate_Success(t *testing.T) {
|
||||||
|
cmd := AddAPIKey{
|
||||||
|
Name: "production-key",
|
||||||
|
Key: "us_ak_1234567890123456",
|
||||||
|
Refs: []string{"main"},
|
||||||
|
Read: true,
|
||||||
|
Publish: false,
|
||||||
|
Initiator: "user@example.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
org := &Organization{
|
||||||
|
BaseAggregate: eventsourced.BaseAggregateFromString("org-123"),
|
||||||
|
APIKeys: []APIKey{},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := cmd.Validate(context.Background(), org)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddAPIKey_Validate_OrganizationNotExists(t *testing.T) {
|
||||||
|
cmd := AddAPIKey{
|
||||||
|
Name: "production-key",
|
||||||
|
Key: "us_ak_1234567890123456",
|
||||||
|
Refs: []string{"main"},
|
||||||
|
Read: true,
|
||||||
|
Publish: false,
|
||||||
|
Initiator: "user@example.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
org := &Organization{} // No identity means it doesn't exist
|
||||||
|
err := cmd.Validate(context.Background(), org)
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "does not exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddAPIKey_Validate_DuplicateKeyName(t *testing.T) {
|
||||||
|
cmd := AddAPIKey{
|
||||||
|
Name: "existing-key",
|
||||||
|
Key: "us_ak_1234567890123456",
|
||||||
|
Refs: []string{"main"},
|
||||||
|
Read: true,
|
||||||
|
Publish: false,
|
||||||
|
Initiator: "user@example.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
org := &Organization{
|
||||||
|
BaseAggregate: eventsourced.BaseAggregateFromString("org-123"),
|
||||||
|
APIKeys: []APIKey{
|
||||||
|
{
|
||||||
|
Name: "existing-key",
|
||||||
|
Key: "hashed-key",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := cmd.Validate(context.Background(), org)
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "already exist")
|
||||||
|
assert.Contains(t, err.Error(), "existing-key")
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateSubGraph tests
|
||||||
|
|
||||||
|
func TestUpdateSubGraph_Validate_Success(t *testing.T) {
|
||||||
|
url := "http://example.com/graphql"
|
||||||
|
cmd := UpdateSubGraph{
|
||||||
|
OrganizationId: "org-123",
|
||||||
|
Ref: "main",
|
||||||
|
Service: "users",
|
||||||
|
Url: &url,
|
||||||
|
Sdl: "type Query { hello: String }",
|
||||||
|
Initiator: "user@example.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
subGraph := &SubGraph{
|
||||||
|
BaseAggregate: eventsourced.BaseAggregateFromString("subgraph-123"),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := cmd.Validate(context.Background(), subGraph)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateSubGraph_Validate_MissingRef(t *testing.T) {
|
||||||
|
url := "http://example.com/graphql"
|
||||||
|
cmd := UpdateSubGraph{
|
||||||
|
OrganizationId: "org-123",
|
||||||
|
Ref: "",
|
||||||
|
Service: "users",
|
||||||
|
Url: &url,
|
||||||
|
Sdl: "type Query { hello: String }",
|
||||||
|
Initiator: "user@example.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
subGraph := &SubGraph{
|
||||||
|
BaseAggregate: eventsourced.BaseAggregateFromString("subgraph-123"),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := cmd.Validate(context.Background(), subGraph)
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "ref is missing")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateSubGraph_Validate_RefWhitespaceOnly(t *testing.T) {
|
||||||
|
url := "http://example.com/graphql"
|
||||||
|
cmd := UpdateSubGraph{
|
||||||
|
OrganizationId: "org-123",
|
||||||
|
Ref: " ",
|
||||||
|
Service: "users",
|
||||||
|
Url: &url,
|
||||||
|
Sdl: "type Query { hello: String }",
|
||||||
|
Initiator: "user@example.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
subGraph := &SubGraph{
|
||||||
|
BaseAggregate: eventsourced.BaseAggregateFromString("subgraph-123"),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := cmd.Validate(context.Background(), subGraph)
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "ref is missing")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateSubGraph_Validate_MissingService(t *testing.T) {
|
||||||
|
url := "http://example.com/graphql"
|
||||||
|
cmd := UpdateSubGraph{
|
||||||
|
OrganizationId: "org-123",
|
||||||
|
Ref: "main",
|
||||||
|
Service: "",
|
||||||
|
Url: &url,
|
||||||
|
Sdl: "type Query { hello: String }",
|
||||||
|
Initiator: "user@example.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
subGraph := &SubGraph{
|
||||||
|
BaseAggregate: eventsourced.BaseAggregateFromString("subgraph-123"),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := cmd.Validate(context.Background(), subGraph)
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "service is missing")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateSubGraph_Validate_ServiceWhitespaceOnly(t *testing.T) {
|
||||||
|
url := "http://example.com/graphql"
|
||||||
|
cmd := UpdateSubGraph{
|
||||||
|
OrganizationId: "org-123",
|
||||||
|
Ref: "main",
|
||||||
|
Service: " ",
|
||||||
|
Url: &url,
|
||||||
|
Sdl: "type Query { hello: String }",
|
||||||
|
Initiator: "user@example.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
subGraph := &SubGraph{
|
||||||
|
BaseAggregate: eventsourced.BaseAggregateFromString("subgraph-123"),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := cmd.Validate(context.Background(), subGraph)
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "service is missing")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateSubGraph_Validate_MissingSDL(t *testing.T) {
|
||||||
|
url := "http://example.com/graphql"
|
||||||
|
cmd := UpdateSubGraph{
|
||||||
|
OrganizationId: "org-123",
|
||||||
|
Ref: "main",
|
||||||
|
Service: "users",
|
||||||
|
Url: &url,
|
||||||
|
Sdl: "",
|
||||||
|
Initiator: "user@example.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
subGraph := &SubGraph{
|
||||||
|
BaseAggregate: eventsourced.BaseAggregateFromString("subgraph-123"),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := cmd.Validate(context.Background(), subGraph)
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "SDL is missing")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateSubGraph_Validate_SDLWhitespaceOnly(t *testing.T) {
|
||||||
|
url := "http://example.com/graphql"
|
||||||
|
cmd := UpdateSubGraph{
|
||||||
|
OrganizationId: "org-123",
|
||||||
|
Ref: "main",
|
||||||
|
Service: "users",
|
||||||
|
Url: &url,
|
||||||
|
Sdl: " \n\t ",
|
||||||
|
Initiator: "user@example.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
subGraph := &SubGraph{
|
||||||
|
BaseAggregate: eventsourced.BaseAggregateFromString("subgraph-123"),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := cmd.Validate(context.Background(), subGraph)
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "SDL is missing")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateSubGraph_Validate_MissingURL_NoExistingURL(t *testing.T) {
|
||||||
|
cmd := UpdateSubGraph{
|
||||||
|
OrganizationId: "org-123",
|
||||||
|
Ref: "main",
|
||||||
|
Service: "users",
|
||||||
|
Url: nil,
|
||||||
|
Sdl: "type Query { hello: String }",
|
||||||
|
Initiator: "user@example.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
subGraph := &SubGraph{
|
||||||
|
BaseAggregate: eventsourced.BaseAggregateFromString("subgraph-123"),
|
||||||
|
Url: nil, // No existing URL
|
||||||
|
}
|
||||||
|
|
||||||
|
err := cmd.Validate(context.Background(), subGraph)
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "url is missing")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateSubGraph_Validate_MissingURL_HasExistingURL(t *testing.T) {
|
||||||
|
existingURL := "http://example.com/graphql"
|
||||||
|
cmd := UpdateSubGraph{
|
||||||
|
OrganizationId: "org-123",
|
||||||
|
Ref: "main",
|
||||||
|
Service: "users",
|
||||||
|
Url: nil,
|
||||||
|
Sdl: "type Query { hello: String }",
|
||||||
|
Initiator: "user@example.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
subGraph := &SubGraph{
|
||||||
|
BaseAggregate: eventsourced.BaseAggregateFromString("subgraph-123"),
|
||||||
|
Url: &existingURL, // Has existing URL, so nil is OK
|
||||||
|
}
|
||||||
|
|
||||||
|
err := cmd.Validate(context.Background(), subGraph)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateSubGraph_Validate_EmptyURL_NoExistingURL(t *testing.T) {
|
||||||
|
emptyURL := ""
|
||||||
|
cmd := UpdateSubGraph{
|
||||||
|
OrganizationId: "org-123",
|
||||||
|
Ref: "main",
|
||||||
|
Service: "users",
|
||||||
|
Url: &emptyURL,
|
||||||
|
Sdl: "type Query { hello: String }",
|
||||||
|
Initiator: "user@example.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
subGraph := &SubGraph{
|
||||||
|
BaseAggregate: eventsourced.BaseAggregateFromString("subgraph-123"),
|
||||||
|
Url: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := cmd.Validate(context.Background(), subGraph)
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "url is missing")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateSubGraph_Validate_URLWhitespaceOnly_NoExistingURL(t *testing.T) {
|
||||||
|
whitespaceURL := " "
|
||||||
|
cmd := UpdateSubGraph{
|
||||||
|
OrganizationId: "org-123",
|
||||||
|
Ref: "main",
|
||||||
|
Service: "users",
|
||||||
|
Url: &whitespaceURL,
|
||||||
|
Sdl: "type Query { hello: String }",
|
||||||
|
Initiator: "user@example.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
subGraph := &SubGraph{
|
||||||
|
BaseAggregate: eventsourced.BaseAggregateFromString("subgraph-123"),
|
||||||
|
Url: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := cmd.Validate(context.Background(), subGraph)
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "url is missing")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateSubGraph_Validate_WrongAggregateType(t *testing.T) {
|
||||||
|
url := "http://example.com/graphql"
|
||||||
|
cmd := UpdateSubGraph{
|
||||||
|
OrganizationId: "org-123",
|
||||||
|
Ref: "main",
|
||||||
|
Service: "users",
|
||||||
|
Url: &url,
|
||||||
|
Sdl: "type Query { hello: String }",
|
||||||
|
Initiator: "user@example.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass wrong aggregate type
|
||||||
|
org := &Organization{
|
||||||
|
BaseAggregate: eventsourced.BaseAggregateFromString("org-123"),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := cmd.Validate(context.Background(), org)
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "not a SubGraph")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateSubGraph_Event(t *testing.T) {
|
||||||
|
url := "http://example.com/graphql"
|
||||||
|
wsURL := "ws://example.com/graphql"
|
||||||
|
cmd := UpdateSubGraph{
|
||||||
|
OrganizationId: "org-123",
|
||||||
|
Ref: "main",
|
||||||
|
Service: "users",
|
||||||
|
Url: &url,
|
||||||
|
WSUrl: &wsURL,
|
||||||
|
Sdl: "type Query { hello: String }",
|
||||||
|
Initiator: "user@example.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
event := cmd.Event(context.Background())
|
||||||
|
require.NotNil(t, event)
|
||||||
|
|
||||||
|
subGraphEvent, ok := event.(*SubGraphUpdated)
|
||||||
|
require.True(t, ok)
|
||||||
|
assert.Equal(t, "org-123", subGraphEvent.OrganizationId)
|
||||||
|
assert.Equal(t, "main", subGraphEvent.Ref)
|
||||||
|
assert.Equal(t, "users", subGraphEvent.Service)
|
||||||
|
assert.Equal(t, url, *subGraphEvent.Url)
|
||||||
|
assert.Equal(t, wsURL, *subGraphEvent.WSUrl)
|
||||||
|
assert.Equal(t, "type Query { hello: String }", subGraphEvent.Sdl)
|
||||||
|
assert.Equal(t, "user@example.com", subGraphEvent.Initiator)
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,434 @@
|
|||||||
|
package sdlmerge
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMergeSDLs_Success(t *testing.T) {
|
||||||
|
// Both types need to be in the same subgraph or properly federated
|
||||||
|
sdl1 := `
|
||||||
|
type User {
|
||||||
|
id: ID!
|
||||||
|
name: String!
|
||||||
|
}
|
||||||
|
|
||||||
|
type Post {
|
||||||
|
id: ID!
|
||||||
|
title: String!
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
result, err := MergeSDLs(sdl1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Contains(t, result, "User")
|
||||||
|
assert.Contains(t, result, "Post")
|
||||||
|
assert.Contains(t, result, "id")
|
||||||
|
assert.Contains(t, result, "name")
|
||||||
|
assert.Contains(t, result, "title")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMergeSDLs_SingleSchema(t *testing.T) {
|
||||||
|
sdl := `
|
||||||
|
type Query {
|
||||||
|
hello: String
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
result, err := MergeSDLs(sdl)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Contains(t, result, "Query")
|
||||||
|
assert.Contains(t, result, "hello")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMergeSDLs_EmptySchemas(t *testing.T) {
|
||||||
|
result, err := MergeSDLs()
|
||||||
|
require.NoError(t, err)
|
||||||
|
// With no schemas, result will be empty after processing
|
||||||
|
// This is valid - just verifies no crash
|
||||||
|
_ = result
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMergeSDLs_InvalidSyntax(t *testing.T) {
|
||||||
|
invalidSDL := `
|
||||||
|
type User {
|
||||||
|
id: ID!
|
||||||
|
name: String!
|
||||||
|
// Missing closing brace
|
||||||
|
`
|
||||||
|
|
||||||
|
_, err := MergeSDLs(invalidSDL)
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "parse graphql document string")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMergeSDLs_UnknownType(t *testing.T) {
|
||||||
|
sdl := `
|
||||||
|
type User {
|
||||||
|
id: ID!
|
||||||
|
profile: UnknownType
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
_, err := MergeSDLs(sdl)
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "validate schema")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMergeSDLs_DuplicateTypes_DifferentFields(t *testing.T) {
|
||||||
|
// Same type with different fields in different subgraphs - should fail
|
||||||
|
// In federation, shared types must be identical
|
||||||
|
sdl1 := `
|
||||||
|
type User {
|
||||||
|
id: ID!
|
||||||
|
}
|
||||||
|
`
|
||||||
|
sdl2 := `
|
||||||
|
type User {
|
||||||
|
name: String!
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
_, err := MergeSDLs(sdl1, sdl2)
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "shared type")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMergeSDLs_ExtendType(t *testing.T) {
|
||||||
|
sdl1 := `
|
||||||
|
type User {
|
||||||
|
id: ID!
|
||||||
|
}
|
||||||
|
`
|
||||||
|
sdl2 := `
|
||||||
|
extend type User {
|
||||||
|
email: String!
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
result, err := MergeSDLs(sdl1, sdl2)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Contains(t, result, "User")
|
||||||
|
assert.Contains(t, result, "id")
|
||||||
|
assert.Contains(t, result, "email")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMergeSDLs_Scalars(t *testing.T) {
|
||||||
|
sdl := `
|
||||||
|
scalar DateTime
|
||||||
|
|
||||||
|
type Event {
|
||||||
|
id: ID!
|
||||||
|
createdAt: DateTime!
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
result, err := MergeSDLs(sdl)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Contains(t, result, "DateTime")
|
||||||
|
assert.Contains(t, result, "Event")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMergeSDLs_Enums(t *testing.T) {
|
||||||
|
sdl := `
|
||||||
|
enum Role {
|
||||||
|
ADMIN
|
||||||
|
USER
|
||||||
|
GUEST
|
||||||
|
}
|
||||||
|
|
||||||
|
type User {
|
||||||
|
id: ID!
|
||||||
|
role: Role!
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
result, err := MergeSDLs(sdl)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Contains(t, result, "Role")
|
||||||
|
assert.Contains(t, result, "ADMIN")
|
||||||
|
assert.Contains(t, result, "USER")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMergeSDLs_Interfaces(t *testing.T) {
|
||||||
|
sdl := `
|
||||||
|
interface Node {
|
||||||
|
id: ID!
|
||||||
|
}
|
||||||
|
|
||||||
|
type User implements Node {
|
||||||
|
id: ID!
|
||||||
|
name: String!
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
result, err := MergeSDLs(sdl)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Contains(t, result, "Node")
|
||||||
|
assert.Contains(t, result, "implements")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMergeSDLs_Unions(t *testing.T) {
|
||||||
|
sdl := `
|
||||||
|
type User {
|
||||||
|
id: ID!
|
||||||
|
name: String!
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bot {
|
||||||
|
id: ID!
|
||||||
|
version: String!
|
||||||
|
}
|
||||||
|
|
||||||
|
union Actor = User | Bot
|
||||||
|
`
|
||||||
|
|
||||||
|
result, err := MergeSDLs(sdl)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Contains(t, result, "Actor")
|
||||||
|
assert.Contains(t, result, "User")
|
||||||
|
assert.Contains(t, result, "Bot")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMergeSDLs_InputTypes(t *testing.T) {
|
||||||
|
sdl := `
|
||||||
|
input CreateUserInput {
|
||||||
|
name: String!
|
||||||
|
email: String!
|
||||||
|
}
|
||||||
|
|
||||||
|
type Mutation {
|
||||||
|
createUser(input: CreateUserInput!): User
|
||||||
|
}
|
||||||
|
|
||||||
|
type User {
|
||||||
|
id: ID!
|
||||||
|
name: String!
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
result, err := MergeSDLs(sdl)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Contains(t, result, "CreateUserInput")
|
||||||
|
assert.Contains(t, result, "createUser")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMergeSDLs_Directives(t *testing.T) {
|
||||||
|
sdl := `
|
||||||
|
type User {
|
||||||
|
id: ID!
|
||||||
|
name: String! @deprecated(reason: "Use fullName instead")
|
||||||
|
fullName: String!
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
result, err := MergeSDLs(sdl)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Contains(t, result, "User")
|
||||||
|
assert.Contains(t, result, "name")
|
||||||
|
assert.Contains(t, result, "fullName")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMergeSDLs_FederationKeys(t *testing.T) {
|
||||||
|
// Federation @key directive
|
||||||
|
sdl := `
|
||||||
|
type User @key(fields: "id") {
|
||||||
|
id: ID!
|
||||||
|
name: String!
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
result, err := MergeSDLs(sdl)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Contains(t, result, "User")
|
||||||
|
// @key directive should be removed during merge
|
||||||
|
assert.NotContains(t, result, "@key")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMergeSDLs_ExternalFields(t *testing.T) {
|
||||||
|
// Federation @external directive
|
||||||
|
sdl1 := `
|
||||||
|
type User @key(fields: "id") {
|
||||||
|
id: ID!
|
||||||
|
name: String!
|
||||||
|
}
|
||||||
|
`
|
||||||
|
sdl2 := `
|
||||||
|
extend type User @key(fields: "id") {
|
||||||
|
id: ID! @external
|
||||||
|
posts: [Post!]!
|
||||||
|
}
|
||||||
|
|
||||||
|
type Post {
|
||||||
|
id: ID!
|
||||||
|
title: String!
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
result, err := MergeSDLs(sdl1, sdl2)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Contains(t, result, "User")
|
||||||
|
assert.Contains(t, result, "Post")
|
||||||
|
// @external fields should be removed
|
||||||
|
assert.NotContains(t, result, "@external")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMergeSDLs_ComplexSchema(t *testing.T) {
|
||||||
|
// Multiple subgraphs with various types - simplified to avoid cross-references
|
||||||
|
users := `
|
||||||
|
type User @key(fields: "id") {
|
||||||
|
id: ID!
|
||||||
|
username: String!
|
||||||
|
email: String!
|
||||||
|
}
|
||||||
|
|
||||||
|
type Query {
|
||||||
|
user(id: ID!): User
|
||||||
|
users: [User!]!
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
posts := `
|
||||||
|
extend type User @key(fields: "id") {
|
||||||
|
id: ID! @external
|
||||||
|
posts: [Post!]!
|
||||||
|
}
|
||||||
|
|
||||||
|
type Post @key(fields: "id") {
|
||||||
|
id: ID!
|
||||||
|
title: String!
|
||||||
|
content: String!
|
||||||
|
}
|
||||||
|
|
||||||
|
extend type Query {
|
||||||
|
post(id: ID!): Post
|
||||||
|
posts: [Post!]!
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
comments := `
|
||||||
|
extend type Post @key(fields: "id") {
|
||||||
|
id: ID! @external
|
||||||
|
comments: [Comment!]!
|
||||||
|
}
|
||||||
|
|
||||||
|
type Comment {
|
||||||
|
id: ID!
|
||||||
|
text: String!
|
||||||
|
}
|
||||||
|
|
||||||
|
extend type Query {
|
||||||
|
comment(id: ID!): Comment
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
result, err := MergeSDLs(users, posts, comments)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Verify all types are present
|
||||||
|
assert.Contains(t, result, "User")
|
||||||
|
assert.Contains(t, result, "Post")
|
||||||
|
assert.Contains(t, result, "Comment")
|
||||||
|
assert.Contains(t, result, "Query")
|
||||||
|
|
||||||
|
// Verify fields from all subgraphs
|
||||||
|
assert.Contains(t, result, "username")
|
||||||
|
assert.Contains(t, result, "posts")
|
||||||
|
assert.Contains(t, result, "comments")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMergeSDLs_EmptyTypeDefinition(t *testing.T) {
|
||||||
|
sdl := `
|
||||||
|
type Empty {}
|
||||||
|
`
|
||||||
|
|
||||||
|
_, err := MergeSDLs(sdl)
|
||||||
|
require.Error(t, err)
|
||||||
|
// Empty types are invalid in GraphQL
|
||||||
|
assert.Contains(t, err.Error(), "empty body")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMergeSDLs_MultipleValidationErrors(t *testing.T) {
|
||||||
|
// Schema with multiple errors
|
||||||
|
sdl := `
|
||||||
|
type User {
|
||||||
|
id: ID!
|
||||||
|
profile: NonExistentType1
|
||||||
|
settings: NonExistentType2
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
_, err := MergeSDLs(sdl)
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMergeSDLs_ListTypes(t *testing.T) {
|
||||||
|
sdl := `
|
||||||
|
type User {
|
||||||
|
id: ID!
|
||||||
|
tags: [String!]!
|
||||||
|
friends: [User!]
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
result, err := MergeSDLs(sdl)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Contains(t, result, "User")
|
||||||
|
assert.Contains(t, result, "tags")
|
||||||
|
assert.Contains(t, result, "friends")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMergeSDLs_NonNullTypes(t *testing.T) {
|
||||||
|
sdl := `
|
||||||
|
type User {
|
||||||
|
id: ID!
|
||||||
|
name: String!
|
||||||
|
email: String
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
result, err := MergeSDLs(sdl)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Contains(t, result, "User")
|
||||||
|
assert.Contains(t, result, "id")
|
||||||
|
assert.Contains(t, result, "name")
|
||||||
|
assert.Contains(t, result, "email")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMergeSDLs_Comments(t *testing.T) {
|
||||||
|
sdl := `
|
||||||
|
# This is a user type
|
||||||
|
type User {
|
||||||
|
# User ID
|
||||||
|
id: ID!
|
||||||
|
# User name
|
||||||
|
name: String!
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
result, err := MergeSDLs(sdl)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Contains(t, result, "User")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMergeSDLs_LargeSchema(t *testing.T) {
|
||||||
|
// Test with a reasonably large schema to ensure performance
|
||||||
|
var sdlBuilder strings.Builder
|
||||||
|
for i := 0; i < 50; i++ {
|
||||||
|
sdlBuilder.WriteString("type Type")
|
||||||
|
sdlBuilder.WriteString(strings.Repeat(string(rune('A'+i%26)), 1))
|
||||||
|
sdlBuilder.WriteString(string(rune('0' + i/26)))
|
||||||
|
sdlBuilder.WriteString(" { id: ID }\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := MergeSDLs(sdlBuilder.String())
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Verify some types are present
|
||||||
|
assert.Contains(t, result, "TypeA0")
|
||||||
|
assert.Contains(t, result, "TypeB0")
|
||||||
|
assert.Contains(t, result, "TypeC0")
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user