435 lines
8.0 KiB
Go
435 lines
8.0 KiB
Go
|
|
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")
|
||
|
|
}
|