add tests
This commit is contained in:
373
tests/integration/graphql_integration_test.go
Normal file
373
tests/integration/graphql_integration_test.go
Normal file
@@ -0,0 +1,373 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"omega-server/local/api"
|
||||
"omega-server/local/model"
|
||||
"omega-server/local/repository"
|
||||
"omega-server/local/service"
|
||||
"omega-server/tests"
|
||||
"testing"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func TestGraphQLIntegration(t *testing.T) {
|
||||
// Initialize test suite
|
||||
testSuite := tests.NewTestSuite()
|
||||
testSuite.Setup(t)
|
||||
defer testSuite.Teardown(t)
|
||||
|
||||
// Skip if no test database
|
||||
testSuite.SkipIfNoTestDB(t)
|
||||
|
||||
// Create Fiber app
|
||||
app := fiber.New(fiber.Config{
|
||||
DisableStartupMessage: true,
|
||||
})
|
||||
|
||||
// Initialize services and repositories
|
||||
membershipRepo := repository.NewMembershipRepository(testSuite.DB)
|
||||
membershipService := service.NewMembershipService(membershipRepo)
|
||||
|
||||
// Provide services to DI container
|
||||
err := testSuite.DI.Provide(func() *service.MembershipService {
|
||||
return membershipService
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to provide membership service: %v", err)
|
||||
}
|
||||
|
||||
// Initialize API routes
|
||||
api.Init(testSuite.DI, app)
|
||||
|
||||
// Create integration test utils
|
||||
integrationUtils := NewIntegrationTestUtils(app, testSuite.DB, membershipService)
|
||||
|
||||
t.Run("GraphQLSchemaEndpoint", func(t *testing.T) {
|
||||
req := TestRequest{
|
||||
Method: "GET",
|
||||
URL: "/v1/graphql",
|
||||
}
|
||||
|
||||
resp := integrationUtils.ExecuteRequest(t, req)
|
||||
integrationUtils.AssertStatusCode(t, resp, 200)
|
||||
integrationUtils.AssertResponseBody(t, resp, "type User")
|
||||
integrationUtils.AssertResponseBody(t, resp, "type Query")
|
||||
integrationUtils.AssertResponseBody(t, resp, "type Mutation")
|
||||
})
|
||||
|
||||
t.Run("GraphQLLoginMutation", func(t *testing.T) {
|
||||
// First create a user through the service
|
||||
user := &model.User{
|
||||
Email: "graphql@example.com",
|
||||
FullName: "GraphQL User",
|
||||
}
|
||||
|
||||
if err := user.SetPassword("password123"); err != nil {
|
||||
t.Fatalf("Failed to set password: %v", err)
|
||||
}
|
||||
|
||||
_, err := membershipService.CreateUser(
|
||||
testSuite.TestContext(),
|
||||
user,
|
||||
[]string{"user"},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test user: %v", err)
|
||||
}
|
||||
|
||||
// Test GraphQL login mutation
|
||||
req := TestRequest{
|
||||
Method: "POST",
|
||||
URL: "/v1/graphql",
|
||||
Body: map[string]interface{}{
|
||||
"query": `
|
||||
mutation Login($email: String!, $password: String!) {
|
||||
login(input: {email: $email, password: $password}) {
|
||||
token
|
||||
user {
|
||||
id
|
||||
email
|
||||
fullName
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
"variables": map[string]interface{}{
|
||||
"email": "graphql@example.com",
|
||||
"password": "password123",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
resp := integrationUtils.ExecuteRequest(t, req)
|
||||
integrationUtils.AssertStatusCode(t, resp, 200)
|
||||
|
||||
// Parse response and check for token
|
||||
data := integrationUtils.ParseJSONResponse(t, resp)
|
||||
if errors, exists := data["errors"]; exists {
|
||||
t.Fatalf("GraphQL returned errors: %v", errors)
|
||||
}
|
||||
|
||||
loginData := integrationUtils.ExtractJSONField(t, resp, "data", "login")
|
||||
if loginData == nil {
|
||||
t.Fatal("Expected login data in response")
|
||||
}
|
||||
|
||||
token := integrationUtils.ExtractStringField(t, resp, "data", "login", "token")
|
||||
if token == "" {
|
||||
t.Error("Expected non-empty token")
|
||||
}
|
||||
|
||||
userEmail := integrationUtils.ExtractStringField(t, resp, "data", "login", "user", "email")
|
||||
if userEmail != "graphql@example.com" {
|
||||
t.Errorf("Expected email 'graphql@example.com', got '%s'", userEmail)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("GraphQLCreateUserMutation", func(t *testing.T) {
|
||||
req := TestRequest{
|
||||
Method: "POST",
|
||||
URL: "/v1/graphql",
|
||||
Body: map[string]interface{}{
|
||||
"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": "newuser@example.com",
|
||||
"password": "password123",
|
||||
"fullName": "New User",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
resp := integrationUtils.ExecuteRequest(t, req)
|
||||
integrationUtils.AssertStatusCode(t, resp, 200)
|
||||
|
||||
// Parse response and verify user creation
|
||||
data := integrationUtils.ParseJSONResponse(t, resp)
|
||||
if errors, exists := data["errors"]; exists {
|
||||
t.Fatalf("GraphQL returned errors: %v", errors)
|
||||
}
|
||||
|
||||
userEmail := integrationUtils.ExtractStringField(t, resp, "data", "createUser", "email")
|
||||
if userEmail != "newuser@example.com" {
|
||||
t.Errorf("Expected email 'newuser@example.com', got '%s'", userEmail)
|
||||
}
|
||||
|
||||
userFullName := integrationUtils.ExtractStringField(t, resp, "data", "createUser", "fullName")
|
||||
if userFullName != "New User" {
|
||||
t.Errorf("Expected full name 'New User', got '%s'", userFullName)
|
||||
}
|
||||
|
||||
userID := integrationUtils.ExtractStringField(t, resp, "data", "createUser", "id")
|
||||
if userID == "" {
|
||||
t.Error("Expected non-empty user ID")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("GraphQLMeQuery", func(t *testing.T) {
|
||||
req := TestRequest{
|
||||
Method: "POST",
|
||||
URL: "/v1/graphql",
|
||||
Body: map[string]interface{}{
|
||||
"query": `
|
||||
query Me {
|
||||
me {
|
||||
id
|
||||
email
|
||||
fullName
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
resp := integrationUtils.ExecuteRequest(t, req)
|
||||
integrationUtils.AssertStatusCode(t, resp, 200)
|
||||
|
||||
// Parse response
|
||||
data := integrationUtils.ParseJSONResponse(t, resp)
|
||||
if errors, exists := data["errors"]; exists {
|
||||
t.Fatalf("GraphQL returned errors: %v", errors)
|
||||
}
|
||||
|
||||
userEmail := integrationUtils.ExtractStringField(t, resp, "data", "me", "email")
|
||||
if userEmail == "" {
|
||||
t.Error("Expected non-empty email")
|
||||
}
|
||||
|
||||
userID := integrationUtils.ExtractStringField(t, resp, "data", "me", "id")
|
||||
if userID == "" {
|
||||
t.Error("Expected non-empty user ID")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("GraphQLInvalidQuery", func(t *testing.T) {
|
||||
req := TestRequest{
|
||||
Method: "POST",
|
||||
URL: "/v1/graphql",
|
||||
Body: map[string]interface{}{
|
||||
"query": "invalid query syntax {",
|
||||
},
|
||||
}
|
||||
|
||||
resp := integrationUtils.ExecuteRequest(t, req)
|
||||
integrationUtils.AssertStatusCode(t, resp, 400)
|
||||
|
||||
// Should have errors in response
|
||||
data := integrationUtils.ParseJSONResponse(t, resp)
|
||||
if errors, exists := data["errors"]; !exists {
|
||||
t.Fatal("Expected errors in response for invalid query")
|
||||
} else {
|
||||
errorList, ok := errors.([]interface{})
|
||||
if !ok || len(errorList) == 0 {
|
||||
t.Fatal("Expected non-empty error list")
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("GraphQLUnsupportedQuery", func(t *testing.T) {
|
||||
req := TestRequest{
|
||||
Method: "POST",
|
||||
URL: "/v1/graphql",
|
||||
Body: map[string]interface{}{
|
||||
"query": `
|
||||
query UnsupportedQuery {
|
||||
unsupportedField {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
resp := integrationUtils.ExecuteRequest(t, req)
|
||||
integrationUtils.AssertStatusCode(t, resp, 400)
|
||||
|
||||
// Should return "Query not supported" error
|
||||
data := integrationUtils.ParseJSONResponse(t, resp)
|
||||
if errors, exists := data["errors"]; exists {
|
||||
errorList, ok := errors.([]interface{})
|
||||
if ok && len(errorList) > 0 {
|
||||
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' error, got '%s'", message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("GraphQLCreateProjectMutation", func(t *testing.T) {
|
||||
req := TestRequest{
|
||||
Method: "POST",
|
||||
URL: "/v1/graphql",
|
||||
Body: map[string]interface{}{
|
||||
"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": "Integration Test Project",
|
||||
"description": "A project created during integration testing",
|
||||
"ownerId": "test-owner-id",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
resp := integrationUtils.ExecuteRequest(t, req)
|
||||
integrationUtils.AssertStatusCode(t, resp, 200)
|
||||
|
||||
// Parse response (should work since it's mocked)
|
||||
data := integrationUtils.ParseJSONResponse(t, resp)
|
||||
if errors, exists := data["errors"]; exists {
|
||||
t.Fatalf("GraphQL returned errors: %v", errors)
|
||||
}
|
||||
|
||||
projectName := integrationUtils.ExtractStringField(t, resp, "data", "createProject", "name")
|
||||
if projectName != "Integration Test Project" {
|
||||
t.Errorf("Expected project name 'Integration Test Project', got '%s'", projectName)
|
||||
}
|
||||
|
||||
projectID := integrationUtils.ExtractStringField(t, resp, "data", "createProject", "id")
|
||||
if projectID == "" {
|
||||
t.Error("Expected non-empty project ID")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("GraphQLCreateTaskMutation", func(t *testing.T) {
|
||||
req := TestRequest{
|
||||
Method: "POST",
|
||||
URL: "/v1/graphql",
|
||||
Body: map[string]interface{}{
|
||||
"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": "Integration Test Task",
|
||||
"description": "A task created during integration testing",
|
||||
"status": "todo",
|
||||
"priority": "medium",
|
||||
"projectId": "test-project-id",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
resp := integrationUtils.ExecuteRequest(t, req)
|
||||
integrationUtils.AssertStatusCode(t, resp, 200)
|
||||
|
||||
// Parse response (should work since it's mocked)
|
||||
data := integrationUtils.ParseJSONResponse(t, resp)
|
||||
if errors, exists := data["errors"]; exists {
|
||||
t.Fatalf("GraphQL returned errors: %v", errors)
|
||||
}
|
||||
|
||||
taskTitle := integrationUtils.ExtractStringField(t, resp, "data", "createTask", "title")
|
||||
if taskTitle != "Integration Test Task" {
|
||||
t.Errorf("Expected task title 'Integration Test Task', got '%s'", taskTitle)
|
||||
}
|
||||
|
||||
taskStatus := integrationUtils.ExtractStringField(t, resp, "data", "createTask", "status")
|
||||
if taskStatus != "todo" {
|
||||
t.Errorf("Expected task status 'todo', got '%s'", taskStatus)
|
||||
}
|
||||
|
||||
taskID := integrationUtils.ExtractStringField(t, resp, "data", "createTask", "id")
|
||||
if taskID == "" {
|
||||
t.Error("Expected non-empty task ID")
|
||||
}
|
||||
})
|
||||
}
|
||||
463
tests/integration/integration_test_utils.go
Normal file
463
tests/integration/integration_test_utils.go
Normal file
@@ -0,0 +1,463 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"omega-server/local/model"
|
||||
"omega-server/local/service"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// IntegrationTestUtils provides utilities for integration testing
|
||||
type IntegrationTestUtils struct {
|
||||
App *fiber.App
|
||||
DB *gorm.DB
|
||||
MembershipService *service.MembershipService
|
||||
}
|
||||
|
||||
// TestRequest represents an HTTP test request
|
||||
type TestRequest struct {
|
||||
Method string
|
||||
URL string
|
||||
Body interface{}
|
||||
Headers map[string]string
|
||||
}
|
||||
|
||||
// TestResponse represents an HTTP test response
|
||||
type TestResponse struct {
|
||||
StatusCode int
|
||||
Body string
|
||||
Headers map[string]string
|
||||
}
|
||||
|
||||
// NewIntegrationTestUtils creates a new integration test utilities instance
|
||||
func NewIntegrationTestUtils(app *fiber.App, db *gorm.DB, membershipService *service.MembershipService) *IntegrationTestUtils {
|
||||
return &IntegrationTestUtils{
|
||||
App: app,
|
||||
DB: db,
|
||||
MembershipService: membershipService,
|
||||
}
|
||||
}
|
||||
|
||||
// ExecuteRequest executes an HTTP request and returns the response
|
||||
func (itu *IntegrationTestUtils) ExecuteRequest(t *testing.T, req TestRequest) *TestResponse {
|
||||
var body []byte
|
||||
var err error
|
||||
|
||||
if req.Body != nil {
|
||||
body, err = json.Marshal(req.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to marshal request body: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
httpReq := httptest.NewRequest(req.Method, req.URL, bytes.NewBuffer(body))
|
||||
|
||||
// Set default headers
|
||||
httpReq.Header.Set("Content-Type", "application/json")
|
||||
|
||||
// Set custom headers
|
||||
for key, value := range req.Headers {
|
||||
httpReq.Header.Set(key, value)
|
||||
}
|
||||
|
||||
resp, err := itu.App.Test(httpReq)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to execute request: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Read response body
|
||||
buf := new(bytes.Buffer)
|
||||
buf.ReadFrom(resp.Body)
|
||||
responseBody := buf.String()
|
||||
|
||||
// Extract response headers
|
||||
responseHeaders := make(map[string]string)
|
||||
for key, values := range resp.Header {
|
||||
if len(values) > 0 {
|
||||
responseHeaders[key] = values[0]
|
||||
}
|
||||
}
|
||||
|
||||
return &TestResponse{
|
||||
StatusCode: resp.StatusCode,
|
||||
Body: responseBody,
|
||||
Headers: responseHeaders,
|
||||
}
|
||||
}
|
||||
|
||||
// ExecuteRequestWithAuth executes an HTTP request with authentication
|
||||
func (itu *IntegrationTestUtils) ExecuteRequestWithAuth(t *testing.T, req TestRequest, token string) *TestResponse {
|
||||
if req.Headers == nil {
|
||||
req.Headers = make(map[string]string)
|
||||
}
|
||||
req.Headers["Authorization"] = "Bearer " + token
|
||||
|
||||
return itu.ExecuteRequest(t, req)
|
||||
}
|
||||
|
||||
// LoginUser logs in a user and returns the auth token
|
||||
func (itu *IntegrationTestUtils) LoginUser(t *testing.T, email, password string) string {
|
||||
loginReq := TestRequest{
|
||||
Method: "POST",
|
||||
URL: "/v1/auth/login",
|
||||
Body: map[string]interface{}{
|
||||
"email": email,
|
||||
"password": password,
|
||||
},
|
||||
}
|
||||
|
||||
resp := itu.ExecuteRequest(t, loginReq)
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Fatalf("Failed to login user: status %d, body: %s", resp.StatusCode, resp.Body)
|
||||
}
|
||||
|
||||
var loginResp map[string]interface{}
|
||||
if err := json.Unmarshal([]byte(resp.Body), &loginResp); err != nil {
|
||||
t.Fatalf("Failed to unmarshal login response: %v", err)
|
||||
}
|
||||
|
||||
token, ok := loginResp["token"].(string)
|
||||
if !ok {
|
||||
t.Fatalf("Failed to extract token from login response: %+v", loginResp)
|
||||
}
|
||||
|
||||
return token
|
||||
}
|
||||
|
||||
// CreateTestUserWithAuth creates a test user and returns auth token
|
||||
func (itu *IntegrationTestUtils) CreateTestUserWithAuth(t *testing.T, email, fullName, password string) (string, *model.User) {
|
||||
// Create domain model
|
||||
user := &model.User{
|
||||
Email: email,
|
||||
FullName: fullName,
|
||||
}
|
||||
|
||||
if err := user.SetPassword(password); err != nil {
|
||||
t.Fatalf("Failed to set password: %v", err)
|
||||
}
|
||||
|
||||
// Create user
|
||||
createdUser, err := itu.MembershipService.CreateUser(context.Background(), user, []string{"user"})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test user: %v", err)
|
||||
}
|
||||
|
||||
// Login to get token
|
||||
token := itu.LoginUser(t, email, password)
|
||||
|
||||
return token, createdUser
|
||||
}
|
||||
|
||||
// CreateTestAdmin creates a test admin user and returns auth token
|
||||
func (itu *IntegrationTestUtils) CreateTestAdmin(t *testing.T, email, fullName, password string) (string, *model.User) {
|
||||
// Create domain model
|
||||
user := &model.User{
|
||||
Email: email,
|
||||
FullName: fullName,
|
||||
}
|
||||
|
||||
if err := user.SetPassword(password); err != nil {
|
||||
t.Fatalf("Failed to set password: %v", err)
|
||||
}
|
||||
|
||||
// Create admin user
|
||||
createdUser, err := itu.MembershipService.CreateUser(context.Background(), user, []string{"admin"})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test admin: %v", err)
|
||||
}
|
||||
|
||||
// Login to get token
|
||||
token := itu.LoginUser(t, email, password)
|
||||
|
||||
return token, createdUser
|
||||
}
|
||||
|
||||
// AssertStatusCode asserts that the response has the expected status code
|
||||
func (itu *IntegrationTestUtils) AssertStatusCode(t *testing.T, resp *TestResponse, expectedStatusCode int) {
|
||||
if resp.StatusCode != expectedStatusCode {
|
||||
t.Errorf("Expected status code %d, got %d. Response body: %s", expectedStatusCode, resp.StatusCode, resp.Body)
|
||||
}
|
||||
}
|
||||
|
||||
// AssertResponseBody asserts that the response body contains expected content
|
||||
func (itu *IntegrationTestUtils) AssertResponseBody(t *testing.T, resp *TestResponse, expectedContent string) {
|
||||
if !contains(resp.Body, expectedContent) {
|
||||
t.Errorf("Expected response body to contain '%s', but got: %s", expectedContent, resp.Body)
|
||||
}
|
||||
}
|
||||
|
||||
// AssertResponseNotContains asserts that the response body does not contain specific content
|
||||
func (itu *IntegrationTestUtils) AssertResponseNotContains(t *testing.T, resp *TestResponse, content string) {
|
||||
if contains(resp.Body, content) {
|
||||
t.Errorf("Expected response body to not contain '%s', but it did: %s", content, resp.Body)
|
||||
}
|
||||
}
|
||||
|
||||
// AssertResponseJSON asserts that the response body is valid JSON and matches expected structure
|
||||
func (itu *IntegrationTestUtils) AssertResponseJSON(t *testing.T, resp *TestResponse, expectedJSON interface{}) {
|
||||
var actualJSON interface{}
|
||||
if err := json.Unmarshal([]byte(resp.Body), &actualJSON); err != nil {
|
||||
t.Fatalf("Failed to unmarshal response body as JSON: %v. Body: %s", err, resp.Body)
|
||||
}
|
||||
|
||||
expectedBytes, err := json.Marshal(expectedJSON)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to marshal expected JSON: %v", err)
|
||||
}
|
||||
|
||||
actualBytes, err := json.Marshal(actualJSON)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to marshal actual JSON: %v", err)
|
||||
}
|
||||
|
||||
if string(expectedBytes) != string(actualBytes) {
|
||||
t.Errorf("Expected JSON: %s, got: %s", string(expectedBytes), string(actualBytes))
|
||||
}
|
||||
}
|
||||
|
||||
// AssertHeader asserts that the response has a specific header with expected value
|
||||
func (itu *IntegrationTestUtils) AssertHeader(t *testing.T, resp *TestResponse, headerName, expectedValue string) {
|
||||
actualValue, exists := resp.Headers[headerName]
|
||||
if !exists {
|
||||
t.Errorf("Expected header '%s' to exist, but it doesn't", headerName)
|
||||
return
|
||||
}
|
||||
|
||||
if actualValue != expectedValue {
|
||||
t.Errorf("Expected header '%s' to have value '%s', got '%s'", headerName, expectedValue, actualValue)
|
||||
}
|
||||
}
|
||||
|
||||
// ParseJSONResponse parses the response body as JSON
|
||||
func (itu *IntegrationTestUtils) ParseJSONResponse(t *testing.T, resp *TestResponse) map[string]interface{} {
|
||||
var result map[string]interface{}
|
||||
if err := json.Unmarshal([]byte(resp.Body), &result); err != nil {
|
||||
t.Fatalf("Failed to parse response body as JSON: %v. Body: %s", err, resp.Body)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// ExtractJSONField extracts a field from JSON response
|
||||
func (itu *IntegrationTestUtils) ExtractJSONField(t *testing.T, resp *TestResponse, fieldPath ...string) interface{} {
|
||||
data := itu.ParseJSONResponse(t, resp)
|
||||
|
||||
var result interface{} = data
|
||||
for _, field := range fieldPath {
|
||||
if dataMap, ok := result.(map[string]interface{}); ok {
|
||||
if value, exists := dataMap[field]; exists {
|
||||
result = value
|
||||
} else {
|
||||
t.Fatalf("Field '%s' not found in JSON response: %+v", field, dataMap)
|
||||
}
|
||||
} else {
|
||||
t.Fatalf("Cannot extract field '%s' from non-map data: %+v", field, result)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// ExtractStringField extracts a string field from JSON response
|
||||
func (itu *IntegrationTestUtils) ExtractStringField(t *testing.T, resp *TestResponse, fieldPath ...string) string {
|
||||
value := itu.ExtractJSONField(t, resp, fieldPath...)
|
||||
if str, ok := value.(string); ok {
|
||||
return str
|
||||
}
|
||||
t.Fatalf("Expected string value for field %v, but got: %+v", fieldPath, value)
|
||||
return ""
|
||||
}
|
||||
|
||||
// ExtractIntField extracts an integer field from JSON response
|
||||
func (itu *IntegrationTestUtils) ExtractIntField(t *testing.T, resp *TestResponse, fieldPath ...string) int {
|
||||
value := itu.ExtractJSONField(t, resp, 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
|
||||
}
|
||||
|
||||
// ExtractBoolField extracts a boolean field from JSON response
|
||||
func (itu *IntegrationTestUtils) ExtractBoolField(t *testing.T, resp *TestResponse, fieldPath ...string) bool {
|
||||
value := itu.ExtractJSONField(t, resp, fieldPath...)
|
||||
if boolVal, ok := value.(bool); ok {
|
||||
return boolVal
|
||||
}
|
||||
t.Fatalf("Expected bool value for field %v, but got: %+v", fieldPath, value)
|
||||
return false
|
||||
}
|
||||
|
||||
// WaitForCondition waits for a condition to be true with timeout
|
||||
func (itu *IntegrationTestUtils) WaitForCondition(t *testing.T, condition func() bool, timeout time.Duration, message string) {
|
||||
start := time.Now()
|
||||
for time.Since(start) < timeout {
|
||||
if condition() {
|
||||
return
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
t.Fatalf("Condition not met within timeout: %s", message)
|
||||
}
|
||||
|
||||
// CleanupDatabase cleans up test data from database
|
||||
func (itu *IntegrationTestUtils) CleanupDatabase(t *testing.T) {
|
||||
// Clean up in reverse order of dependencies
|
||||
tables := []string{
|
||||
"task_assignees",
|
||||
"project_members",
|
||||
"integrations",
|
||||
"tasks",
|
||||
"projects",
|
||||
"types",
|
||||
"user_roles",
|
||||
"role_permissions",
|
||||
"users",
|
||||
"roles",
|
||||
"permissions",
|
||||
"system_configs",
|
||||
"audit_logs",
|
||||
"security_events",
|
||||
}
|
||||
|
||||
for _, table := range tables {
|
||||
err := itu.DB.Exec(fmt.Sprintf("DELETE FROM %s", table)).Error
|
||||
if err != nil {
|
||||
t.Logf("Warning: Failed to clean table %s: %v", table, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SeedTestData seeds the database with test data
|
||||
func (itu *IntegrationTestUtils) SeedTestData(t *testing.T) {
|
||||
// Create test roles
|
||||
itu.CreateTestRole(t, "admin", "Administrator role")
|
||||
itu.CreateTestRole(t, "user", "Regular user role")
|
||||
|
||||
// Create test permissions
|
||||
itu.CreateTestPermission(t, "user:read", "Read user data", "user")
|
||||
itu.CreateTestPermission(t, "user:write", "Write user data", "user")
|
||||
itu.CreateTestPermission(t, "project:read", "Read project data", "project")
|
||||
itu.CreateTestPermission(t, "project:write", "Write project data", "project")
|
||||
|
||||
// Create test project types
|
||||
itu.CreateTestType(t, "Web Development", "Standard web development project", nil)
|
||||
itu.CreateTestType(t, "Mobile App", "Mobile application development", nil)
|
||||
}
|
||||
|
||||
// CreateTestRole creates a test role in the database
|
||||
func (itu *IntegrationTestUtils) CreateTestRole(t *testing.T, name, description string) *model.Role {
|
||||
role := &model.Role{
|
||||
Name: name,
|
||||
Description: description,
|
||||
Active: true,
|
||||
}
|
||||
role.Init()
|
||||
|
||||
err := itu.DB.Create(role).Error
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test role: %v", err)
|
||||
}
|
||||
|
||||
return role
|
||||
}
|
||||
|
||||
// CreateTestPermission creates a test permission in the database
|
||||
func (itu *IntegrationTestUtils) CreateTestPermission(t *testing.T, name, description, category string) *model.Permission {
|
||||
permission := &model.Permission{
|
||||
Name: name,
|
||||
Description: description,
|
||||
Category: category,
|
||||
Active: true,
|
||||
}
|
||||
permission.Init()
|
||||
|
||||
err := itu.DB.Create(permission).Error
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test permission: %v", err)
|
||||
}
|
||||
|
||||
return permission
|
||||
}
|
||||
|
||||
// CreateTestType creates a test project type in the database
|
||||
func (itu *IntegrationTestUtils) CreateTestType(t *testing.T, name, description string, userID *string) *model.Type {
|
||||
projectType := &model.Type{
|
||||
Name: name,
|
||||
Description: description,
|
||||
UserID: userID,
|
||||
}
|
||||
projectType.Init()
|
||||
|
||||
err := itu.DB.Create(projectType).Error
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test type: %v", err)
|
||||
}
|
||||
|
||||
return projectType
|
||||
}
|
||||
|
||||
// CreateTestProject creates a test project in the database
|
||||
func (itu *IntegrationTestUtils) CreateTestProject(t *testing.T, name, description, ownerID, typeID string) *model.Project {
|
||||
project := &model.Project{
|
||||
Name: name,
|
||||
Description: description,
|
||||
OwnerID: ownerID,
|
||||
TypeID: typeID,
|
||||
}
|
||||
project.Init()
|
||||
|
||||
err := itu.DB.Create(project).Error
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test project: %v", err)
|
||||
}
|
||||
|
||||
return project
|
||||
}
|
||||
|
||||
// CreateTestTask creates a test task in the database
|
||||
func (itu *IntegrationTestUtils) CreateTestTask(t *testing.T, title, description, projectID string) *model.Task {
|
||||
task := &model.Task{
|
||||
Title: title,
|
||||
Description: description,
|
||||
Status: model.TaskStatusTodo,
|
||||
Priority: model.TaskPriorityMedium,
|
||||
ProjectID: projectID,
|
||||
}
|
||||
task.Init()
|
||||
|
||||
err := itu.DB.Create(task).Error
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test task: %v", err)
|
||||
}
|
||||
|
||||
return task
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
Reference in New Issue
Block a user