1
0

integration_test.go 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921
  1. package graph
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "strings"
  6. "testing"
  7. "time"
  8. "github.com/99designs/gqlgen/client"
  9. "github.com/99designs/gqlgen/graphql/handler"
  10. "github.com/bradleyjkemp/cupaloy/v2"
  11. "gogs.dmsc.dev/arp/auth"
  12. "gogs.dmsc.dev/arp/graph/model"
  13. "gogs.dmsc.dev/arp/graph/testutil"
  14. "gorm.io/gorm"
  15. )
  16. var snapshotter = cupaloy.New(cupaloy.SnapshotSubdirectory("testdata/snapshots"))
  17. type TestClient struct {
  18. client *client.Client
  19. db *gorm.DB
  20. token string
  21. }
  22. type IDTracker struct {
  23. Permissions map[string]string
  24. Roles map[string]string
  25. Users map[string]string
  26. TaskStatuses map[string]string
  27. Services map[string]string
  28. Tasks map[string]string
  29. Notes map[string]string
  30. Channels []string
  31. Messages []string
  32. }
  33. func NewIDTracker() *IDTracker {
  34. return &IDTracker{
  35. Permissions: make(map[string]string),
  36. Roles: make(map[string]string),
  37. Users: make(map[string]string),
  38. TaskStatuses: make(map[string]string),
  39. Services: make(map[string]string),
  40. Tasks: make(map[string]string),
  41. Notes: make(map[string]string),
  42. Channels: make([]string, 0),
  43. Messages: make([]string, 0),
  44. }
  45. }
  46. func setupTestClient(t *testing.T) (*TestClient, *IDTracker) {
  47. // Setup and bootstrap the database with initial data from init_tests.sql
  48. db, err := testutil.SetupAndBootstrapTestDB()
  49. if err != nil {
  50. t.Fatalf("Failed to setup test database: %v", err)
  51. }
  52. resolver := &Resolver{DB: db}
  53. schema := NewExecutableSchema(Config{Resolvers: resolver})
  54. srv := handler.NewDefaultServer(schema)
  55. // Wrap with auth middleware
  56. authSrv := auth.AuthMiddleware(srv)
  57. gqlClient := client.New(authSrv)
  58. // Login as admin to get a token
  59. var loginResponse struct {
  60. Login struct {
  61. Token string `json:"token"`
  62. User struct {
  63. ID string `json:"id"`
  64. Email string `json:"email"`
  65. } `json:"user"`
  66. } `json:"login"`
  67. }
  68. loginQuery := `mutation { login(email: "admin@example.com", password: "secret123") { token user { id email } } }`
  69. err = gqlClient.Post(loginQuery, &loginResponse)
  70. if err != nil {
  71. t.Fatalf("Failed to login as admin: %v", err)
  72. }
  73. if loginResponse.Login.Token == "" {
  74. t.Fatal("Login returned empty token")
  75. }
  76. // Create authenticated client
  77. authClient := client.New(authSrv, client.AddHeader("Authorization", "Bearer "+loginResponse.Login.Token))
  78. // Initialize tracker with bootstrapped data
  79. tracker := NewIDTracker()
  80. // Track bootstrapped admin user
  81. tracker.Users["admin@example.com"] = loginResponse.Login.User.ID
  82. // Fetch and track bootstrapped permissions
  83. var permsResponse struct {
  84. Permissions []struct {
  85. ID string `json:"id"`
  86. Code string `json:"code"`
  87. } `json:"permissions"`
  88. }
  89. authClient.Post(`query { permissions { id code } }`, &permsResponse)
  90. for _, perm := range permsResponse.Permissions {
  91. tracker.Permissions[perm.Code] = perm.ID
  92. }
  93. // Fetch and track bootstrapped roles
  94. var rolesResponse struct {
  95. Roles []struct {
  96. ID string `json:"id"`
  97. Name string `json:"name"`
  98. } `json:"roles"`
  99. }
  100. authClient.Post(`query { roles { id name } }`, &rolesResponse)
  101. for _, role := range rolesResponse.Roles {
  102. tracker.Roles[role.Name] = role.ID
  103. }
  104. // Fetch and track bootstrapped task statuses
  105. var statusesResponse struct {
  106. TaskStatuses []struct {
  107. ID string `json:"id"`
  108. Code string `json:"code"`
  109. } `json:"taskStatuses"`
  110. }
  111. authClient.Post(`query { taskStatuses { id code } }`, &statusesResponse)
  112. for _, status := range statusesResponse.TaskStatuses {
  113. tracker.TaskStatuses[status.Code] = status.ID
  114. }
  115. return &TestClient{client: authClient, db: db, token: loginResponse.Login.Token}, tracker
  116. }
  117. func normalizeJSON(jsonStr string) string {
  118. var data interface{}
  119. if err := json.Unmarshal([]byte(jsonStr), &data); err != nil {
  120. return jsonStr
  121. }
  122. normalizeData(data)
  123. bytes, _ := json.MarshalIndent(data, "", " ")
  124. return string(bytes)
  125. }
  126. func normalizeData(data interface{}) {
  127. switch v := data.(type) {
  128. case map[string]interface{}:
  129. delete(v, "id")
  130. delete(v, "ID")
  131. delete(v, "createdAt")
  132. delete(v, "updatedAt")
  133. delete(v, "sentAt")
  134. delete(v, "createdByID")
  135. delete(v, "userId")
  136. delete(v, "serviceId")
  137. delete(v, "statusId")
  138. delete(v, "assigneeId")
  139. delete(v, "conversationId")
  140. delete(v, "senderId")
  141. for _, val := range v {
  142. normalizeData(val)
  143. }
  144. case []interface{}:
  145. for _, item := range v {
  146. normalizeData(item)
  147. }
  148. }
  149. }
  150. func snapshotResult(t *testing.T, name string, jsonStr string) {
  151. normalized := normalizeJSON(jsonStr)
  152. snapshotter.SnapshotT(t, name, normalized)
  153. }
  154. func TestIntegration_Bootstrap(t *testing.T) {
  155. tc, tracker := setupTestClient(t)
  156. seed := testutil.GetSeedData()
  157. // Phase 1: Create Permissions (skip if already bootstrapped)
  158. t.Run("CreatePermissions", func(t *testing.T) {
  159. for _, perm := range seed.Permissions {
  160. // Skip if already exists from bootstrap
  161. if _, exists := tracker.Permissions[perm.Code]; exists {
  162. continue
  163. }
  164. var response struct {
  165. CreatePermission struct {
  166. ID string `json:"id"`
  167. Code string `json:"code"`
  168. Description string `json:"description"`
  169. } `json:"createPermission"`
  170. }
  171. query := fmt.Sprintf(`mutation { createPermission(input: {code: "%s", description: "%s"}) { id code description } }`, perm.Code, perm.Description)
  172. err := tc.client.Post(query, &response)
  173. if err != nil {
  174. t.Fatalf("Failed to create permission %s: %v", perm.Code, err)
  175. }
  176. tracker.Permissions[perm.Code] = response.CreatePermission.ID
  177. }
  178. var permsResponse struct {
  179. Permissions []interface{} `json:"permissions"`
  180. }
  181. tc.client.Post(`query { permissions { id code description } }`, &permsResponse)
  182. jsonBytes, _ := json.MarshalIndent(permsResponse, "", " ")
  183. snapshotResult(t, "permissions", string(jsonBytes))
  184. })
  185. // Phase 2: Create Roles (skip if already bootstrapped)
  186. t.Run("CreateRoles", func(t *testing.T) {
  187. for _, role := range seed.Roles {
  188. // Skip if already exists from bootstrap
  189. if _, exists := tracker.Roles[role.Name]; exists {
  190. continue
  191. }
  192. permIDs := make([]string, len(role.PermissionCodes))
  193. for i, code := range role.PermissionCodes {
  194. permIDs[i] = tracker.Permissions[code]
  195. }
  196. var response struct {
  197. CreateRole struct {
  198. ID string `json:"id"`
  199. Name string `json:"name"`
  200. Description string `json:"description"`
  201. } `json:"createRole"`
  202. }
  203. query := fmt.Sprintf(`mutation { createRole(input: {name: "%s", description: "%s", permissions: ["%s"]}) { id name description } }`, role.Name, role.Description, strings.Join(permIDs, `", "`))
  204. err := tc.client.Post(query, &response)
  205. if err != nil {
  206. t.Fatalf("Failed to create role %s: %v", role.Name, err)
  207. }
  208. tracker.Roles[role.Name] = response.CreateRole.ID
  209. }
  210. var rolesResponse struct {
  211. Roles []interface{} `json:"roles"`
  212. }
  213. tc.client.Post(`query { roles { id name description permissions { id code } } }`, &rolesResponse)
  214. jsonBytes, _ := json.MarshalIndent(rolesResponse, "", " ")
  215. snapshotResult(t, "roles", string(jsonBytes))
  216. })
  217. // Phase 3: Create Users (additional to bootstrapped admin)
  218. t.Run("CreateUsers", func(t *testing.T) {
  219. for _, user := range seed.Users {
  220. roleIDs := make([]string, len(user.RoleNames))
  221. for i, name := range user.RoleNames {
  222. roleIDs[i] = tracker.Roles[name]
  223. }
  224. var response struct {
  225. CreateUser struct {
  226. ID string `json:"id"`
  227. Email string `json:"email"`
  228. } `json:"createUser"`
  229. }
  230. query := fmt.Sprintf(`mutation { createUser(input: {email: "%s", password: "%s", roles: ["%s"]}) { id email } }`, user.Email, user.Password, strings.Join(roleIDs, `", "`))
  231. err := tc.client.Post(query, &response)
  232. if err != nil {
  233. t.Fatalf("Failed to create user %s: %v", user.Email, err)
  234. }
  235. tracker.Users[user.Email] = response.CreateUser.ID
  236. }
  237. var usersResponse struct {
  238. Users []interface{} `json:"users"`
  239. }
  240. tc.client.Post(`query { users { id email roles { id name } } }`, &usersResponse)
  241. jsonBytes, _ := json.MarshalIndent(usersResponse, "", " ")
  242. snapshotResult(t, "users", string(jsonBytes))
  243. })
  244. // Phase 4: Create Task Statuses (skip if already bootstrapped)
  245. t.Run("CreateTaskStatuses", func(t *testing.T) {
  246. for _, status := range seed.TaskStatuses {
  247. // Skip if already exists from bootstrap
  248. if _, exists := tracker.TaskStatuses[status.Code]; exists {
  249. continue
  250. }
  251. var response struct {
  252. CreateTaskStatus struct {
  253. ID string `json:"id"`
  254. Code string `json:"code"`
  255. Label string `json:"label"`
  256. } `json:"createTaskStatus"`
  257. }
  258. query := fmt.Sprintf(`mutation { createTaskStatus(input: {code: "%s", label: "%s"}) { id code label } }`, status.Code, status.Label)
  259. err := tc.client.Post(query, &response)
  260. if err != nil {
  261. t.Fatalf("Failed to create task status %s: %v", status.Code, err)
  262. }
  263. tracker.TaskStatuses[status.Code] = response.CreateTaskStatus.ID
  264. }
  265. var statusesResponse struct {
  266. TaskStatuses []interface{} `json:"taskStatuses"`
  267. }
  268. tc.client.Post(`query { taskStatuses { id code label } }`, &statusesResponse)
  269. jsonBytes, _ := json.MarshalIndent(statusesResponse, "", " ")
  270. snapshotResult(t, "taskStatuses", string(jsonBytes))
  271. })
  272. // Phase 5: Create Services
  273. t.Run("CreateServices", func(t *testing.T) {
  274. for _, service := range seed.Services {
  275. participantIDs := make([]string, len(service.ParticipantEmails))
  276. for i, email := range service.ParticipantEmails {
  277. participantIDs[i] = tracker.Users[email]
  278. }
  279. var response struct {
  280. CreateService struct {
  281. ID string `json:"id"`
  282. Name string `json:"name"`
  283. } `json:"createService"`
  284. }
  285. 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, `", "`))
  286. err := tc.client.Post(query, &response)
  287. if err != nil {
  288. t.Fatalf("Failed to create service %s: %v", service.Name, err)
  289. }
  290. tracker.Services[service.Name] = response.CreateService.ID
  291. }
  292. var servicesResponse struct {
  293. Services []interface{} `json:"services"`
  294. }
  295. tc.client.Post(`query { services { id name description } }`, &servicesResponse)
  296. jsonBytes, _ := json.MarshalIndent(servicesResponse, "", " ")
  297. snapshotResult(t, "services", string(jsonBytes))
  298. })
  299. // Phase 6: Create Tasks
  300. t.Run("CreateTasks", func(t *testing.T) {
  301. for _, task := range seed.Tasks {
  302. var response struct {
  303. CreateTask struct {
  304. ID string `json:"id"`
  305. Title string `json:"title"`
  306. } `json:"createTask"`
  307. }
  308. var assigneeID string
  309. if task.AssigneeEmail != "" {
  310. assigneeID = tracker.Users[task.AssigneeEmail]
  311. }
  312. statusID := tracker.TaskStatuses[task.StatusCode]
  313. if assigneeID != "" {
  314. 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)
  315. err := tc.client.Post(query, &response)
  316. if err != nil {
  317. t.Fatalf("Failed to create task %s: %v", task.Title, err)
  318. }
  319. } else {
  320. 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)
  321. err := tc.client.Post(query, &response)
  322. if err != nil {
  323. t.Fatalf("Failed to create task %s: %v", task.Title, err)
  324. }
  325. }
  326. tracker.Tasks[task.Title] = response.CreateTask.ID
  327. }
  328. var tasksResponse struct {
  329. Tasks []interface{} `json:"tasks"`
  330. }
  331. tc.client.Post(`query { tasks { id title content priority } }`, &tasksResponse)
  332. jsonBytes, _ := json.MarshalIndent(tasksResponse, "", " ")
  333. snapshotResult(t, "tasks", string(jsonBytes))
  334. })
  335. // Phase 7: Create Notes
  336. t.Run("CreateNotes", func(t *testing.T) {
  337. for _, note := range seed.Notes {
  338. var response struct {
  339. CreateNote struct {
  340. ID string `json:"id"`
  341. Title string `json:"title"`
  342. } `json:"createNote"`
  343. }
  344. 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])
  345. err := tc.client.Post(query, &response)
  346. if err != nil {
  347. t.Fatalf("Failed to create note %s: %v", note.Title, err)
  348. }
  349. tracker.Notes[note.Title] = response.CreateNote.ID
  350. }
  351. var notesResponse struct {
  352. Notes []interface{} `json:"notes"`
  353. }
  354. tc.client.Post(`query { notes { id title content } }`, &notesResponse)
  355. jsonBytes, _ := json.MarshalIndent(notesResponse, "", " ")
  356. snapshotResult(t, "notes", string(jsonBytes))
  357. })
  358. // Phase 8: Create Channels
  359. t.Run("CreateChannels", func(t *testing.T) {
  360. for _, channel := range seed.Channels {
  361. participantIDs := make([]string, len(channel.ParticipantEmails))
  362. for i, email := range channel.ParticipantEmails {
  363. participantIDs[i] = tracker.Users[email]
  364. }
  365. var response struct {
  366. CreateChannel struct {
  367. ID string `json:"id"`
  368. } `json:"createChannel"`
  369. }
  370. query := fmt.Sprintf(`mutation { createChannel(input: {participants: ["%s"]}) { id } }`, strings.Join(participantIDs, `", "`))
  371. err := tc.client.Post(query, &response)
  372. if err != nil {
  373. t.Fatalf("Failed to create channel: %v", err)
  374. }
  375. tracker.Channels = append(tracker.Channels, response.CreateChannel.ID)
  376. }
  377. var channelsResponse struct {
  378. Channels []interface{} `json:"channels"`
  379. }
  380. tc.client.Post(`query { channels { id } }`, &channelsResponse)
  381. jsonBytes, _ := json.MarshalIndent(channelsResponse, "", " ")
  382. snapshotResult(t, "channels", string(jsonBytes))
  383. })
  384. // Phase 9: Create Messages
  385. t.Run("CreateMessages", func(t *testing.T) {
  386. for _, msg := range seed.Messages {
  387. var response struct {
  388. CreateMessage struct {
  389. ID string `json:"id"`
  390. } `json:"createMessage"`
  391. }
  392. query := fmt.Sprintf(`mutation { createMessage(input: {conversationId: "%s", senderId: "%s", content: "%s"}) { id } }`, tracker.Channels[msg.ChannelIndex], tracker.Users[msg.SenderEmail], msg.Content)
  393. err := tc.client.Post(query, &response)
  394. if err != nil {
  395. t.Fatalf("Failed to create message: %v", err)
  396. }
  397. tracker.Messages = append(tracker.Messages, response.CreateMessage.ID)
  398. }
  399. var messagesResponse struct {
  400. Messages []interface{} `json:"messages"`
  401. }
  402. tc.client.Post(`query { messages { id content } }`, &messagesResponse)
  403. jsonBytes, _ := json.MarshalIndent(messagesResponse, "", " ")
  404. snapshotResult(t, "messages", string(jsonBytes))
  405. })
  406. }
  407. // TestIntegration_Update tests update operations
  408. func TestIntegration_Update(t *testing.T) {
  409. tc, tracker := setupTestClient(t)
  410. seed := testutil.GetSeedData()
  411. // Bootstrap first
  412. bootstrapData(t, tc, tracker, seed)
  413. // Update a user
  414. t.Run("UpdateUser", func(t *testing.T) {
  415. userID := tracker.Users["admin@example.com"]
  416. var response struct {
  417. UpdateUser struct {
  418. ID string `json:"id"`
  419. Email string `json:"email"`
  420. } `json:"updateUser"`
  421. }
  422. query := fmt.Sprintf(`mutation { updateUser(id: "%s", input: {email: "admin-updated@example.com", password: "new-password", roles: ["%s"]}) { id email } }`, userID, tracker.Roles["admin"])
  423. err := tc.client.Post(query, &response)
  424. if err != nil {
  425. t.Fatalf("Failed to update user: %v", err)
  426. }
  427. var usersResponse struct {
  428. Users []interface{} `json:"users"`
  429. }
  430. tc.client.Post(`query { users { id email roles { id name } } }`, &usersResponse)
  431. jsonBytes, _ := json.MarshalIndent(usersResponse, "", " ")
  432. snapshotResult(t, "users_after_update", string(jsonBytes))
  433. })
  434. // Update a task
  435. t.Run("UpdateTask", func(t *testing.T) {
  436. taskID := tracker.Tasks["Setup development environment"]
  437. var response struct {
  438. UpdateTask struct {
  439. ID string `json:"id"`
  440. Title string `json:"title"`
  441. } `json:"updateTask"`
  442. }
  443. query := fmt.Sprintf(`mutation { updateTask(id: "%s", input: {title: "Setup development environment - COMPLETE", priority: "low"}) { id title } }`, taskID)
  444. err := tc.client.Post(query, &response)
  445. if err != nil {
  446. t.Fatalf("Failed to update task: %v", err)
  447. }
  448. var tasksResponse struct {
  449. Tasks []interface{} `json:"tasks"`
  450. }
  451. tc.client.Post(`query { tasks { id title content priority } }`, &tasksResponse)
  452. jsonBytes, _ := json.MarshalIndent(tasksResponse, "", " ")
  453. snapshotResult(t, "tasks_after_update", string(jsonBytes))
  454. })
  455. }
  456. // TestIntegration_Delete tests delete operations
  457. func TestIntegration_Delete(t *testing.T) {
  458. tc, tracker := setupTestClient(t)
  459. seed := testutil.GetSeedData()
  460. // Bootstrap first
  461. bootstrapData(t, tc, tracker, seed)
  462. // Delete a note
  463. t.Run("DeleteNote", func(t *testing.T) {
  464. noteID := tracker.Notes["Meeting notes - Sprint 1"]
  465. var response struct {
  466. DeleteNote bool `json:"deleteNote"`
  467. }
  468. query := fmt.Sprintf(`mutation { deleteNote(id: "%s") }`, noteID)
  469. err := tc.client.Post(query, &response)
  470. if err != nil {
  471. t.Fatalf("Failed to delete note: %v", err)
  472. }
  473. var notesResponse struct {
  474. Notes []interface{} `json:"notes"`
  475. }
  476. tc.client.Post(`query { notes { id title content } }`, &notesResponse)
  477. jsonBytes, _ := json.MarshalIndent(notesResponse, "", " ")
  478. snapshotResult(t, "notes_after_delete", string(jsonBytes))
  479. })
  480. // Delete a task
  481. t.Run("DeleteTask", func(t *testing.T) {
  482. taskID := tracker.Tasks["Performance optimization"]
  483. var response struct {
  484. DeleteTask bool `json:"deleteTask"`
  485. }
  486. query := fmt.Sprintf(`mutation { deleteTask(id: "%s") }`, taskID)
  487. err := tc.client.Post(query, &response)
  488. if err != nil {
  489. t.Fatalf("Failed to delete task: %v", err)
  490. }
  491. var tasksResponse struct {
  492. Tasks []interface{} `json:"tasks"`
  493. }
  494. tc.client.Post(`query { tasks { id title content priority } }`, &tasksResponse)
  495. jsonBytes, _ := json.MarshalIndent(tasksResponse, "", " ")
  496. snapshotResult(t, "tasks_after_delete", string(jsonBytes))
  497. })
  498. }
  499. // TestIntegration_Subscriptions tests subscription functionality
  500. func TestIntegration_Subscriptions(t *testing.T) {
  501. db, err := testutil.SetupAndBootstrapTestDB()
  502. if err != nil {
  503. t.Fatalf("Failed to setup test database: %v", err)
  504. }
  505. resolver := NewResolver(db)
  506. schema := NewExecutableSchema(Config{Resolvers: resolver})
  507. srv := handler.NewDefaultServer(schema)
  508. authSrv := auth.AuthMiddleware(srv)
  509. // Create admin client
  510. adminClient := client.New(authSrv)
  511. // Login as admin
  512. var loginResponse struct {
  513. Login struct {
  514. Token string `json:"token"`
  515. User struct {
  516. ID string `json:"id"`
  517. Email string `json:"email"`
  518. } `json:"user"`
  519. } `json:"login"`
  520. }
  521. loginQuery := `mutation { login(email: "admin@example.com", password: "secret123") { token user { id email } } }`
  522. err = adminClient.Post(loginQuery, &loginResponse)
  523. if err != nil {
  524. t.Fatalf("Failed to login as admin: %v", err)
  525. }
  526. adminToken := loginResponse.Login.Token
  527. adminID := loginResponse.Login.User.ID
  528. adminClient = client.New(authSrv, client.AddHeader("Authorization", "Bearer "+adminToken))
  529. // Create user1
  530. var createUserResponse struct {
  531. CreateUser struct {
  532. ID string `json:"id"`
  533. Email string `json:"email"`
  534. } `json:"createUser"`
  535. }
  536. // Get user role ID
  537. var rolesResponse struct {
  538. Roles []struct {
  539. ID string `json:"id"`
  540. Name string `json:"name"`
  541. } `json:"roles"`
  542. }
  543. adminClient.Post(`query { roles { id name } }`, &rolesResponse)
  544. var userRoleID string
  545. for _, role := range rolesResponse.Roles {
  546. if role.Name == "user" {
  547. userRoleID = role.ID
  548. break
  549. }
  550. }
  551. createUserQuery := fmt.Sprintf(`mutation { createUser(input: {email: "user1@example.com", password: "password123", roles: ["%s"]}) { id email } }`, userRoleID)
  552. err = adminClient.Post(createUserQuery, &createUserResponse)
  553. if err != nil {
  554. t.Fatalf("Failed to create user1: %v", err)
  555. }
  556. user1ID := createUserResponse.CreateUser.ID
  557. // Login as user1
  558. var user1LoginResponse struct {
  559. Login struct {
  560. Token string `json:"token"`
  561. } `json:"login"`
  562. }
  563. user1LoginQuery := `mutation { login(email: "user1@example.com", password: "password123") { token } }`
  564. err = adminClient.Post(user1LoginQuery, &user1LoginResponse)
  565. if err != nil {
  566. t.Fatalf("Failed to login as user1: %v", err)
  567. }
  568. user1Token := user1LoginResponse.Login.Token
  569. // Create WebSocket client for user1
  570. user1WSClient := client.New(authSrv, client.AddHeader("Authorization", "Bearer "+user1Token))
  571. // Subscribe to taskCreated as user1
  572. taskCreatedSub := `subscription { taskCreated { id title content assigneeId } }`
  573. taskCreatedChan := make(chan *model.Task, 1)
  574. taskCreatedSubscription := user1WSClient.Websocket(taskCreatedSub)
  575. defer taskCreatedSubscription.Close()
  576. go func() {
  577. for {
  578. var taskCreatedResponse struct {
  579. TaskCreated *model.Task `json:"taskCreated"`
  580. }
  581. err := taskCreatedSubscription.Next(&taskCreatedResponse)
  582. if err != nil {
  583. return
  584. }
  585. if taskCreatedResponse.TaskCreated != nil {
  586. select {
  587. case taskCreatedChan <- taskCreatedResponse.TaskCreated:
  588. default:
  589. }
  590. }
  591. }
  592. }()
  593. // Subscribe to messageAdded as user1
  594. messageAddedSub := `subscription { messageAdded { id content conversationId senderId } }`
  595. messageAddedChan := make(chan *model.Message, 1)
  596. messageAddedSubscription := user1WSClient.Websocket(messageAddedSub)
  597. defer messageAddedSubscription.Close()
  598. go func() {
  599. for {
  600. var messageAddedResponse struct {
  601. MessageAdded *model.Message `json:"messageAdded"`
  602. }
  603. err := messageAddedSubscription.Next(&messageAddedResponse)
  604. if err != nil {
  605. return
  606. }
  607. if messageAddedResponse.MessageAdded != nil {
  608. select {
  609. case messageAddedChan <- messageAddedResponse.MessageAdded:
  610. default:
  611. }
  612. }
  613. }
  614. }()
  615. // Give subscriptions time to connect
  616. time.Sleep(100 * time.Millisecond)
  617. // Create TestConversation with admin and user1 as participants
  618. var createChannelResponse struct {
  619. CreateChannel struct {
  620. ID string `json:"id"`
  621. } `json:"createChannel"`
  622. }
  623. createChannelQuery := fmt.Sprintf(`mutation { createChannel(input: {participants: ["%s", "%s"]}) { id } }`, adminID, user1ID)
  624. err = adminClient.Post(createChannelQuery, &createChannelResponse)
  625. if err != nil {
  626. t.Fatalf("Failed to create channel: %v", err)
  627. }
  628. channelID := createChannelResponse.CreateChannel.ID
  629. // Admin sends a message to the channel
  630. var createMessageResponse struct {
  631. CreateMessage struct {
  632. ID string `json:"id"`
  633. } `json:"createMessage"`
  634. }
  635. createMessageQuery := fmt.Sprintf(`mutation { createMessage(input: {conversationId: "%s", senderId: "%s", content: "Hello user1!"}) { id } }`, channelID, adminID)
  636. err = adminClient.Post(createMessageQuery, &createMessageResponse)
  637. if err != nil {
  638. t.Fatalf("Failed to create message: %v", err)
  639. }
  640. // Wait for messageAdded event
  641. select {
  642. case msg := <-messageAddedChan:
  643. if msg == nil {
  644. t.Error("Expected messageAdded event, got nil")
  645. } else if msg.Content != "Hello user1!" {
  646. t.Errorf("Expected message content 'Hello user1!', got '%s'", msg.Content)
  647. } else {
  648. t.Log("user1 received messageAdded event successfully")
  649. }
  650. case <-time.After(2 * time.Second):
  651. t.Error("Timeout waiting for messageAdded event")
  652. }
  653. // Get a task status ID
  654. var statusesResponse struct {
  655. TaskStatuses []struct {
  656. ID string `json:"id"`
  657. Code string `json:"code"`
  658. } `json:"taskStatuses"`
  659. }
  660. adminClient.Post(`query { taskStatuses { id code } }`, &statusesResponse)
  661. var statusID string
  662. if len(statusesResponse.TaskStatuses) > 0 {
  663. statusID = statusesResponse.TaskStatuses[0].ID
  664. }
  665. // Admin creates a task assigned to user1
  666. var createTaskResponse struct {
  667. CreateTask struct {
  668. ID string `json:"id"`
  669. Title string `json:"title"`
  670. } `json:"createTask"`
  671. }
  672. 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)
  673. err = adminClient.Post(createTaskQuery, &createTaskResponse)
  674. if err != nil {
  675. t.Fatalf("Failed to create task: %v", err)
  676. }
  677. // Wait for taskCreated event
  678. select {
  679. case task := <-taskCreatedChan:
  680. if task == nil {
  681. t.Error("Expected taskCreated event, got nil")
  682. } else if task.Title != "Task for user1" {
  683. t.Errorf("Expected task title 'Task for user1', got '%s'", task.Title)
  684. } else {
  685. t.Log("user1 received taskCreated event successfully")
  686. }
  687. case <-time.After(2 * time.Second):
  688. t.Error("Timeout waiting for taskCreated event")
  689. }
  690. }
  691. // bootstrapData creates all entities for testing (skips existing items)
  692. func bootstrapData(t *testing.T, tc *TestClient, tracker *IDTracker, seed testutil.SeedData) {
  693. // Create Permissions (skip if already exists)
  694. for _, perm := range seed.Permissions {
  695. if _, exists := tracker.Permissions[perm.Code]; exists {
  696. continue
  697. }
  698. var response struct {
  699. CreatePermission struct {
  700. ID string `json:"id"`
  701. } `json:"createPermission"`
  702. }
  703. query := fmt.Sprintf(`mutation { createPermission(input: {code: "%s", description: "%s"}) { id } }`, perm.Code, perm.Description)
  704. err := tc.client.Post(query, &response)
  705. if err != nil {
  706. t.Fatalf("Failed to create permission %s: %v", perm.Code, err)
  707. }
  708. tracker.Permissions[perm.Code] = response.CreatePermission.ID
  709. }
  710. // Create Roles (skip if already exists)
  711. for _, role := range seed.Roles {
  712. if _, exists := tracker.Roles[role.Name]; exists {
  713. continue
  714. }
  715. permIDs := make([]string, len(role.PermissionCodes))
  716. for i, code := range role.PermissionCodes {
  717. permIDs[i] = tracker.Permissions[code]
  718. }
  719. var response struct {
  720. CreateRole struct {
  721. ID string `json:"id"`
  722. } `json:"createRole"`
  723. }
  724. query := fmt.Sprintf(`mutation { createRole(input: {name: "%s", description: "%s", permissions: ["%s"]}) { id } }`, role.Name, role.Description, strings.Join(permIDs, `", "`))
  725. err := tc.client.Post(query, &response)
  726. if err != nil {
  727. t.Fatalf("Failed to create role %s: %v", role.Name, err)
  728. }
  729. tracker.Roles[role.Name] = response.CreateRole.ID
  730. }
  731. // Create Users (skip if already exists)
  732. for _, user := range seed.Users {
  733. if _, exists := tracker.Users[user.Email]; exists {
  734. continue
  735. }
  736. roleIDs := make([]string, len(user.RoleNames))
  737. for i, name := range user.RoleNames {
  738. roleIDs[i] = tracker.Roles[name]
  739. }
  740. var response struct {
  741. CreateUser struct {
  742. ID string `json:"id"`
  743. } `json:"createUser"`
  744. }
  745. query := fmt.Sprintf(`mutation { createUser(input: {email: "%s", password: "%s", roles: ["%s"]}) { id } }`, user.Email, user.Password, strings.Join(roleIDs, `", "`))
  746. err := tc.client.Post(query, &response)
  747. if err != nil {
  748. t.Fatalf("Failed to create user %s: %v", user.Email, err)
  749. }
  750. tracker.Users[user.Email] = response.CreateUser.ID
  751. }
  752. // Create Task Statuses (skip if already exists)
  753. for _, status := range seed.TaskStatuses {
  754. if _, exists := tracker.TaskStatuses[status.Code]; exists {
  755. continue
  756. }
  757. var response struct {
  758. CreateTaskStatus struct {
  759. ID string `json:"id"`
  760. } `json:"createTaskStatus"`
  761. }
  762. query := fmt.Sprintf(`mutation { createTaskStatus(input: {code: "%s", label: "%s"}) { id } }`, status.Code, status.Label)
  763. err := tc.client.Post(query, &response)
  764. if err != nil {
  765. t.Fatalf("Failed to create task status %s: %v", status.Code, err)
  766. }
  767. tracker.TaskStatuses[status.Code] = response.CreateTaskStatus.ID
  768. }
  769. // Create Services
  770. for _, service := range seed.Services {
  771. participantIDs := make([]string, len(service.ParticipantEmails))
  772. for i, email := range service.ParticipantEmails {
  773. participantIDs[i] = tracker.Users[email]
  774. }
  775. var response struct {
  776. CreateService struct {
  777. ID string `json:"id"`
  778. } `json:"createService"`
  779. }
  780. 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, `", "`))
  781. err := tc.client.Post(query, &response)
  782. if err != nil {
  783. t.Fatalf("Failed to create service %s: %v", service.Name, err)
  784. }
  785. tracker.Services[service.Name] = response.CreateService.ID
  786. }
  787. // Create Tasks
  788. for _, task := range seed.Tasks {
  789. var response struct {
  790. CreateTask struct {
  791. ID string `json:"id"`
  792. } `json:"createTask"`
  793. }
  794. statusID := tracker.TaskStatuses[task.StatusCode]
  795. if task.AssigneeEmail != "" {
  796. 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)
  797. err := tc.client.Post(query, &response)
  798. if err != nil {
  799. t.Fatalf("Failed to create task %s: %v", task.Title, err)
  800. }
  801. } else {
  802. 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)
  803. err := tc.client.Post(query, &response)
  804. if err != nil {
  805. t.Fatalf("Failed to create task %s: %v", task.Title, err)
  806. }
  807. }
  808. tracker.Tasks[task.Title] = response.CreateTask.ID
  809. }
  810. // Create Notes
  811. for _, note := range seed.Notes {
  812. var response struct {
  813. CreateNote struct {
  814. ID string `json:"id"`
  815. } `json:"createNote"`
  816. }
  817. 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])
  818. err := tc.client.Post(query, &response)
  819. if err != nil {
  820. t.Fatalf("Failed to create note %s: %v", note.Title, err)
  821. }
  822. tracker.Notes[note.Title] = response.CreateNote.ID
  823. }
  824. // Create Channels
  825. for _, channel := range seed.Channels {
  826. participantIDs := make([]string, len(channel.ParticipantEmails))
  827. for i, email := range channel.ParticipantEmails {
  828. participantIDs[i] = tracker.Users[email]
  829. }
  830. var response struct {
  831. CreateChannel struct {
  832. ID string `json:"id"`
  833. } `json:"createChannel"`
  834. }
  835. query := fmt.Sprintf(`mutation { createChannel(input: {participants: ["%s"]}) { id } }`, strings.Join(participantIDs, `", "`))
  836. err := tc.client.Post(query, &response)
  837. if err != nil {
  838. t.Fatalf("Failed to create channel: %v", err)
  839. }
  840. tracker.Channels = append(tracker.Channels, response.CreateChannel.ID)
  841. }
  842. // Create Messages
  843. for _, msg := range seed.Messages {
  844. var response struct {
  845. CreateMessage struct {
  846. ID string `json:"id"`
  847. } `json:"createMessage"`
  848. }
  849. query := fmt.Sprintf(`mutation { createMessage(input: {conversationId: "%s", senderId: "%s", content: "%s"}) { id } }`, tracker.Channels[msg.ChannelIndex], tracker.Users[msg.SenderEmail], msg.Content)
  850. err := tc.client.Post(query, &response)
  851. if err != nil {
  852. t.Fatalf("Failed to create message: %v", err)
  853. }
  854. tracker.Messages = append(tracker.Messages, response.CreateMessage.ID)
  855. }
  856. }