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