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 181 lines 6.3 kB view raw
1import { ScalarTypes } from '@roostorg/types'; 2 3import getBottle, { type Dependencies } from '../../iocContainer/index.js'; 4import { getBottleContainerWithIOMocks } from '../../test/setupMockedServer.js'; 5import { SignalType, type SignalsService } from '../signalsService/index.js'; 6import makeGetTransientRunSignalWithCache from './signalExecutionService.js'; 7 8describe('Signal Execution Service', () => { 9 let container: Dependencies; 10 let signalsService: SignalsService; 11 let getPolicyActionPenalties: Dependencies['getPolicyActionPenaltiesEventuallyConsistent']; 12 const mockLocationsLoader = async () => []; 13 const mockTextBankStringsLoader = jest.fn(async ({ bankId }) => 14 bankId === '1' ? ['a', 'b', 'c'] : bankId === '2' ? ['d', 'e', 'f'] : [], 15 ); 16 const mockGetImageBank = jest.fn(async ({ bankId }) => 17 bankId === 'test-bank' ? { id: 1, name: 'test-bank', hma_name: 'org_test-bank', description: null, enabled_ratio: 1.0, org_id: 'test-org', created_at: new Date(), updated_at: new Date() } : null, 18 ); 19 20 // eslint-disable-next-line better-mutation/no-mutation 21 mockLocationsLoader.close = jest.fn(); 22 // eslint-disable-next-line better-mutation/no-mutation 23 (mockTextBankStringsLoader as any).close = jest.fn(); 24 // eslint-disable-next-line better-mutation/no-mutation, @typescript-eslint/no-explicit-any 25 (mockGetImageBank as any).close = jest.fn(); 26 27 describe('getTransientRunSignalWithCache', () => { 28 beforeAll(async () => { 29 // No mutation rule here is a false positive, since this is more initial 30 // setup (we're never gonna reassign again later) that simply has to use 31 // `let` vars to defer the work until the test suite is actually running 32 // (so we don't bother w/ it if this test suite is skipped, e.g., in which 33 // case cleanup wouldn't happen). 34 /* eslint-disable better-mutation/no-mutation */ 35 container = await getBottleContainerWithIOMocks(); 36 signalsService = container.SignalsService; 37 getPolicyActionPenalties = 38 container.getPolicyActionPenaltiesEventuallyConsistent; 39 /* eslint-enable better-mutation/no-mutation */ 40 }); 41 42 afterAll(async () => { 43 await container.closeSharedResourcesForShutdown(); 44 }, 20_000); 45 46 beforeEach(() => { 47 // This is only safe while we're not running tests concurrently. 48 // Consider using the `makeTestWithFixture` helper instead to make 49 // a local copy of this state for each test. 50 jest.clearAllMocks(); 51 }); 52 53 test('should batch textBank loads w/i a single tick', async () => { 54 const runSignal = makeGetTransientRunSignalWithCache( 55 mockLocationsLoader, 56 mockTextBankStringsLoader, 57 getPolicyActionPenalties, 58 mockGetImageBank, 59 signalsService, 60 container.Tracer, 61 )(); 62 63 const signalInputs = [ 64 { 65 signal: { type: SignalType.TEXT_MATCHING_CONTAINS_REGEX }, 66 value: { type: ScalarTypes.STRING, value: 'a' }, 67 matchingValues: { 68 textBankIds: ['1'], 69 }, 70 threshold: null, 71 comparator: null, 72 userId: 'dummy', 73 orgId: 'dummmy', 74 }, 75 { 76 signal: { type: SignalType.TEXT_MATCHING_CONTAINS_TEXT }, 77 value: { type: ScalarTypes.STRING, value: 'a' }, 78 matchingValues: { 79 textBankIds: ['2'], 80 }, 81 threshold: null, 82 comparator: null, 83 userId: 'dummy', 84 orgId: 'dummmy', 85 }, 86 ] as const; 87 88 const results = await Promise.all(signalInputs.map(runSignal)); 89 expect(mockTextBankStringsLoader).toHaveBeenCalledTimes(2); 90 expect(mockTextBankStringsLoader.mock.calls).toMatchInlineSnapshot(` 91 [ 92 [ 93 { 94 "bankId": "1", 95 "orgId": "dummmy", 96 }, 97 ], 98 [ 99 { 100 "bankId": "2", 101 "orgId": "dummmy", 102 }, 103 ], 104 ] 105 `); 106 107 expect(results).toEqual([ 108 { 109 score: true, 110 outputType: { scalarType: ScalarTypes.BOOLEAN }, 111 matchedValue: 'a', 112 }, 113 { 114 score: false, 115 outputType: { scalarType: ScalarTypes.BOOLEAN }, 116 matchedValue: undefined, 117 }, 118 ]); 119 }); 120 121 test('should not re-run the same signal with the same input', async () => { 122 // NB: this test actually does run the signals, but that's fine; these 123 // signals don't hit the network. (The point of the mock is just so we can 124 // spy on how many times runSignal was called.) 125 const signalsServiceSpy = (await getBottle()).container.SignalsService; 126 // eslint-disable-next-line better-mutation/no-mutation 127 signalsServiceSpy.runSignal = jest.fn( 128 signalsServiceSpy.runSignal.bind(signalsServiceSpy), 129 ) as any; 130 131 const runSignal = makeGetTransientRunSignalWithCache( 132 mockLocationsLoader, 133 mockTextBankStringsLoader, 134 getPolicyActionPenalties, 135 mockGetImageBank, 136 signalsServiceSpy, 137 container.Tracer, 138 )(); 139 140 const signalInputs = [ 141 { 142 signal: { type: SignalType.TEXT_MATCHING_CONTAINS_REGEX }, 143 value: { type: ScalarTypes.STRING, value: 'a' }, 144 matchingValues: { 145 textBankIds: ['1'], 146 }, 147 threshold: null, 148 comparator: null, 149 userId: 'dummy', 150 orgId: 'dummmy', 151 }, 152 { 153 signal: { type: SignalType.TEXT_MATCHING_CONTAINS_REGEX }, 154 value: { type: ScalarTypes.STRING, value: 'a' }, 155 matchingValues: { 156 textBankIds: ['1'], 157 }, 158 threshold: null, 159 comparator: null, 160 userId: 'dummy', 161 orgId: 'dummmy', 162 }, 163 ] as const; 164 165 const results = await Promise.all(signalInputs.map(runSignal)); 166 expect(signalsServiceSpy.runSignal).toHaveBeenCalledTimes(1); 167 expect(results).toEqual([ 168 { 169 matchedValue: 'a', 170 score: true, 171 outputType: { scalarType: ScalarTypes.BOOLEAN }, 172 }, 173 { 174 matchedValue: 'a', 175 score: true, 176 outputType: { scalarType: ScalarTypes.BOOLEAN }, 177 }, 178 ]); 179 }); 180 }); 181});