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 242 lines 7.1 kB view raw
1import { v1 as uuidv1 } from 'uuid'; 2 3import getBottle, { type Dependencies } from '../../iocContainer/index.js'; 4import { instantiateOpaqueType } from '../../utils/typescript-types.js'; 5import { 6 makeSubmissionId, 7 type NormalizedItemData, 8} from '../itemProcessingService/index.js'; 9import { type ItemSubmissionWithTypeIdentifier } from '../itemProcessingService/makeItemSubmissionWithTypeIdentifier.js'; 10import { 11 type ManualReviewToolService, 12 type ReportHistory, 13} from './manualReviewToolService.js'; 14 15describe('Manual Review Tool Service', () => { 16 let mrtService: ManualReviewToolService; 17 let container: Dependencies; 18 19 beforeAll(async () => { 20 // The mutation should be ok here since this is initial setup in a 21 // beforeAll; it doesn't involve reset state for each test in the suite 22 /* eslint-disable better-mutation/no-mutation */ 23 ({ container } = await getBottle()); 24 mrtService = container.ManualReviewToolService; 25 /* eslint-enable better-mutation/no-mutation */ 26 }); 27 28 afterAll(async () => { 29 await container.closeSharedResourcesForShutdown(); 30 }); 31 32 // Test that we can start the stalled jobs checker for manual job processing 33 test('should be able to start stalled jobs checker', async () => { 34 const worker = await mrtService['queueOps']['getBullWorker']({ 35 orgId: 'dummyOrg', 36 queueId: 'dummyQueue', 37 }); 38 // The startStalledCheckTimer method should be available and not throw 39 expect(worker).toBeDefined(); 40 }); 41 42 // TODO: rework when we rework the MRT error handling 43 test.skip('MRT throws for submitting a job that has already been moved to completed', async () => { 44 const orgId = 'e7c89ce7729', 45 queueId = '1', 46 reviewerId = uuidv1(), 47 reviewerEmail = 'test@test.com', 48 itemId = uuidv1(), 49 itemTypeId = uuidv1(); 50 51 await mrtService['queueOps']['addJob']({ 52 queueId, 53 enqueueSourceInfo: { kind: 'REPORT' }, 54 jobPayload: { 55 createdAt: new Date(), 56 payload: { 57 kind: 'DEFAULT', 58 reportHistory: [], 59 item: instantiateOpaqueType<ItemSubmissionWithTypeIdentifier>({ 60 submissionId: makeSubmissionId(), 61 // eslint-disable-next-line @typescript-eslint/consistent-type-assertions 62 data: {} as NormalizedItemData, 63 itemTypeIdentifier: { 64 id: itemTypeId, 65 version: new Date().toISOString(), 66 schemaVariant: 'original', 67 }, 68 creator: { 69 id: uuidv1(), 70 typeId: uuidv1(), 71 }, 72 itemId, 73 }), 74 reportedForReason: undefined, 75 reportedForReasons: [], 76 enqueueSourceInfo: { kind: 'REPORT' }, 77 }, 78 policyIds: [], 79 }, 80 orgId, 81 }); 82 83 const dequeuedJob = await mrtService.dequeueNextJob({ 84 orgId, 85 queueId, 86 userId: reviewerId, 87 }); 88 89 if (!dequeuedJob) { 90 throw new Error('should have dequeued successfully.'); 91 } 92 93 await mrtService.submitDecision({ 94 queueId, 95 reportHistory: [], 96 jobId: dequeuedJob.job.id, 97 lockToken: dequeuedJob.lockToken, 98 decisionComponents: [ 99 { 100 type: 'CUSTOM_ACTION', 101 actions: [{ id: '8481310e8c4' }], 102 policies: [], 103 itemIds: [itemId], 104 itemTypeId, 105 }, 106 ], 107 relatedActions: [], 108 reviewerId, 109 reviewerEmail, 110 orgId, 111 }); 112 113 const duplicativeDecision = async () => { 114 return mrtService.submitDecision({ 115 queueId, 116 reportHistory: [], 117 jobId: dequeuedJob.job.id, 118 lockToken: dequeuedJob.lockToken, 119 decisionComponents: [ 120 { 121 type: 'CUSTOM_ACTION', 122 actions: [{ id: '8481310e8c4' }], 123 policies: [], 124 itemIds: [itemId], 125 itemTypeId, 126 }, 127 ], 128 relatedActions: [], 129 reviewerId, 130 reviewerEmail, 131 orgId, 132 }); 133 }; 134 135 await expect(duplicativeDecision()).rejects.toThrow( 136 `No job with ID ${dequeuedJob.job.id} in queue with ID ${queueId}`, 137 ); 138 }); 139 140 describe('duplicate decision handling', () => { 141 function makeDummyJob() { 142 return { 143 createdAt: new Date(), 144 policyIds: [] as string[], 145 payload: { 146 kind: 'DEFAULT', 147 reportHistory: [] as ReportHistory, 148 item: instantiateOpaqueType<ItemSubmissionWithTypeIdentifier>({ 149 submissionId: makeSubmissionId(), 150 submissionTime: new Date(), 151 // eslint-disable-next-line @typescript-eslint/consistent-type-assertions 152 data: {} as NormalizedItemData, 153 itemTypeIdentifier: { 154 id: uuidv1(), 155 version: new Date().toISOString(), 156 schemaVariant: 'original', 157 }, 158 creator: { 159 id: uuidv1(), 160 typeId: uuidv1(), 161 }, 162 itemId: uuidv1(), 163 }), 164 enqueueSourceInfo: { kind: 'REPORT' }, 165 }, 166 } as const; 167 } 168 169 it('should reject duplicate decisions with the same lock token', async () => { 170 const orgId = 'e7c89ce7729', 171 queueId = '1', 172 reviewerId = uuidv1(), 173 reviewerEmail = 'test@test.com', 174 jobPayload = makeDummyJob(); 175 const itemId = jobPayload.payload.item.itemId, 176 itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; 177 178 await mrtService['queueOps']['addJob']({ 179 jobPayload, 180 orgId, 181 queueId, 182 enqueueSourceInfo: { kind: 'REPORT' }, 183 }); 184 185 const dequeuedJob = await mrtService.dequeueNextJob({ 186 orgId, 187 queueId, 188 userId: reviewerId, 189 }); 190 191 if (!dequeuedJob) { 192 throw new Error("should've returned a job"); 193 } 194 195 await mrtService.submitDecision({ 196 queueId, 197 reportHistory: [], 198 jobId: dequeuedJob.job.id, 199 lockToken: dequeuedJob.lockToken, 200 decisionComponents: [ 201 { 202 type: 'CUSTOM_ACTION', 203 actions: [{ id: '8481310e8c4' }], 204 policies: [], 205 itemIds: [itemId], 206 itemTypeId, 207 }, 208 ], 209 relatedActions: [], 210 reviewerId, 211 reviewerEmail, 212 orgId, 213 }); 214 215 const duplicativeDecision = async () => { 216 await mrtService.submitDecision({ 217 queueId, 218 reportHistory: [], 219 jobId: dequeuedJob.job.id, 220 lockToken: dequeuedJob.lockToken, 221 decisionComponents: [ 222 { 223 type: 'CUSTOM_ACTION', 224 actions: [{ id: '8481310e8c4' }], 225 policies: [], 226 itemIds: [itemId], 227 itemTypeId, 228 }, 229 ], 230 relatedActions: [], 231 reviewerId, 232 reviewerEmail, 233 orgId, 234 }); 235 }; 236 237 await expect(duplicativeDecision()).rejects.toThrow(); 238 }); 239 240 it.skip('should reject duplicate decisions on jobs dequeued again after the lock expires', async () => {}); 241 }); 242});