Files
schemas/sdlmerge/sdlmerge_test.go
T

435 lines
8.0 KiB
Go
Raw Normal View History

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")
}