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

Configure Feed

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

at main 158 lines 6.2 kB view raw
1import { randomUUID } from 'crypto'; 2import { Kysely, type CompiledQuery, type QueryResult } from 'kysely'; 3 4import { type Dependencies } from '../../iocContainer/index.js'; 5import { type MockedFn } from '../../test/mockHelpers/jestMocks.js'; 6import { makeMockWarehouseDialect } from '../../test/stubs/makeMockWarehouseKyselyDialect.js'; 7import { safePick } from '../../utils/misc.js'; 8import { makeFetchUserSubmissionStatistics } from './fetchUserSubmissionStatistics.js'; 9 10describe('fetchUserSubmissionStatistics', () => { 11 let warehouseMock: MockedFn< 12 (it: CompiledQuery) => Promise<QueryResult<unknown>> 13 >; 14 let sut: ReturnType<typeof makeFetchUserSubmissionStatistics>; 15 16 beforeEach(() => { 17 // This mutation is safe (while we're not running tests concurrently) as 18 // it's local to the test suite. Consider using the `makeTestWithFixture` 19 // helper instead to make a local copy of this state for each test. 20 21 warehouseMock = jest.fn(async (_it) => Promise.resolve({ rows: [] })); 22 23 // This mutation is safe (while we're not running tests concurrently) as 24 // it's local to the test suite. Consider using the `makeTestWithFixture` 25 // helper instead to make a local copy of this state for each test. 26 27 const kysely = new Kysely({ 28 dialect: makeMockWarehouseDialect(warehouseMock), 29 }); 30 const dialectMock: Dependencies['DataWarehouseDialect'] = { 31 getKyselyInstance: () => kysely, 32 destroy: jest.fn(async () => {}), 33 }; 34 35 sut = makeFetchUserSubmissionStatistics(dialectMock); 36 }); 37 38 test('should generate proper query given org + user ids only', async () => { 39 await sut({ orgId: 'x', userItemIdentifiers: [{ id: '1', typeId: 'a' }] }); 40 await sut({ 41 orgId: 'x', 42 userItemIdentifiers: [ 43 { id: '1', typeId: 'a' }, 44 { id: '3', typeId: 'b' }, 45 ], 46 }); 47 48 expect(warehouseMock).toHaveBeenCalledTimes(2); 49 50 const queriesRan = warehouseMock.mock.calls.map((it) => 51 safePick(it[0], ['parameters', 'sql']), 52 ); 53 54 expect(queriesRan).toMatchInlineSnapshot(` 55 [ 56 { 57 "parameters": [ 58 "x", 59 "1", 60 "a", 61 ], 62 "sql": "select "USER_ID" as "userId", "USER_TYPE_ID" as "userTypeId", "ITEM_TYPE_ID" as "itemTypeId", sum("NUM_SUBMISSIONS") as "numSubmissions" from "USER_STATISTICS_SERVICE"."SUBMISSION_STATS" where "ORG_ID" = :1 and ("USER_ID" = :2 and "USER_TYPE_ID" = :3) group by "USER_ID", "USER_TYPE_ID", "ITEM_TYPE_ID"", 63 }, 64 { 65 "parameters": [ 66 "x", 67 "1", 68 "a", 69 "3", 70 "b", 71 ], 72 "sql": "select "USER_ID" as "userId", "USER_TYPE_ID" as "userTypeId", "ITEM_TYPE_ID" as "itemTypeId", sum("NUM_SUBMISSIONS") as "numSubmissions" from "USER_STATISTICS_SERVICE"."SUBMISSION_STATS" where "ORG_ID" = :1 and (("USER_ID" = :2 and "USER_TYPE_ID" = :3) or ("USER_ID" = :4 and "USER_TYPE_ID" = :5)) group by "USER_ID", "USER_TYPE_ID", "ITEM_TYPE_ID"", 73 }, 74 ] 75 `); 76 }); 77 78 test('should batch queries of more than 16,000 unique user ids', async () => { 79 const numUserIds = Math.floor(16_000 / Math.max(Math.random(), 0.05)); // some big int over 16,000 80 const largeUserIdList = Array.from({ length: numUserIds }, (_) => ({ 81 id: randomUUID(), 82 typeId: randomUUID(), 83 })); 84 85 await sut({ orgId: 'x', userItemIdentifiers: largeUserIdList }); 86 expect(warehouseMock.mock.calls.length).toBeGreaterThan(1); 87 }); 88 89 test('should generate proper query given user/org ids + date filters', async () => { 90 await sut({ 91 orgId: 'x', 92 userItemIdentifiers: [{ id: '1', typeId: 'a' }], 93 startTime: new Date('2020-01-01T00:00Z'), 94 }); 95 96 await sut({ 97 orgId: 'x', 98 userItemIdentifiers: [ 99 { id: '1', typeId: 'a' }, 100 { id: '3', typeId: 'b' }, 101 ], 102 endTime: new Date('2020-01-01T00:00:00Z'), 103 }); 104 105 await sut({ 106 orgId: 'x', 107 userItemIdentifiers: [ 108 { id: '1', typeId: 'a' }, 109 { id: '3', typeId: 'b' }, 110 ], 111 endTime: new Date('2020-01-01T00:00:00Z'), 112 startTime: new Date('2020-02-01T00:00:00Z'), 113 }); 114 115 expect(warehouseMock).toHaveBeenCalledTimes(3); 116 117 const queriesRan = warehouseMock.mock.calls.map((it) => 118 safePick(it[0], ['parameters', 'sql']), 119 ); 120 121 expect(queriesRan).toMatchInlineSnapshot(` 122 [ 123 { 124 "parameters": [ 125 "x", 126 "1", 127 "a", 128 2020-01-01T00:00:00.000Z, 129 ], 130 "sql": "select "USER_ID" as "userId", "USER_TYPE_ID" as "userTypeId", "ITEM_TYPE_ID" as "itemTypeId", sum("NUM_SUBMISSIONS") as "numSubmissions" from "USER_STATISTICS_SERVICE"."SUBMISSION_STATS" where "ORG_ID" = :1 and ("USER_ID" = :2 and "USER_TYPE_ID" = :3) and "TS_START_INCLUSIVE" >= :4 group by "USER_ID", "USER_TYPE_ID", "ITEM_TYPE_ID"", 131 }, 132 { 133 "parameters": [ 134 "x", 135 "1", 136 "a", 137 "3", 138 "b", 139 2020-01-01T00:00:00.000Z, 140 ], 141 "sql": "select "USER_ID" as "userId", "USER_TYPE_ID" as "userTypeId", "ITEM_TYPE_ID" as "itemTypeId", sum("NUM_SUBMISSIONS") as "numSubmissions" from "USER_STATISTICS_SERVICE"."SUBMISSION_STATS" where "ORG_ID" = :1 and (("USER_ID" = :2 and "USER_TYPE_ID" = :3) or ("USER_ID" = :4 and "USER_TYPE_ID" = :5)) and "TS_END_EXCLUSIVE" <= :6 group by "USER_ID", "USER_TYPE_ID", "ITEM_TYPE_ID"", 142 }, 143 { 144 "parameters": [ 145 "x", 146 "1", 147 "a", 148 "3", 149 "b", 150 2020-02-01T00:00:00.000Z, 151 2020-01-01T00:00:00.000Z, 152 ], 153 "sql": "select "USER_ID" as "userId", "USER_TYPE_ID" as "userTypeId", "ITEM_TYPE_ID" as "itemTypeId", sum("NUM_SUBMISSIONS") as "numSubmissions" from "USER_STATISTICS_SERVICE"."SUBMISSION_STATS" where "ORG_ID" = :1 and (("USER_ID" = :2 and "USER_TYPE_ID" = :3) or ("USER_ID" = :4 and "USER_TYPE_ID" = :5)) and "TS_START_INCLUSIVE" >= :6 and "TS_END_EXCLUSIVE" <= :7 group by "USER_ID", "USER_TYPE_ID", "ITEM_TYPE_ID"", 154 }, 155 ] 156 `); 157 }); 158});