package graph import ( "encoding/json" "fmt" "strings" "testing" "time" "github.com/99designs/gqlgen/client" "github.com/99designs/gqlgen/graphql/handler" "github.com/bradleyjkemp/cupaloy/v2" "gogs.dmsc.dev/arp/auth" "gogs.dmsc.dev/arp/graph/model" "gogs.dmsc.dev/arp/graph/testutil" "gorm.io/gorm" ) var snapshotter = cupaloy.New(cupaloy.SnapshotSubdirectory("testdata/snapshots")) type TestClient struct { client *client.Client db *gorm.DB token string } type IDTracker struct { Permissions map[string]string Roles map[string]string Users map[string]string TaskStatuses map[string]string Services map[string]string Tasks map[string]string Notes map[string]string Messages []string } func NewIDTracker() *IDTracker { return &IDTracker{ Permissions: make(map[string]string), Roles: make(map[string]string), Users: make(map[string]string), TaskStatuses: make(map[string]string), Services: make(map[string]string), Tasks: make(map[string]string), Notes: make(map[string]string), Messages: make([]string, 0), } } func setupTestClient(t *testing.T) (*TestClient, *IDTracker) { // Setup and bootstrap the database with initial data from init_tests.sql db, err := testutil.SetupAndBootstrapTestDB() if err != nil { t.Fatalf("Failed to setup test database: %v", err) } resolver := &Resolver{DB: db} schema := NewExecutableSchema(Config{Resolvers: resolver}) srv := handler.NewDefaultServer(schema) // Wrap with auth middleware authSrv := auth.AuthMiddleware(srv) gqlClient := client.New(authSrv) // Login as admin to get a token var loginResponse struct { Login struct { Token string `json:"token"` User struct { ID string `json:"id"` Email string `json:"email"` } `json:"user"` } `json:"login"` } loginQuery := `mutation { login(email: "admin@example.com", password: "secret123") { token user { id email } } }` err = gqlClient.Post(loginQuery, &loginResponse) if err != nil { t.Fatalf("Failed to login as admin: %v", err) } if loginResponse.Login.Token == "" { t.Fatal("Login returned empty token") } // Create authenticated client authClient := client.New(authSrv, client.AddHeader("Authorization", "Bearer "+loginResponse.Login.Token)) // Initialize tracker with bootstrapped data tracker := NewIDTracker() // Track bootstrapped admin user tracker.Users["admin@example.com"] = loginResponse.Login.User.ID // Fetch and track bootstrapped permissions var permsResponse struct { Permissions []struct { ID string `json:"id"` Code string `json:"code"` } `json:"permissions"` } authClient.Post(`query { permissions { id code } }`, &permsResponse) for _, perm := range permsResponse.Permissions { tracker.Permissions[perm.Code] = perm.ID } // Fetch and track bootstrapped roles var rolesResponse struct { Roles []struct { ID string `json:"id"` Name string `json:"name"` } `json:"roles"` } authClient.Post(`query { roles { id name } }`, &rolesResponse) for _, role := range rolesResponse.Roles { tracker.Roles[role.Name] = role.ID } // Fetch and track bootstrapped task statuses var statusesResponse struct { TaskStatuses []struct { ID string `json:"id"` Code string `json:"code"` } `json:"taskStatuses"` } authClient.Post(`query { taskStatuses { id code } }`, &statusesResponse) for _, status := range statusesResponse.TaskStatuses { tracker.TaskStatuses[status.Code] = status.ID } return &TestClient{client: authClient, db: db, token: loginResponse.Login.Token}, tracker } func normalizeJSON(jsonStr string) string { var data interface{} if err := json.Unmarshal([]byte(jsonStr), &data); err != nil { return jsonStr } normalizeData(data) bytes, _ := json.MarshalIndent(data, "", " ") return string(bytes) } func normalizeData(data interface{}) { switch v := data.(type) { case map[string]interface{}: delete(v, "id") delete(v, "ID") delete(v, "createdAt") delete(v, "updatedAt") delete(v, "sentAt") delete(v, "createdByID") delete(v, "userId") delete(v, "serviceId") delete(v, "statusId") delete(v, "assigneeId") delete(v, "conversationId") delete(v, "senderId") for _, val := range v { normalizeData(val) } case []interface{}: for _, item := range v { normalizeData(item) } } } func snapshotResult(t *testing.T, name string, jsonStr string) { normalized := normalizeJSON(jsonStr) snapshotter.SnapshotT(t, name, normalized) } func TestIntegration_Bootstrap(t *testing.T) { tc, tracker := setupTestClient(t) seed := testutil.GetSeedData() // Phase 1: Create Permissions (skip if already bootstrapped) t.Run("CreatePermissions", func(t *testing.T) { for _, perm := range seed.Permissions { // Skip if already exists from bootstrap if _, exists := tracker.Permissions[perm.Code]; exists { continue } var response struct { CreatePermission struct { ID string `json:"id"` Code string `json:"code"` Description string `json:"description"` } `json:"createPermission"` } query := fmt.Sprintf(`mutation { createPermission(input: {code: "%s", description: "%s"}) { id code description } }`, perm.Code, perm.Description) err := tc.client.Post(query, &response) if err != nil { t.Fatalf("Failed to create permission %s: %v", perm.Code, err) } tracker.Permissions[perm.Code] = response.CreatePermission.ID } var permsResponse struct { Permissions []interface{} `json:"permissions"` } tc.client.Post(`query { permissions { id code description } }`, &permsResponse) jsonBytes, _ := json.MarshalIndent(permsResponse, "", " ") snapshotResult(t, "permissions", string(jsonBytes)) }) // Phase 2: Create Roles (skip if already bootstrapped) t.Run("CreateRoles", func(t *testing.T) { for _, role := range seed.Roles { // Skip if already exists from bootstrap if _, exists := tracker.Roles[role.Name]; exists { continue } permIDs := make([]string, len(role.PermissionCodes)) for i, code := range role.PermissionCodes { permIDs[i] = tracker.Permissions[code] } var response struct { CreateRole struct { ID string `json:"id"` Name string `json:"name"` Description string `json:"description"` } `json:"createRole"` } query := fmt.Sprintf(`mutation { createRole(input: {name: "%s", description: "%s", permissions: ["%s"]}) { id name description } }`, role.Name, role.Description, strings.Join(permIDs, `", "`)) err := tc.client.Post(query, &response) if err != nil { t.Fatalf("Failed to create role %s: %v", role.Name, err) } tracker.Roles[role.Name] = response.CreateRole.ID } var rolesResponse struct { Roles []interface{} `json:"roles"` } tc.client.Post(`query { roles { id name description permissions { id code } } }`, &rolesResponse) jsonBytes, _ := json.MarshalIndent(rolesResponse, "", " ") snapshotResult(t, "roles", string(jsonBytes)) }) // Phase 3: Create Users (additional to bootstrapped admin) t.Run("CreateUsers", func(t *testing.T) { for _, user := range seed.Users { roleIDs := make([]string, len(user.RoleNames)) for i, name := range user.RoleNames { roleIDs[i] = tracker.Roles[name] } var response struct { CreateUser struct { ID string `json:"id"` Email string `json:"email"` } `json:"createUser"` } query := fmt.Sprintf(`mutation { createUser(input: {email: "%s", password: "%s", roles: ["%s"]}) { id email } }`, user.Email, user.Password, strings.Join(roleIDs, `", "`)) err := tc.client.Post(query, &response) if err != nil { t.Fatalf("Failed to create user %s: %v", user.Email, err) } tracker.Users[user.Email] = response.CreateUser.ID } var usersResponse struct { Users []interface{} `json:"users"` } tc.client.Post(`query { users { id email roles { id name } } }`, &usersResponse) jsonBytes, _ := json.MarshalIndent(usersResponse, "", " ") snapshotResult(t, "users", string(jsonBytes)) }) // Phase 4: Create Task Statuses (skip if already bootstrapped) t.Run("CreateTaskStatuses", func(t *testing.T) { for _, status := range seed.TaskStatuses { // Skip if already exists from bootstrap if _, exists := tracker.TaskStatuses[status.Code]; exists { continue } var response struct { CreateTaskStatus struct { ID string `json:"id"` Code string `json:"code"` Label string `json:"label"` } `json:"createTaskStatus"` } query := fmt.Sprintf(`mutation { createTaskStatus(input: {code: "%s", label: "%s"}) { id code label } }`, status.Code, status.Label) err := tc.client.Post(query, &response) if err != nil { t.Fatalf("Failed to create task status %s: %v", status.Code, err) } tracker.TaskStatuses[status.Code] = response.CreateTaskStatus.ID } var statusesResponse struct { TaskStatuses []interface{} `json:"taskStatuses"` } tc.client.Post(`query { taskStatuses { id code label } }`, &statusesResponse) jsonBytes, _ := json.MarshalIndent(statusesResponse, "", " ") snapshotResult(t, "taskStatuses", string(jsonBytes)) }) // Phase 5: Create Services t.Run("CreateServices", func(t *testing.T) { for _, service := range seed.Services { participantIDs := make([]string, len(service.ParticipantEmails)) for i, email := range service.ParticipantEmails { participantIDs[i] = tracker.Users[email] } var response struct { CreateService struct { ID string `json:"id"` Name string `json:"name"` } `json:"createService"` } query := fmt.Sprintf(`mutation { createService(input: {name: "%s", description: "%s", createdById: "%s", participants: ["%s"]}) { id name } }`, service.Name, service.Description, tracker.Users[service.CreatorEmail], strings.Join(participantIDs, `", "`)) err := tc.client.Post(query, &response) if err != nil { t.Fatalf("Failed to create service %s: %v", service.Name, err) } tracker.Services[service.Name] = response.CreateService.ID } var servicesResponse struct { Services []interface{} `json:"services"` } tc.client.Post(`query { services { id name description } }`, &servicesResponse) jsonBytes, _ := json.MarshalIndent(servicesResponse, "", " ") snapshotResult(t, "services", string(jsonBytes)) }) // Phase 6: Create Tasks t.Run("CreateTasks", func(t *testing.T) { for _, task := range seed.Tasks { var response struct { CreateTask struct { ID string `json:"id"` Title string `json:"title"` } `json:"createTask"` } var assigneeID string if task.AssigneeEmail != "" { assigneeID = tracker.Users[task.AssigneeEmail] } statusID := tracker.TaskStatuses[task.StatusCode] if assigneeID != "" { query := fmt.Sprintf(`mutation { createTask(input: {title: "%s", content: "%s", createdById: "%s", assigneeId: "%s", statusId: "%s", priority: "%s"}) { id title } }`, task.Title, task.Content, tracker.Users[task.CreatorEmail], assigneeID, statusID, task.Priority) err := tc.client.Post(query, &response) if err != nil { t.Fatalf("Failed to create task %s: %v", task.Title, err) } } else { query := fmt.Sprintf(`mutation { createTask(input: {title: "%s", content: "%s", createdById: "%s", statusId: "%s", priority: "%s"}) { id title } }`, task.Title, task.Content, tracker.Users[task.CreatorEmail], statusID, task.Priority) err := tc.client.Post(query, &response) if err != nil { t.Fatalf("Failed to create task %s: %v", task.Title, err) } } tracker.Tasks[task.Title] = response.CreateTask.ID } var tasksResponse struct { Tasks []interface{} `json:"tasks"` } tc.client.Post(`query { tasks { id title content priority } }`, &tasksResponse) jsonBytes, _ := json.MarshalIndent(tasksResponse, "", " ") snapshotResult(t, "tasks", string(jsonBytes)) }) // Phase 7: Create Notes t.Run("CreateNotes", func(t *testing.T) { for _, note := range seed.Notes { var response struct { CreateNote struct { ID string `json:"id"` Title string `json:"title"` } `json:"createNote"` } query := fmt.Sprintf(`mutation { createNote(input: {title: "%s", content: "%s", userId: "%s", serviceId: "%s"}) { id title } }`, note.Title, note.Content, tracker.Users[note.UserEmail], tracker.Services[note.ServiceName]) err := tc.client.Post(query, &response) if err != nil { t.Fatalf("Failed to create note %s: %v", note.Title, err) } tracker.Notes[note.Title] = response.CreateNote.ID } var notesResponse struct { Notes []interface{} `json:"notes"` } tc.client.Post(`query { notes { id title content } }`, ¬esResponse) jsonBytes, _ := json.MarshalIndent(notesResponse, "", " ") snapshotResult(t, "notes", string(jsonBytes)) }) // Phase 8: Create Messages t.Run("CreateMessages", func(t *testing.T) { for _, msg := range seed.Messages { receiverIDs := make([]string, len(msg.ReceiverEmails)) for i, email := range msg.ReceiverEmails { receiverIDs[i] = tracker.Users[email] } var response struct { CreateMessage struct { ID string `json:"id"` } `json:"createMessage"` } query := fmt.Sprintf(`mutation { createMessage(input: {content: "%s", receivers: ["%s"]}) { id } }`, msg.Content, strings.Join(receiverIDs, `", "`)) err := tc.client.Post(query, &response) if err != nil { t.Fatalf("Failed to create message: %v", err) } tracker.Messages = append(tracker.Messages, response.CreateMessage.ID) } var messagesResponse struct { Messages []interface{} `json:"messages"` } tc.client.Post(`query { messages { id content receivers } }`, &messagesResponse) jsonBytes, _ := json.MarshalIndent(messagesResponse, "", " ") snapshotResult(t, "messages", string(jsonBytes)) }) } // TestIntegration_Update tests update operations func TestIntegration_Update(t *testing.T) { tc, tracker := setupTestClient(t) seed := testutil.GetSeedData() // Bootstrap first bootstrapData(t, tc, tracker, seed) // Update a user t.Run("UpdateUser", func(t *testing.T) { userID := tracker.Users["admin@example.com"] var response struct { UpdateUser struct { ID string `json:"id"` Email string `json:"email"` } `json:"updateUser"` } query := fmt.Sprintf(`mutation { updateUser(id: "%s", input: {email: "admin-updated@example.com", password: "new-password", roles: ["%s"]}) { id email } }`, userID, tracker.Roles["admin"]) err := tc.client.Post(query, &response) if err != nil { t.Fatalf("Failed to update user: %v", err) } var usersResponse struct { Users []interface{} `json:"users"` } tc.client.Post(`query { users { id email roles { id name } } }`, &usersResponse) jsonBytes, _ := json.MarshalIndent(usersResponse, "", " ") snapshotResult(t, "users_after_update", string(jsonBytes)) }) // Update a task t.Run("UpdateTask", func(t *testing.T) { taskID := tracker.Tasks["Setup development environment"] var response struct { UpdateTask struct { ID string `json:"id"` Title string `json:"title"` } `json:"updateTask"` } query := fmt.Sprintf(`mutation { updateTask(id: "%s", input: {title: "Setup development environment - COMPLETE", priority: "low"}) { id title } }`, taskID) err := tc.client.Post(query, &response) if err != nil { t.Fatalf("Failed to update task: %v", err) } var tasksResponse struct { Tasks []interface{} `json:"tasks"` } tc.client.Post(`query { tasks { id title content priority } }`, &tasksResponse) jsonBytes, _ := json.MarshalIndent(tasksResponse, "", " ") snapshotResult(t, "tasks_after_update", string(jsonBytes)) }) } // TestIntegration_Delete tests delete operations func TestIntegration_Delete(t *testing.T) { tc, tracker := setupTestClient(t) seed := testutil.GetSeedData() // Bootstrap first bootstrapData(t, tc, tracker, seed) // Delete a note t.Run("DeleteNote", func(t *testing.T) { noteID := tracker.Notes["Meeting notes - Sprint 1"] var response struct { DeleteNote bool `json:"deleteNote"` } query := fmt.Sprintf(`mutation { deleteNote(id: "%s") }`, noteID) err := tc.client.Post(query, &response) if err != nil { t.Fatalf("Failed to delete note: %v", err) } var notesResponse struct { Notes []interface{} `json:"notes"` } tc.client.Post(`query { notes { id title content } }`, ¬esResponse) jsonBytes, _ := json.MarshalIndent(notesResponse, "", " ") snapshotResult(t, "notes_after_delete", string(jsonBytes)) }) // Delete a task t.Run("DeleteTask", func(t *testing.T) { taskID := tracker.Tasks["Performance optimization"] var response struct { DeleteTask bool `json:"deleteTask"` } query := fmt.Sprintf(`mutation { deleteTask(id: "%s") }`, taskID) err := tc.client.Post(query, &response) if err != nil { t.Fatalf("Failed to delete task: %v", err) } var tasksResponse struct { Tasks []interface{} `json:"tasks"` } tc.client.Post(`query { tasks { id title content priority } }`, &tasksResponse) jsonBytes, _ := json.MarshalIndent(tasksResponse, "", " ") snapshotResult(t, "tasks_after_delete", string(jsonBytes)) }) } // TestIntegration_Subscriptions tests subscription functionality func TestIntegration_Subscriptions(t *testing.T) { db, err := testutil.SetupAndBootstrapTestDB() if err != nil { t.Fatalf("Failed to setup test database: %v", err) } resolver := NewResolver(db) schema := NewExecutableSchema(Config{Resolvers: resolver}) srv := handler.NewDefaultServer(schema) authSrv := auth.AuthMiddleware(srv) // Create admin client adminClient := client.New(authSrv) // Login as admin var loginResponse struct { Login struct { Token string `json:"token"` User struct { ID string `json:"id"` Email string `json:"email"` } `json:"user"` } `json:"login"` } loginQuery := `mutation { login(email: "admin@example.com", password: "secret123") { token user { id email } } }` err = adminClient.Post(loginQuery, &loginResponse) if err != nil { t.Fatalf("Failed to login as admin: %v", err) } adminToken := loginResponse.Login.Token adminID := loginResponse.Login.User.ID adminClient = client.New(authSrv, client.AddHeader("Authorization", "Bearer "+adminToken)) // Create user1 var createUserResponse struct { CreateUser struct { ID string `json:"id"` Email string `json:"email"` } `json:"createUser"` } // Get user role ID var rolesResponse struct { Roles []struct { ID string `json:"id"` Name string `json:"name"` } `json:"roles"` } adminClient.Post(`query { roles { id name } }`, &rolesResponse) var userRoleID string for _, role := range rolesResponse.Roles { if role.Name == "user" { userRoleID = role.ID break } } createUserQuery := fmt.Sprintf(`mutation { createUser(input: {email: "user1@example.com", password: "password123", roles: ["%s"]}) { id email } }`, userRoleID) err = adminClient.Post(createUserQuery, &createUserResponse) if err != nil { t.Fatalf("Failed to create user1: %v", err) } user1ID := createUserResponse.CreateUser.ID // Login as user1 var user1LoginResponse struct { Login struct { Token string `json:"token"` } `json:"login"` } user1LoginQuery := `mutation { login(email: "user1@example.com", password: "password123") { token } }` err = adminClient.Post(user1LoginQuery, &user1LoginResponse) if err != nil { t.Fatalf("Failed to login as user1: %v", err) } user1Token := user1LoginResponse.Login.Token // Create WebSocket client for user1 user1WSClient := client.New(authSrv, client.AddHeader("Authorization", "Bearer "+user1Token)) // Subscribe to taskCreated as user1 taskCreatedSub := `subscription { taskCreated { id title content assigneeId } }` taskCreatedChan := make(chan *model.Task, 1) taskCreatedSubscription := user1WSClient.Websocket(taskCreatedSub) defer taskCreatedSubscription.Close() go func() { for { var taskCreatedResponse struct { TaskCreated *model.Task `json:"taskCreated"` } err := taskCreatedSubscription.Next(&taskCreatedResponse) if err != nil { return } if taskCreatedResponse.TaskCreated != nil { select { case taskCreatedChan <- taskCreatedResponse.TaskCreated: default: } } } }() // Subscribe to messageAdded as user1 messageAddedSub := `subscription { messageAdded { id content senderId receivers } }` messageAddedChan := make(chan *model.Message, 1) messageAddedSubscription := user1WSClient.Websocket(messageAddedSub) defer messageAddedSubscription.Close() go func() { for { var messageAddedResponse struct { MessageAdded *model.Message `json:"messageAdded"` } err := messageAddedSubscription.Next(&messageAddedResponse) if err != nil { return } if messageAddedResponse.MessageAdded != nil { select { case messageAddedChan <- messageAddedResponse.MessageAdded: default: } } } }() // Give subscriptions time to connect time.Sleep(100 * time.Millisecond) // Admin sends a message to user1 var createMessageResponse struct { CreateMessage struct { ID string `json:"id"` } `json:"createMessage"` } createMessageQuery := fmt.Sprintf(`mutation { createMessage(input: {content: "Hello user1!", receivers: ["%s"]}) { id } }`, user1ID) err = adminClient.Post(createMessageQuery, &createMessageResponse) if err != nil { t.Fatalf("Failed to create message: %v", err) } // Wait for messageAdded event select { case msg := <-messageAddedChan: if msg == nil { t.Error("Expected messageAdded event, got nil") } else if msg.Content != "Hello user1!" { t.Errorf("Expected message content 'Hello user1!', got '%s'", msg.Content) } else { t.Log("user1 received messageAdded event successfully") } case <-time.After(2 * time.Second): t.Error("Timeout waiting for messageAdded event") } // Get a task status ID var statusesResponse struct { TaskStatuses []struct { ID string `json:"id"` Code string `json:"code"` } `json:"taskStatuses"` } adminClient.Post(`query { taskStatuses { id code } }`, &statusesResponse) var statusID string if len(statusesResponse.TaskStatuses) > 0 { statusID = statusesResponse.TaskStatuses[0].ID } // Admin creates a task assigned to user1 var createTaskResponse struct { CreateTask struct { ID string `json:"id"` Title string `json:"title"` } `json:"createTask"` } createTaskQuery := fmt.Sprintf(`mutation { createTask(input: {title: "Task for user1", content: "This is assigned to user1", createdById: "%s", assigneeId: "%s", statusId: "%s", priority: "medium"}) { id title } }`, adminID, user1ID, statusID) err = adminClient.Post(createTaskQuery, &createTaskResponse) if err != nil { t.Fatalf("Failed to create task: %v", err) } // Wait for taskCreated event select { case task := <-taskCreatedChan: if task == nil { t.Error("Expected taskCreated event, got nil") } else if task.Title != "Task for user1" { t.Errorf("Expected task title 'Task for user1', got '%s'", task.Title) } else { t.Log("user1 received taskCreated event successfully") } case <-time.After(2 * time.Second): t.Error("Timeout waiting for taskCreated event") } } // bootstrapData creates all entities for testing (skips existing items) func bootstrapData(t *testing.T, tc *TestClient, tracker *IDTracker, seed testutil.SeedData) { // Create Permissions (skip if already exists) for _, perm := range seed.Permissions { if _, exists := tracker.Permissions[perm.Code]; exists { continue } var response struct { CreatePermission struct { ID string `json:"id"` } `json:"createPermission"` } query := fmt.Sprintf(`mutation { createPermission(input: {code: "%s", description: "%s"}) { id } }`, perm.Code, perm.Description) err := tc.client.Post(query, &response) if err != nil { t.Fatalf("Failed to create permission %s: %v", perm.Code, err) } tracker.Permissions[perm.Code] = response.CreatePermission.ID } // Create Roles (skip if already exists) for _, role := range seed.Roles { if _, exists := tracker.Roles[role.Name]; exists { continue } permIDs := make([]string, len(role.PermissionCodes)) for i, code := range role.PermissionCodes { permIDs[i] = tracker.Permissions[code] } var response struct { CreateRole struct { ID string `json:"id"` } `json:"createRole"` } query := fmt.Sprintf(`mutation { createRole(input: {name: "%s", description: "%s", permissions: ["%s"]}) { id } }`, role.Name, role.Description, strings.Join(permIDs, `", "`)) err := tc.client.Post(query, &response) if err != nil { t.Fatalf("Failed to create role %s: %v", role.Name, err) } tracker.Roles[role.Name] = response.CreateRole.ID } // Create Users (skip if already exists) for _, user := range seed.Users { if _, exists := tracker.Users[user.Email]; exists { continue } roleIDs := make([]string, len(user.RoleNames)) for i, name := range user.RoleNames { roleIDs[i] = tracker.Roles[name] } var response struct { CreateUser struct { ID string `json:"id"` } `json:"createUser"` } query := fmt.Sprintf(`mutation { createUser(input: {email: "%s", password: "%s", roles: ["%s"]}) { id } }`, user.Email, user.Password, strings.Join(roleIDs, `", "`)) err := tc.client.Post(query, &response) if err != nil { t.Fatalf("Failed to create user %s: %v", user.Email, err) } tracker.Users[user.Email] = response.CreateUser.ID } // Create Task Statuses (skip if already exists) for _, status := range seed.TaskStatuses { if _, exists := tracker.TaskStatuses[status.Code]; exists { continue } var response struct { CreateTaskStatus struct { ID string `json:"id"` } `json:"createTaskStatus"` } query := fmt.Sprintf(`mutation { createTaskStatus(input: {code: "%s", label: "%s"}) { id } }`, status.Code, status.Label) err := tc.client.Post(query, &response) if err != nil { t.Fatalf("Failed to create task status %s: %v", status.Code, err) } tracker.TaskStatuses[status.Code] = response.CreateTaskStatus.ID } // Create Services for _, service := range seed.Services { participantIDs := make([]string, len(service.ParticipantEmails)) for i, email := range service.ParticipantEmails { participantIDs[i] = tracker.Users[email] } var response struct { CreateService struct { ID string `json:"id"` } `json:"createService"` } query := fmt.Sprintf(`mutation { createService(input: {name: "%s", description: "%s", createdById: "%s", participants: ["%s"]}) { id } }`, service.Name, service.Description, tracker.Users[service.CreatorEmail], strings.Join(participantIDs, `", "`)) err := tc.client.Post(query, &response) if err != nil { t.Fatalf("Failed to create service %s: %v", service.Name, err) } tracker.Services[service.Name] = response.CreateService.ID } // Create Tasks for _, task := range seed.Tasks { var response struct { CreateTask struct { ID string `json:"id"` } `json:"createTask"` } statusID := tracker.TaskStatuses[task.StatusCode] if task.AssigneeEmail != "" { query := fmt.Sprintf(`mutation { createTask(input: {title: "%s", content: "%s", createdById: "%s", assigneeId: "%s", statusId: "%s", priority: "%s"}) { id } }`, task.Title, task.Content, tracker.Users[task.CreatorEmail], tracker.Users[task.AssigneeEmail], statusID, task.Priority) err := tc.client.Post(query, &response) if err != nil { t.Fatalf("Failed to create task %s: %v", task.Title, err) } } else { query := fmt.Sprintf(`mutation { createTask(input: {title: "%s", content: "%s", createdById: "%s", statusId: "%s", priority: "%s"}) { id } }`, task.Title, task.Content, tracker.Users[task.CreatorEmail], statusID, task.Priority) err := tc.client.Post(query, &response) if err != nil { t.Fatalf("Failed to create task %s: %v", task.Title, err) } } tracker.Tasks[task.Title] = response.CreateTask.ID } // Create Notes for _, note := range seed.Notes { var response struct { CreateNote struct { ID string `json:"id"` } `json:"createNote"` } query := fmt.Sprintf(`mutation { createNote(input: {title: "%s", content: "%s", userId: "%s", serviceId: "%s"}) { id } }`, note.Title, note.Content, tracker.Users[note.UserEmail], tracker.Services[note.ServiceName]) err := tc.client.Post(query, &response) if err != nil { t.Fatalf("Failed to create note %s: %v", note.Title, err) } tracker.Notes[note.Title] = response.CreateNote.ID } // Create Messages for _, msg := range seed.Messages { receiverIDs := make([]string, len(msg.ReceiverEmails)) for i, email := range msg.ReceiverEmails { receiverIDs[i] = tracker.Users[email] } var response struct { CreateMessage struct { ID string `json:"id"` } `json:"createMessage"` } query := fmt.Sprintf(`mutation { createMessage(input: {content: "%s", receivers: ["%s"]}) { id } }`, msg.Content, strings.Join(receiverIDs, `", "`)) err := tc.client.Post(query, &response) if err != nil { t.Fatalf("Failed to create message: %v", err) } tracker.Messages = append(tracker.Messages, response.CreateMessage.ID) } }