Mirror of https://github.com/roostorg/coop github.com/roostorg/coop
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at 557ff54b2b435e5f1e789c6a8a4e1bebf2d7deb6 468 lines 14 kB view raw
1import { v1 as uuidv1 } from 'uuid'; 2 3import getBottle from '../../../iocContainer/index.js'; 4import { UserPermission } from '../../../models/types/permissioning.js'; 5import createOrg from '../../../test/fixtureHelpers/createOrg.js'; 6import createUser from '../../../test/fixtureHelpers/createUser.js'; 7import { makeTestWithFixture } from '../../../test/utils.js'; 8import CommentOperations from './CommentOperations.js'; 9 10describe('CommentOperations', () => { 11 const testWithFixtures = makeTestWithFixture(async () => { 12 const container = (await getBottle()).container; 13 const pgQuery = container.KyselyPg; 14 const commentOps = new CommentOperations(pgQuery); 15 16 // Create test org 17 const orgId = uuidv1(); 18 const { cleanup: orgCleanup } = await createOrg( 19 { Org: container.Sequelize.Org }, 20 container.ModerationConfigService, 21 container.ApiKeyService, 22 orgId, 23 ); 24 25 // Create test user 26 const { user, cleanup: userCleanup } = await createUser( 27 container.Sequelize, 28 orgId, 29 ); 30 31 // Create a queue (required for job_creations foreign key) 32 const queue = await container.ManualReviewToolService.createManualReviewQueue({ 33 name: 'Test Queue', 34 description: null, 35 userIds: [user.id], 36 hiddenActionIds: [], 37 isAppealsQueue: false, 38 invokedBy: { 39 userId: user.id, 40 permissions: [UserPermission.EDIT_MRT_QUEUES], 41 orgId, 42 }, 43 }); 44 45 // Create test item identifiers and jobs 46 const itemId = uuidv1(); 47 const itemTypeId = uuidv1(); 48 const jobId1 = uuidv1(); 49 const jobId2 = uuidv1(); 50 51 await pgQuery 52 .insertInto('manual_review_tool.job_creations') 53 .values([ 54 { 55 id: jobId1 as any, 56 org_id: orgId, 57 item_id: itemId, 58 item_type_id: itemTypeId, 59 queue_id: queue.id, 60 created_at: new Date('2023-01-01'), 61 enqueue_source_info: {}, 62 }, 63 { 64 id: jobId2 as any, 65 org_id: orgId, 66 item_id: itemId, 67 item_type_id: itemTypeId, 68 queue_id: queue.id, 69 created_at: new Date('2023-01-02'), 70 enqueue_source_info: {}, 71 }, 72 ]) 73 .execute(); 74 75 return { 76 commentOps, 77 pgQuery, 78 orgId, 79 userId: user.id, 80 itemId, 81 itemTypeId, 82 jobId1, 83 jobId2, 84 queueId: queue.id, 85 async cleanup() { 86 // Clean up comments 87 await pgQuery 88 .deleteFrom('manual_review_tool.job_comments') 89 .where('org_id', '=', orgId) 90 .execute(); 91 92 // Clean up job_creations 93 await pgQuery 94 .deleteFrom('manual_review_tool.job_creations') 95 .where('org_id', '=', orgId) 96 .execute(); 97 98 // Clean up queue 99 await container.ManualReviewToolService.deleteManualReviewQueueForTestsDO_NOT_USE( 100 orgId, 101 queue.id, 102 ); 103 104 // Clean up user and org 105 await userCleanup(); 106 await orgCleanup(); 107 108 // Close database connections 109 await container.KyselyPg.destroy(); 110 await container.KyselyPgReadReplica.destroy(); 111 }, 112 }; 113 }); 114 115 describe('getRelatedJobIds', () => { 116 testWithFixtures( 117 'should return single job ID when job not found in job_creations', 118 async ({ commentOps, orgId }) => { 119 const nonExistentJobId = uuidv1(); 120 121 // Access private method for testing 122 const result = await (commentOps as any).getRelatedJobIds({ 123 orgId, 124 jobId: nonExistentJobId, 125 }); 126 127 expect(result).toEqual([nonExistentJobId]); 128 }, 129 ); 130 131 testWithFixtures( 132 'should return all related job IDs when job found in job_creations', 133 async ({ commentOps, orgId, jobId1, jobId2 }) => { 134 const result = await (commentOps as any).getRelatedJobIds({ 135 orgId, 136 jobId: jobId1, 137 }); 138 139 expect(result).toHaveLength(2); 140 expect(result).toContain(jobId1); 141 expect(result).toContain(jobId2); 142 }, 143 ); 144 }); 145 146 describe('getComments', () => { 147 testWithFixtures( 148 'should return comments for single job when job not in job_creations', 149 async ({ commentOps, pgQuery, orgId, userId }) => { 150 const singleJobId = uuidv1(); 151 152 // Add a comment directly to a job not in job_creations 153 await pgQuery 154 .insertInto('manual_review_tool.job_comments') 155 .values({ 156 id: uuidv1(), 157 org_id: orgId, 158 job_id: singleJobId, 159 comment_text: 'Test comment', 160 author_id: userId, 161 created_at: new Date(), 162 }) 163 .execute(); 164 165 const result = await commentOps.getComments({ orgId, jobId: singleJobId }); 166 167 expect(result).toHaveLength(1); 168 expect(result[0].commentText).toBe('Test comment'); 169 }, 170 ); 171 172 testWithFixtures( 173 'should return comments for all related jobs when job found in job_creations', 174 async ({ commentOps, pgQuery, orgId, userId, jobId1, jobId2 }) => { 175 // Add comments to both related jobs 176 await pgQuery 177 .insertInto('manual_review_tool.job_comments') 178 .values([ 179 { 180 id: uuidv1(), 181 org_id: orgId, 182 job_id: jobId1, 183 comment_text: 'Comment from first queue', 184 author_id: userId, 185 created_at: new Date('2023-01-01T10:00:00Z'), 186 }, 187 { 188 id: uuidv1(), 189 org_id: orgId, 190 job_id: jobId2, 191 comment_text: 'Comment from second queue', 192 author_id: userId, 193 created_at: new Date('2023-01-02T10:00:00Z'), 194 }, 195 ]) 196 .execute(); 197 198 const result = await commentOps.getComments({ orgId, jobId: jobId1 }); 199 200 expect(result).toHaveLength(2); 201 expect(result[0].commentText).toBe('Comment from first queue'); 202 expect(result[1].commentText).toBe('Comment from second queue'); 203 }, 204 ); 205 206 testWithFixtures( 207 'should return empty array when no comments found', 208 async ({ commentOps, orgId, jobId1 }) => { 209 const result = await commentOps.getComments({ orgId, jobId: jobId1 }); 210 211 expect(result).toEqual([]); 212 }, 213 ); 214 215 testWithFixtures( 216 'should order comments by created_at ascending', 217 async ({ commentOps, pgQuery, orgId, userId, jobId1 }) => { 218 // Add comments with specific timestamps 219 await pgQuery 220 .insertInto('manual_review_tool.job_comments') 221 .values([ 222 { 223 id: uuidv1(), 224 org_id: orgId, 225 job_id: jobId1, 226 comment_text: 'Third comment', 227 author_id: userId, 228 created_at: new Date('2023-01-03T10:00:00Z'), 229 }, 230 { 231 id: uuidv1(), 232 org_id: orgId, 233 job_id: jobId1, 234 comment_text: 'First comment', 235 author_id: userId, 236 created_at: new Date('2023-01-01T10:00:00Z'), 237 }, 238 { 239 id: uuidv1(), 240 org_id: orgId, 241 job_id: jobId1, 242 comment_text: 'Second comment', 243 author_id: userId, 244 created_at: new Date('2023-01-02T10:00:00Z'), 245 }, 246 ]) 247 .execute(); 248 249 const result = await commentOps.getComments({ orgId, jobId: jobId1 }); 250 251 expect(result).toHaveLength(3); 252 expect(result[0].commentText).toBe('First comment'); 253 expect(result[1].commentText).toBe('Second comment'); 254 expect(result[2].commentText).toBe('Third comment'); 255 }, 256 ); 257 }); 258 259 describe('getCommentCount', () => { 260 testWithFixtures( 261 'should return 0 when no comments found', 262 async ({ commentOps, orgId, jobId1 }) => { 263 const result = await commentOps.getCommentCount({ orgId, jobId: jobId1 }); 264 265 expect(result).toBe(0); 266 }, 267 ); 268 269 testWithFixtures( 270 'should return correct count for cross-queue comments', 271 async ({ commentOps, pgQuery, orgId, userId, jobId1, jobId2 }) => { 272 // Add comments to both related jobs 273 await pgQuery 274 .insertInto('manual_review_tool.job_comments') 275 .values([ 276 { 277 id: uuidv1(), 278 org_id: orgId, 279 job_id: jobId1, 280 comment_text: 'Comment 1', 281 author_id: userId, 282 created_at: new Date(), 283 }, 284 { 285 id: uuidv1(), 286 org_id: orgId, 287 job_id: jobId1, 288 comment_text: 'Comment 2', 289 author_id: userId, 290 created_at: new Date(), 291 }, 292 { 293 id: uuidv1(), 294 org_id: orgId, 295 job_id: jobId2, 296 comment_text: 'Comment 3', 297 author_id: userId, 298 created_at: new Date(), 299 }, 300 ]) 301 .execute(); 302 303 const result = await commentOps.getCommentCount({ orgId, jobId: jobId1 }); 304 305 expect(result).toBe(3); 306 }, 307 ); 308 }); 309 310 describe('addComment', () => { 311 testWithFixtures( 312 'should add comment successfully', 313 async ({ commentOps, orgId, userId, jobId1 }) => { 314 const commentText = 'New test comment'; 315 316 const result = await commentOps.addComment({ 317 orgId, 318 jobId: jobId1, 319 commentText, 320 authorId: userId, 321 }); 322 323 expect(result.commentText).toBe(commentText); 324 expect(result.authorId).toBe(userId); 325 expect(result.id).toBeDefined(); 326 expect(result.createdAt).toBeInstanceOf(Date); 327 328 // Verify comment was actually inserted 329 const comments = await commentOps.getComments({ orgId, jobId: jobId1 }); 330 expect(comments).toHaveLength(1); 331 expect(comments[0].id).toBe(result.id); 332 }, 333 ); 334 }); 335 336 describe('deleteComment', () => { 337 testWithFixtures( 338 'should delete comment successfully', 339 async ({ commentOps, orgId, userId, jobId1 }) => { 340 // Add a comment first 341 const comment = await commentOps.addComment({ 342 orgId, 343 jobId: jobId1, 344 commentText: 'Comment to delete', 345 authorId: userId, 346 }); 347 348 const result = await commentOps.deleteComment({ 349 orgId, 350 jobId: jobId1, 351 userId, 352 commentId: comment.id, 353 }); 354 355 expect(result).toBe(true); 356 357 // Verify comment was actually deleted 358 const comments = await commentOps.getComments({ orgId, jobId: jobId1 }); 359 expect(comments).toHaveLength(0); 360 }, 361 ); 362 363 testWithFixtures( 364 'should return false when comment not found', 365 async ({ commentOps, orgId, userId, jobId1 }) => { 366 const nonExistentCommentId = uuidv1(); 367 368 const result = await commentOps.deleteComment({ 369 orgId, 370 jobId: jobId1, 371 userId, 372 commentId: nonExistentCommentId, 373 }); 374 375 expect(result).toBe(false); 376 }, 377 ); 378 379 testWithFixtures( 380 'should return false when comment not owned by user', 381 async ({ commentOps, orgId, userId, jobId1 }) => { 382 const otherUserId = uuidv1(); 383 384 // Add a comment with a different user 385 const comment = await commentOps.addComment({ 386 orgId, 387 jobId: jobId1, 388 commentText: 'Comment by other user', 389 authorId: otherUserId, 390 }); 391 392 // Try to delete with wrong user 393 const result = await commentOps.deleteComment({ 394 orgId, 395 jobId: jobId1, 396 userId, // Different user 397 commentId: comment.id, 398 }); 399 400 expect(result).toBe(false); 401 402 // Verify comment was not deleted 403 const comments = await commentOps.getComments({ orgId, jobId: jobId1 }); 404 expect(comments).toHaveLength(1); 405 }, 406 ); 407 }); 408 409 describe('Cross-queue functionality integration', () => { 410 testWithFixtures( 411 'should show comments from previous queue after job moves', 412 async ({ commentOps, orgId, userId, jobId1, jobId2 }) => { 413 // Add comment to first job 414 await commentOps.addComment({ 415 orgId, 416 jobId: jobId1, 417 commentText: 'Comment from original queue', 418 authorId: userId, 419 }); 420 421 // Add comment to second job (simulating a move to a new queue) 422 await commentOps.addComment({ 423 orgId, 424 jobId: jobId2, 425 commentText: 'Comment from new queue', 426 authorId: userId, 427 }); 428 429 // When querying from the second job, should see both comments 430 const result = await commentOps.getComments({ orgId, jobId: jobId2 }); 431 432 expect(result).toHaveLength(2); 433 const commentTexts = result.map((c) => c.commentText); 434 expect(commentTexts).toContain('Comment from original queue'); 435 expect(commentTexts).toContain('Comment from new queue'); 436 }, 437 ); 438 439 testWithFixtures( 440 'should count comments from all queues', 441 async ({ commentOps, orgId, userId, jobId1, jobId2 }) => { 442 // Add comments to both jobs 443 await commentOps.addComment({ 444 orgId, 445 jobId: jobId1, 446 commentText: 'Comment 1', 447 authorId: userId, 448 }); 449 await commentOps.addComment({ 450 orgId, 451 jobId: jobId2, 452 commentText: 'Comment 2', 453 authorId: userId, 454 }); 455 await commentOps.addComment({ 456 orgId, 457 jobId: jobId2, 458 commentText: 'Comment 3', 459 authorId: userId, 460 }); 461 462 const result = await commentOps.getCommentCount({ orgId, jobId: jobId2 }); 463 464 expect(result).toBe(3); 465 }, 466 ); 467 }); 468});