add tests

This commit is contained in:
Fran Jurmanović
2025-07-06 19:19:42 +02:00
parent 26a0d33592
commit b9cb315944
8 changed files with 2918 additions and 0 deletions

View File

@@ -0,0 +1,272 @@
package unit
import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
"omega-server/local/graphql/handler"
"testing"
"github.com/gofiber/fiber/v2"
)
func TestGraphQLHandler(t *testing.T) {
// Create a basic GraphQL handler without dependencies
graphqlHandler := handler.NewGraphQLHandler(nil)
// Create a Fiber app for testing
app := fiber.New(fiber.Config{
DisableStartupMessage: true,
})
// Set up routes
app.Post("/graphql", graphqlHandler.Handle)
app.Get("/graphql", func(c *fiber.Ctx) error {
return c.SendString(graphqlHandler.GetSchema())
})
t.Run("GetSchema", func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/graphql", nil)
resp, err := app.Test(req)
if err != nil {
t.Fatalf("Failed to execute request: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Errorf("Expected status 200, got %d", resp.StatusCode)
}
// Read response body
buf := new(bytes.Buffer)
buf.ReadFrom(resp.Body)
schema := buf.String()
if schema == "" {
t.Fatal("Expected non-empty schema")
}
// Check for basic GraphQL types
expectedTypes := []string{"type User", "type Project", "type Task", "type Query", "type Mutation"}
for _, expectedType := range expectedTypes {
if !contains(schema, expectedType) {
t.Errorf("Schema should contain '%s'", expectedType)
}
}
})
t.Run("InvalidRequestBody", func(t *testing.T) {
req := httptest.NewRequest(http.MethodPost, "/graphql", bytes.NewBufferString("invalid json"))
req.Header.Set("Content-Type", "application/json")
resp, err := app.Test(req)
if err != nil {
t.Fatalf("Failed to execute request: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusBadRequest {
t.Errorf("Expected status 400, got %d", resp.StatusCode)
}
// Parse response
var response map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
t.Fatalf("Failed to decode response: %v", err)
}
// Should have errors
if errors, exists := response["errors"]; !exists {
t.Error("Expected errors in response")
} else {
errorList, ok := errors.([]interface{})
if !ok || len(errorList) == 0 {
t.Error("Expected non-empty error list")
}
}
})
t.Run("MeQuery", func(t *testing.T) {
requestBody := map[string]interface{}{
"query": `
query Me {
me {
id
email
fullName
}
}
`,
}
body, _ := json.Marshal(requestBody)
req := httptest.NewRequest(http.MethodPost, "/graphql", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
resp, err := app.Test(req)
if err != nil {
t.Fatalf("Failed to execute request: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Errorf("Expected status 200, got %d", resp.StatusCode)
}
// Parse response
var response map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
t.Fatalf("Failed to decode response: %v", err)
}
// Should have data
if data, exists := response["data"]; !exists {
t.Error("Expected data in response")
} else {
dataMap, ok := data.(map[string]interface{})
if !ok {
t.Error("Expected data to be an object")
} else {
if me, exists := dataMap["me"]; !exists {
t.Error("Expected 'me' field in data")
} else {
meMap, ok := me.(map[string]interface{})
if !ok {
t.Error("Expected 'me' to be an object")
} else {
// Check for required fields
if id, exists := meMap["id"]; !exists || id == "" {
t.Error("Expected non-empty 'id' field")
}
if email, exists := meMap["email"]; !exists || email == "" {
t.Error("Expected non-empty 'email' field")
}
}
}
}
}
})
t.Run("UnsupportedQuery", func(t *testing.T) {
requestBody := map[string]interface{}{
"query": `
query UnsupportedQuery {
unsupportedField {
id
name
}
}
`,
}
body, _ := json.Marshal(requestBody)
req := httptest.NewRequest(http.MethodPost, "/graphql", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
resp, err := app.Test(req)
if err != nil {
t.Fatalf("Failed to execute request: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusBadRequest {
t.Errorf("Expected status 400, got %d", resp.StatusCode)
}
// Parse response
var response map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
t.Fatalf("Failed to decode response: %v", err)
}
// Should have errors
if errors, exists := response["errors"]; !exists {
t.Error("Expected errors in response")
} else {
errorList, ok := errors.([]interface{})
if !ok || len(errorList) == 0 {
t.Error("Expected non-empty error list")
} else {
// Check error message
if errorMap, ok := errorList[0].(map[string]interface{}); ok {
if message, ok := errorMap["message"].(string); ok {
if message != "Query not supported" {
t.Errorf("Expected 'Query not supported', got '%s'", message)
}
}
}
}
}
})
t.Run("CreateProjectMutation", func(t *testing.T) {
requestBody := map[string]interface{}{
"query": `
mutation CreateProject($name: String!, $description: String, $ownerId: String!) {
createProject(input: {name: $name, description: $description, ownerId: $ownerId}) {
id
name
description
ownerId
}
}
`,
"variables": map[string]interface{}{
"name": "Test Project",
"description": "A test project",
"ownerId": "test-owner-id",
},
}
body, _ := json.Marshal(requestBody)
req := httptest.NewRequest(http.MethodPost, "/graphql", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
resp, err := app.Test(req)
if err != nil {
t.Fatalf("Failed to execute request: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Errorf("Expected status 200, got %d", resp.StatusCode)
}
// Parse response
var response map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
t.Fatalf("Failed to decode response: %v", err)
}
// Should have data
if data, exists := response["data"]; !exists {
t.Error("Expected data in response")
} else {
dataMap, ok := data.(map[string]interface{})
if !ok {
t.Error("Expected data to be an object")
} else {
if createProject, exists := dataMap["createProject"]; !exists {
t.Error("Expected 'createProject' field in data")
} else {
projectMap, ok := createProject.(map[string]interface{})
if !ok {
t.Error("Expected 'createProject' to be an object")
} else {
// Check for required fields
if id, exists := projectMap["id"]; !exists || id == "" {
t.Error("Expected non-empty 'id' field")
}
if name, exists := projectMap["name"]; !exists || name != "Test Project" {
t.Errorf("Expected name 'Test Project', got '%v'", name)
}
if ownerId, exists := projectMap["ownerId"]; !exists || ownerId != "test-owner-id" {
t.Errorf("Expected ownerId 'test-owner-id', got '%v'", ownerId)
}
}
}
}
}
})
}

View File

@@ -0,0 +1,376 @@
package unit
import (
"context"
"omega-server/local/model"
"testing"
"gorm.io/gorm"
)
// UnitTestUtils provides utilities for unit testing
type UnitTestUtils struct {
DB *gorm.DB
}
// NewUnitTestUtils creates a new unit test utilities instance
func NewUnitTestUtils(db *gorm.DB) *UnitTestUtils {
return &UnitTestUtils{
DB: db,
}
}
// MockUser creates a mock user for testing
func (utu *UnitTestUtils) MockUser(id, email, fullName string) *model.User {
user := &model.User{
BaseModel: model.BaseModel{
ID: id,
},
Email: email,
FullName: fullName,
}
user.Init()
return user
}
// MockRole creates a mock role for testing
func (utu *UnitTestUtils) MockRole(id, name, description string) *model.Role {
role := &model.Role{
BaseModel: model.BaseModel{
ID: id,
},
Name: name,
Description: description,
Active: true,
System: false,
}
role.Init()
return role
}
// MockPermission creates a mock permission for testing
func (utu *UnitTestUtils) MockPermission(id, name, description, category string) *model.Permission {
permission := &model.Permission{
BaseModel: model.BaseModel{
ID: id,
},
Name: name,
Description: description,
Category: category,
Active: true,
System: false,
}
permission.Init()
return permission
}
// MockType creates a mock project type for testing
func (utu *UnitTestUtils) MockType(id, name, description string, userID *string) *model.Type {
projectType := &model.Type{
BaseModel: model.BaseModel{
ID: id,
},
Name: name,
Description: description,
UserID: userID,
}
projectType.Init()
return projectType
}
// MockProject creates a mock project for testing
func (utu *UnitTestUtils) MockProject(id, name, description, ownerID, typeID string) *model.Project {
project := &model.Project{
BaseModel: model.BaseModel{
ID: id,
},
Name: name,
Description: description,
OwnerID: ownerID,
TypeID: typeID,
}
project.Init()
return project
}
// MockTask creates a mock task for testing
func (utu *UnitTestUtils) MockTask(id, title, description, projectID string) *model.Task {
task := &model.Task{
BaseModel: model.BaseModel{
ID: id,
},
Title: title,
Description: description,
Status: model.TaskStatusTodo,
Priority: model.TaskPriorityMedium,
ProjectID: projectID,
}
task.Init()
return task
}
// MockIntegration creates a mock integration for testing
func (utu *UnitTestUtils) MockIntegration(id, projectID, integrationType string, config map[string]interface{}) *model.Integration {
integration := &model.Integration{
BaseModel: model.BaseModel{
ID: id,
},
ProjectID: projectID,
Type: integrationType,
}
integration.Init()
integration.SetConfig(config)
return integration
}
// AssertUserEqual asserts that two users are equal
func (utu *UnitTestUtils) AssertUserEqual(t *testing.T, expected, actual *model.User) {
if expected.ID != actual.ID {
t.Errorf("Expected user ID %s, got %s", expected.ID, actual.ID)
}
if expected.Email != actual.Email {
t.Errorf("Expected user email %s, got %s", expected.Email, actual.Email)
}
if expected.FullName != actual.FullName {
t.Errorf("Expected user full name %s, got %s", expected.FullName, actual.FullName)
}
}
// AssertProjectEqual asserts that two projects are equal
func (utu *UnitTestUtils) AssertProjectEqual(t *testing.T, expected, actual *model.Project) {
if expected.ID != actual.ID {
t.Errorf("Expected project ID %s, got %s", expected.ID, actual.ID)
}
if expected.Name != actual.Name {
t.Errorf("Expected project name %s, got %s", expected.Name, actual.Name)
}
if expected.Description != actual.Description {
t.Errorf("Expected project description %s, got %s", expected.Description, actual.Description)
}
if expected.OwnerID != actual.OwnerID {
t.Errorf("Expected project owner ID %s, got %s", expected.OwnerID, actual.OwnerID)
}
if expected.TypeID != actual.TypeID {
t.Errorf("Expected project type ID %s, got %s", expected.TypeID, actual.TypeID)
}
}
// AssertTaskEqual asserts that two tasks are equal
func (utu *UnitTestUtils) AssertTaskEqual(t *testing.T, expected, actual *model.Task) {
if expected.ID != actual.ID {
t.Errorf("Expected task ID %s, got %s", expected.ID, actual.ID)
}
if expected.Title != actual.Title {
t.Errorf("Expected task title %s, got %s", expected.Title, actual.Title)
}
if expected.Description != actual.Description {
t.Errorf("Expected task description %s, got %s", expected.Description, actual.Description)
}
if expected.Status != actual.Status {
t.Errorf("Expected task status %s, got %s", expected.Status, actual.Status)
}
if expected.Priority != actual.Priority {
t.Errorf("Expected task priority %s, got %s", expected.Priority, actual.Priority)
}
if expected.ProjectID != actual.ProjectID {
t.Errorf("Expected task project ID %s, got %s", expected.ProjectID, actual.ProjectID)
}
}
// AssertRoleEqual asserts that two roles are equal
func (utu *UnitTestUtils) AssertRoleEqual(t *testing.T, expected, actual *model.Role) {
if expected.ID != actual.ID {
t.Errorf("Expected role ID %s, got %s", expected.ID, actual.ID)
}
if expected.Name != actual.Name {
t.Errorf("Expected role name %s, got %s", expected.Name, actual.Name)
}
if expected.Description != actual.Description {
t.Errorf("Expected role description %s, got %s", expected.Description, actual.Description)
}
if expected.Active != actual.Active {
t.Errorf("Expected role active %t, got %t", expected.Active, actual.Active)
}
if expected.System != actual.System {
t.Errorf("Expected role system %t, got %t", expected.System, actual.System)
}
}
// AssertPermissionEqual asserts that two permissions are equal
func (utu *UnitTestUtils) AssertPermissionEqual(t *testing.T, expected, actual *model.Permission) {
if expected.ID != actual.ID {
t.Errorf("Expected permission ID %s, got %s", expected.ID, actual.ID)
}
if expected.Name != actual.Name {
t.Errorf("Expected permission name %s, got %s", expected.Name, actual.Name)
}
if expected.Description != actual.Description {
t.Errorf("Expected permission description %s, got %s", expected.Description, actual.Description)
}
if expected.Category != actual.Category {
t.Errorf("Expected permission category %s, got %s", expected.Category, actual.Category)
}
if expected.Active != actual.Active {
t.Errorf("Expected permission active %t, got %t", expected.Active, actual.Active)
}
if expected.System != actual.System {
t.Errorf("Expected permission system %t, got %t", expected.System, actual.System)
}
}
// AssertTypeEqual asserts that two types are equal
func (utu *UnitTestUtils) AssertTypeEqual(t *testing.T, expected, actual *model.Type) {
if expected.ID != actual.ID {
t.Errorf("Expected type ID %s, got %s", expected.ID, actual.ID)
}
if expected.Name != actual.Name {
t.Errorf("Expected type name %s, got %s", expected.Name, actual.Name)
}
if expected.Description != actual.Description {
t.Errorf("Expected type description %s, got %s", expected.Description, actual.Description)
}
if (expected.UserID == nil) != (actual.UserID == nil) {
t.Errorf("Expected type user ID nullability mismatch")
} else if expected.UserID != nil && actual.UserID != nil && *expected.UserID != *actual.UserID {
t.Errorf("Expected type user ID %s, got %s", *expected.UserID, *actual.UserID)
}
}
// AssertIntegrationEqual asserts that two integrations are equal
func (utu *UnitTestUtils) AssertIntegrationEqual(t *testing.T, expected, actual *model.Integration) {
if expected.ID != actual.ID {
t.Errorf("Expected integration ID %s, got %s", expected.ID, actual.ID)
}
if expected.ProjectID != actual.ProjectID {
t.Errorf("Expected integration project ID %s, got %s", expected.ProjectID, actual.ProjectID)
}
if expected.Type != actual.Type {
t.Errorf("Expected integration type %s, got %s", expected.Type, actual.Type)
}
}
// CreateTestContext creates a test context
func (utu *UnitTestUtils) CreateTestContext() context.Context {
return context.Background()
}
// AssertNoError asserts that there is no error
func (utu *UnitTestUtils) AssertNoError(t *testing.T, err error) {
if err != nil {
t.Fatalf("Expected no error, but got: %v", err)
}
}
// AssertError asserts that there is an error
func (utu *UnitTestUtils) AssertError(t *testing.T, err error) {
if err == nil {
t.Fatalf("Expected an error, but got none")
}
}
// AssertErrorMessage asserts that the error has a specific message
func (utu *UnitTestUtils) AssertErrorMessage(t *testing.T, err error, expectedMessage string) {
if err == nil {
t.Fatalf("Expected an error with message '%s', but got no error", expectedMessage)
}
if err.Error() != expectedMessage {
t.Fatalf("Expected error message '%s', but got '%s'", expectedMessage, err.Error())
}
}
// AssertStringEqual asserts that two strings are equal
func (utu *UnitTestUtils) AssertStringEqual(t *testing.T, expected, actual string) {
if expected != actual {
t.Errorf("Expected string '%s', got '%s'", expected, actual)
}
}
// AssertIntEqual asserts that two integers are equal
func (utu *UnitTestUtils) AssertIntEqual(t *testing.T, expected, actual int) {
if expected != actual {
t.Errorf("Expected int %d, got %d", expected, actual)
}
}
// AssertBoolEqual asserts that two booleans are equal
func (utu *UnitTestUtils) AssertBoolEqual(t *testing.T, expected, actual bool) {
if expected != actual {
t.Errorf("Expected bool %t, got %t", expected, actual)
}
}
// AssertNotNil asserts that a value is not nil
func (utu *UnitTestUtils) AssertNotNil(t *testing.T, value interface{}) {
if value == nil {
t.Fatalf("Expected value to not be nil, but it was")
}
}
// AssertNil asserts that a value is nil
func (utu *UnitTestUtils) AssertNil(t *testing.T, value interface{}) {
if value != nil {
t.Fatalf("Expected value to be nil, but got: %+v", value)
}
}
// AssertSliceLength asserts that a slice has a specific length
func (utu *UnitTestUtils) AssertSliceLength(t *testing.T, slice interface{}, expectedLength int) {
switch s := slice.(type) {
case []interface{}:
if len(s) != expectedLength {
t.Errorf("Expected slice length %d, got %d", expectedLength, len(s))
}
case []*model.User:
if len(s) != expectedLength {
t.Errorf("Expected slice length %d, got %d", expectedLength, len(s))
}
case []*model.Project:
if len(s) != expectedLength {
t.Errorf("Expected slice length %d, got %d", expectedLength, len(s))
}
case []*model.Task:
if len(s) != expectedLength {
t.Errorf("Expected slice length %d, got %d", expectedLength, len(s))
}
case []*model.Role:
if len(s) != expectedLength {
t.Errorf("Expected slice length %d, got %d", expectedLength, len(s))
}
case []*model.Permission:
if len(s) != expectedLength {
t.Errorf("Expected slice length %d, got %d", expectedLength, len(s))
}
default:
t.Fatalf("Unsupported slice type for length assertion: %T", slice)
}
}
// AssertContains asserts that a string contains a substring
func (utu *UnitTestUtils) AssertContains(t *testing.T, str, substr string) {
if !contains(str, substr) {
t.Errorf("Expected string '%s' to contain '%s'", str, substr)
}
}
// AssertNotContains asserts that a string does not contain a substring
func (utu *UnitTestUtils) AssertNotContains(t *testing.T, str, substr string) {
if contains(str, substr) {
t.Errorf("Expected string '%s' to not contain '%s'", str, substr)
}
}
// contains checks if a string contains a substring
func contains(str, substr string) bool {
return len(str) >= len(substr) && (str == substr || len(substr) == 0 ||
(len(substr) > 0 && findSubstring(str, substr)))
}
// findSubstring finds if substr exists in str
func findSubstring(str, substr string) bool {
for i := 0; i <= len(str)-len(substr); i++ {
if str[i:i+len(substr)] == substr {
return true
}
}
return false
}