package graphql import ( "bytes" "context" "encoding/json" "net/http" "net/http/httptest" "omega-server/local/graphql/handler" "omega-server/local/service" "testing" "github.com/gofiber/fiber/v2" "gorm.io/gorm" ) // GraphQLTestUtils provides utilities for testing GraphQL endpoints type GraphQLTestUtils struct { App *fiber.App Handler *handler.GraphQLHandler DB *gorm.DB } // GraphQLTestRequest represents a GraphQL test request type GraphQLTestRequest struct { Query string `json:"query"` Variables map[string]interface{} `json:"variables,omitempty"` } // GraphQLTestResponse represents a GraphQL test response type GraphQLTestResponse struct { Data interface{} `json:"data,omitempty"` Errors []GraphQLError `json:"errors,omitempty"` } // GraphQLError represents a GraphQL error in test responses type GraphQLError struct { Message string `json:"message"` Path []string `json:"path,omitempty"` } // NewGraphQLTestUtils creates a new GraphQL test utilities instance func NewGraphQLTestUtils(db *gorm.DB, membershipService *service.MembershipService) *GraphQLTestUtils { app := fiber.New(fiber.Config{ DisableStartupMessage: true, }) graphqlHandler := handler.NewGraphQLHandler(membershipService) app.Post("/graphql", graphqlHandler.Handle) app.Get("/graphql", func(c *fiber.Ctx) error { return c.SendString(graphqlHandler.GetSchema()) }) return &GraphQLTestUtils{ App: app, Handler: graphqlHandler, DB: db, } } // ExecuteQuery executes a GraphQL query and returns the response func (gtu *GraphQLTestUtils) ExecuteQuery(t *testing.T, query string, variables map[string]interface{}) *GraphQLTestResponse { request := GraphQLTestRequest{ Query: query, Variables: variables, } body, err := json.Marshal(request) if err != nil { t.Fatalf("Failed to marshal GraphQL request: %v", err) } req := httptest.NewRequest(http.MethodPost, "/graphql", bytes.NewBuffer(body)) req.Header.Set("Content-Type", "application/json") resp, err := gtu.App.Test(req) if err != nil { t.Fatalf("Failed to execute GraphQL request: %v", err) } defer resp.Body.Close() var response GraphQLTestResponse if err := json.NewDecoder(resp.Body).Decode(&response); err != nil { t.Fatalf("Failed to decode GraphQL response: %v", err) } return &response } // ExecuteQueryWithContext executes a GraphQL query with context func (gtu *GraphQLTestUtils) ExecuteQueryWithContext(t *testing.T, ctx context.Context, query string, variables map[string]interface{}) *GraphQLTestResponse { request := GraphQLTestRequest{ Query: query, Variables: variables, } body, err := json.Marshal(request) if err != nil { t.Fatalf("Failed to marshal GraphQL request: %v", err) } req := httptest.NewRequest(http.MethodPost, "/graphql", bytes.NewBuffer(body)) req.Header.Set("Content-Type", "application/json") req = req.WithContext(ctx) resp, err := gtu.App.Test(req) if err != nil { t.Fatalf("Failed to execute GraphQL request: %v", err) } defer resp.Body.Close() var response GraphQLTestResponse if err := json.NewDecoder(resp.Body).Decode(&response); err != nil { t.Fatalf("Failed to decode GraphQL response: %v", err) } return &response } // ExecuteLoginMutation executes a login mutation func (gtu *GraphQLTestUtils) ExecuteLoginMutation(t *testing.T, email, password string) *GraphQLTestResponse { query := ` mutation Login($email: String!, $password: String!) { login(input: {email: $email, password: $password}) { token user { id email fullName } } } ` variables := map[string]interface{}{ "email": email, "password": password, } return gtu.ExecuteQuery(t, query, variables) } // ExecuteCreateUserMutation executes a create user mutation func (gtu *GraphQLTestUtils) ExecuteCreateUserMutation(t *testing.T, email, password, fullName string) *GraphQLTestResponse { query := ` mutation CreateUser($email: String!, $password: String!, $fullName: String!) { createUser(input: {email: $email, password: $password, fullName: $fullName}) { id email fullName createdAt updatedAt } } ` variables := map[string]interface{}{ "email": email, "password": password, "fullName": fullName, } return gtu.ExecuteQuery(t, query, variables) } // ExecuteMeQuery executes a me query func (gtu *GraphQLTestUtils) ExecuteMeQuery(t *testing.T) *GraphQLTestResponse { query := ` query Me { me { id email fullName createdAt updatedAt } } ` return gtu.ExecuteQuery(t, query, nil) } // ExecuteUsersQuery executes a users query func (gtu *GraphQLTestUtils) ExecuteUsersQuery(t *testing.T) *GraphQLTestResponse { query := ` query Users { users { id email fullName createdAt updatedAt } } ` return gtu.ExecuteQuery(t, query, nil) } // ExecuteUserQuery executes a user query by ID func (gtu *GraphQLTestUtils) ExecuteUserQuery(t *testing.T, userID string) *GraphQLTestResponse { query := ` query User($id: String!) { user(id: $id) { id email fullName createdAt updatedAt } } ` variables := map[string]interface{}{ "id": userID, } return gtu.ExecuteQuery(t, query, variables) } // ExecuteCreateProjectMutation executes a create project mutation func (gtu *GraphQLTestUtils) ExecuteCreateProjectMutation(t *testing.T, name, description, ownerID string) *GraphQLTestResponse { query := ` mutation CreateProject($name: String!, $description: String, $ownerId: String!) { createProject(input: {name: $name, description: $description, ownerId: $ownerId}) { id name description ownerId createdAt updatedAt } } ` variables := map[string]interface{}{ "name": name, "description": description, "ownerId": ownerID, } return gtu.ExecuteQuery(t, query, variables) } // ExecuteProjectsQuery executes a projects query func (gtu *GraphQLTestUtils) ExecuteProjectsQuery(t *testing.T) *GraphQLTestResponse { query := ` query Projects { projects { id name description ownerId createdAt updatedAt } } ` return gtu.ExecuteQuery(t, query, nil) } // ExecuteCreateTaskMutation executes a create task mutation func (gtu *GraphQLTestUtils) ExecuteCreateTaskMutation(t *testing.T, title, description, status, priority, projectID string) *GraphQLTestResponse { query := ` mutation CreateTask($title: String!, $description: String, $status: String, $priority: String, $projectId: String!) { createTask(input: {title: $title, description: $description, status: $status, priority: $priority, projectId: $projectId}) { id title description status priority projectId createdAt updatedAt } } ` variables := map[string]interface{}{ "title": title, "description": description, "status": status, "priority": priority, "projectId": projectID, } return gtu.ExecuteQuery(t, query, variables) } // ExecuteTasksQuery executes a tasks query func (gtu *GraphQLTestUtils) ExecuteTasksQuery(t *testing.T, projectID *string) *GraphQLTestResponse { query := ` query Tasks($projectId: String) { tasks(projectId: $projectId) { id title description status priority projectId createdAt updatedAt } } ` variables := make(map[string]interface{}) if projectID != nil { variables["projectId"] = *projectID } return gtu.ExecuteQuery(t, query, variables) } // GetSchema returns the GraphQL schema func (gtu *GraphQLTestUtils) GetSchema(t *testing.T) string { return gtu.Handler.GetSchema() } // AssertNoErrors asserts that the GraphQL response has no errors func (gtu *GraphQLTestUtils) AssertNoErrors(t *testing.T, response *GraphQLTestResponse) { if len(response.Errors) > 0 { t.Fatalf("Expected no GraphQL errors, but got: %+v", response.Errors) } } // AssertHasErrors asserts that the GraphQL response has errors func (gtu *GraphQLTestUtils) AssertHasErrors(t *testing.T, response *GraphQLTestResponse) { if len(response.Errors) == 0 { t.Fatalf("Expected GraphQL errors, but got none") } } // AssertErrorMessage asserts that the GraphQL response contains a specific error message func (gtu *GraphQLTestUtils) AssertErrorMessage(t *testing.T, response *GraphQLTestResponse, expectedMessage string) { if len(response.Errors) == 0 { t.Fatalf("Expected GraphQL errors, but got none") } for _, err := range response.Errors { if err.Message == expectedMessage { return } } t.Fatalf("Expected error message '%s', but not found in errors: %+v", expectedMessage, response.Errors) } // AssertDataNotNil asserts that the GraphQL response data is not nil func (gtu *GraphQLTestUtils) AssertDataNotNil(t *testing.T, response *GraphQLTestResponse) { if response.Data == nil { t.Fatalf("Expected GraphQL data to not be nil, but it was") } } // AssertDataNil asserts that the GraphQL response data is nil func (gtu *GraphQLTestUtils) AssertDataNil(t *testing.T, response *GraphQLTestResponse) { if response.Data != nil { t.Fatalf("Expected GraphQL data to be nil, but got: %+v", response.Data) } } // ExtractField extracts a field from the GraphQL response data func (gtu *GraphQLTestUtils) ExtractField(t *testing.T, response *GraphQLTestResponse, fieldPath ...string) interface{} { if response.Data == nil { t.Fatalf("Cannot extract field from nil data") } data := response.Data for _, field := range fieldPath { if dataMap, ok := data.(map[string]interface{}); ok { if value, exists := dataMap[field]; exists { data = value } else { t.Fatalf("Field '%s' not found in data: %+v", field, dataMap) } } else { t.Fatalf("Cannot extract field '%s' from non-map data: %+v", field, data) } } return data } // ExtractString extracts a string field from the GraphQL response data func (gtu *GraphQLTestUtils) ExtractString(t *testing.T, response *GraphQLTestResponse, fieldPath ...string) string { value := gtu.ExtractField(t, response, fieldPath...) if str, ok := value.(string); ok { return str } t.Fatalf("Expected string value for field %v, but got: %+v", fieldPath, value) return "" } // ExtractInt extracts an integer field from the GraphQL response data func (gtu *GraphQLTestUtils) ExtractInt(t *testing.T, response *GraphQLTestResponse, fieldPath ...string) int { value := gtu.ExtractField(t, response, fieldPath...) if floatVal, ok := value.(float64); ok { return int(floatVal) } if intVal, ok := value.(int); ok { return intVal } t.Fatalf("Expected int value for field %v, but got: %+v", fieldPath, value) return 0 } // ExtractBool extracts a boolean field from the GraphQL response data func (gtu *GraphQLTestUtils) ExtractBool(t *testing.T, response *GraphQLTestResponse, fieldPath ...string) bool { value := gtu.ExtractField(t, response, fieldPath...) if boolVal, ok := value.(bool); ok { return boolVal } t.Fatalf("Expected bool value for field %v, but got: %+v", fieldPath, value) return false } // ExtractArray extracts an array field from the GraphQL response data func (gtu *GraphQLTestUtils) ExtractArray(t *testing.T, response *GraphQLTestResponse, fieldPath ...string) []interface{} { value := gtu.ExtractField(t, response, fieldPath...) if arrVal, ok := value.([]interface{}); ok { return arrVal } t.Fatalf("Expected array value for field %v, but got: %+v", fieldPath, value) return nil }