tercul-backend/graph/integration_test.go
Damir Mukimov 4957117cb6 Initial commit: Tercul Go project with comprehensive architecture
- Core Go application with GraphQL API using gqlgen
- Comprehensive data models for literary works, authors, translations
- Repository pattern with caching layer
- Authentication and authorization system
- Linguistics analysis capabilities with multiple adapters
- Vector search integration with Weaviate
- Docker containerization support
- Python data migration and analysis scripts
- Clean architecture with proper separation of concerns
- Production-ready configuration and middleware
- Proper .gitignore excluding vendor/, database files, and build artifacts
2025-08-13 07:42:32 +02:00

295 lines
8.1 KiB
Go

package graph_test
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"context"
"tercul/graph"
"tercul/internal/testutil"
"tercul/models"
"tercul/services"
"github.com/99designs/gqlgen/graphql/handler"
"github.com/stretchr/testify/suite"
)
// MockLocalizationService provides mock localization for tests
type MockLocalizationService struct{}
func (m *MockLocalizationService) GetWorkContent(ctx context.Context, workID uint, preferredLanguage string) (string, error) {
return "Test content", nil
}
func (m *MockLocalizationService) GetAuthorBiography(ctx context.Context, authorID uint, preferredLanguage string) (string, error) {
return "Test biography", nil
}
// GraphQLRequest represents a GraphQL request
type GraphQLRequest struct {
Query string `json:"query"`
OperationName string `json:"operationName,omitempty"`
Variables map[string]interface{} `json:"variables,omitempty"`
}
// GraphQLResponse represents a GraphQL response
type GraphQLResponse struct {
Data map[string]interface{} `json:"data,omitempty"`
Errors []map[string]interface{} `json:"errors,omitempty"`
}
// GraphQLIntegrationSuite is a test suite for GraphQL integration tests
type GraphQLIntegrationSuite struct {
testutil.BaseSuite
server *httptest.Server
client *http.Client
workRepo *testutil.UnifiedMockWorkRepository // direct access to mock repo
}
// SetupSuite sets up the test suite
func (s *GraphQLIntegrationSuite) SetupSuite() {
// Use in-memory/mock repositories and services
workRepo := &testutil.UnifiedMockWorkRepository{}
workService := services.NewWorkService(workRepo, nil)
mockLocalization := &MockLocalizationService{}
resolver := &graph.Resolver{
WorkRepo: workRepo,
WorkService: workService,
Localization: mockLocalization,
}
srv := handler.NewDefaultServer(graph.NewExecutableSchema(graph.Config{Resolvers: resolver}))
s.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
srv.ServeHTTP(w, r)
}))
s.client = s.server.Client()
s.workRepo = workRepo
}
// TearDownSuite tears down the test suite
func (s *GraphQLIntegrationSuite) TearDownSuite() {
s.server.Close()
s.BaseSuite.TearDownSuite()
}
// SetupTest sets up each test
func (s *GraphQLIntegrationSuite) SetupTest() {
s.workRepo.Reset()
}
// createTestWork creates a test work
func (s *GraphQLIntegrationSuite) createTestWork(title, language string) *models.Work {
work := &models.Work{
Title: title,
}
work.Language = language // set via embedded TranslatableModel
s.workRepo.AddWork(work)
return work
}
// executeGraphQL executes a GraphQL query
func (s *GraphQLIntegrationSuite) executeGraphQL(query string, variables map[string]interface{}) (*GraphQLResponse, error) {
// Create the request
request := GraphQLRequest{
Query: query,
Variables: variables,
}
// Marshal the request to JSON
requestBody, err := json.Marshal(request)
if err != nil {
return nil, err
}
// Create an HTTP request
req, err := http.NewRequest("POST", s.server.URL, bytes.NewBuffer(requestBody))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
// Execute the request
resp, err := s.client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
// Parse the response
var response GraphQLResponse
err = json.NewDecoder(resp.Body).Decode(&response)
if err != nil {
return nil, err
}
return &response, nil
}
// TestQueryWork tests the work query
func (s *GraphQLIntegrationSuite) TestQueryWork() {
// Create a test work
work := s.createTestWork("Test Work", "en")
// Define the query
query := `
query GetWork($id: ID!) {
work(id: $id) {
id
name
language
content
}
}
`
// Define the variables
variables := map[string]interface{}{
"id": work.ID,
}
// Execute the query
response, err := s.executeGraphQL(query, variables)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Nil(response.Errors, "GraphQL query should not return errors")
s.Require().NotNil(response.Data, "GraphQL query should return data")
// Verify the response
workData, ok := response.Data["work"].(map[string]interface{})
s.Require().True(ok, "GraphQL response should contain work data")
s.Equal("Test Work", workData["name"], "Work name should match")
s.Equal("Test content", workData["content"], "Work content should match via localization")
s.Equal("en", workData["language"], "Work language should match")
}
// TestQueryWorks tests the works query
func (s *GraphQLIntegrationSuite) TestQueryWorks() {
// Create test works
work1 := s.createTestWork("Test Work 1", "en")
work2 := s.createTestWork("Test Work 2", "en")
work3 := s.createTestWork("Test Work 3", "fr")
// Define the query
query := `
query GetWorks {
works {
id
name
language
}
}
`
// Execute the query
response, err := s.executeGraphQL(query, nil)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Nil(response.Errors, "GraphQL query should not return errors")
s.Require().NotNil(response.Data, "GraphQL query should return data")
// Verify the response
worksData, ok := response.Data["works"].([]interface{})
s.Require().True(ok, "GraphQL response should contain works data")
s.Equal(3, len(worksData), "GraphQL response should contain 3 works")
// Verify each work
foundWork1 := false
foundWork2 := false
foundWork3 := false
for _, workData := range worksData {
work, ok := workData.(map[string]interface{})
s.Require().True(ok, "Work data should be a map")
id := work["id"].(string) // fix: treat id as string
if id == fmt.Sprintf("%d", work1.ID) {
foundWork1 = true
s.Equal("Test Work 1", work["name"], "Work 1 name should match")
s.Equal("en", work["language"], "Work 1 language should match")
} else if id == fmt.Sprintf("%d", work2.ID) {
foundWork2 = true
s.Equal("Test Work 2", work["name"], "Work 2 name should match")
s.Equal("en", work["language"], "Work 2 language should match")
} else if id == fmt.Sprintf("%d", work3.ID) {
foundWork3 = true
s.Equal("Test Work 3", work["name"], "Work 3 name should match")
s.Equal("fr", work["language"], "Work 3 language should match")
}
}
s.True(foundWork1, "GraphQL response should contain work 1")
s.True(foundWork2, "GraphQL response should contain work 2")
s.True(foundWork3, "GraphQL response should contain work 3")
}
func stringToUint(s string) uint {
var id uint
fmt.Sscanf(s, "%d", &id)
return id
}
// TestCreateWork tests the createWork mutation
func (s *GraphQLIntegrationSuite) TestCreateWork() {
// Define the mutation
mutation := `
mutation CreateWork($input: WorkInput!) {
createWork(input: $input) {
id
name
language
content
}
}
`
// Define the variables
variables := map[string]interface{}{
"input": map[string]interface{}{
"name": "New Test Work",
"language": "en",
"content": "New test content",
},
}
// Execute the mutation
response, err := s.executeGraphQL(mutation, variables)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Nil(response.Errors, "GraphQL mutation should not return errors")
s.Require().NotNil(response.Data, "GraphQL mutation should return data")
// Verify the response
workData, ok := response.Data["createWork"].(map[string]interface{})
s.Require().True(ok, "GraphQL response should contain work data")
s.NotNil(workData["id"], "Work ID should not be nil")
s.Equal("New Test Work", workData["name"], "Work name should match")
s.Equal("en", workData["language"], "Work language should match")
s.Equal("New test content", workData["content"], "Work content should match")
// Verify that the work was created in the mock repository
var found *models.Work
for _, w := range s.workRepo.Works {
if w.Title == "New Test Work" {
found = w
break
}
}
s.Require().NotNil(found)
s.Equal("New Test Work", found.Title)
s.Equal("en", found.Language)
// Content is not stored on Work model; translations hold content
}
// TestGraphQLIntegrationSuite runs the test suite
func TestGraphQLIntegrationSuite(t *testing.T) {
testutil.SkipIfShort(t)
suite.Run(t, new(GraphQLIntegrationSuite))
}