import { ReportItemType, ReportItemWithContext } from '..'; import { TestReporter, TestTransport } from './utils'; describe('Transport', () => { beforeEach(() => { jest.resetAllMocks(); jest.useFakeTimers(); }); afterEach(() => { jest.useRealTimers(); }); afterAll(() => { jest.resetAllMocks(); }); it('construct with correct default options value', () => { const transport = new TestTransport(); expect((transport as any).bufferSize).toBe(50); expect((transport as any).flushInterval).toBe(10000); }); it('init correctly', () => { const setIntervalSpy = jest.spyOn(global, 'setInterval'); const transport = new TestTransport({ flushInterval: 500, }); const reporter = new TestReporter(); const flushSpy = jest.spyOn(transport as any, 'flush'); expect((transport as any).reporter).toBeUndefined(); jest.advanceTimersByTime(3100); expect(setIntervalSpy).not.toHaveBeenCalled(); transport.init(reporter); expect((transport as any).reporter).toBe(reporter); jest.advanceTimersByTime(3100); expect(setIntervalSpy).toHaveBeenCalledTimes(1); // flush 正确按间隔调用 expect(flushSpy).toHaveBeenCalledTimes(6); }); it("won't trigger setInterval if flushInterval is set to 0", () => { const setIntervalSpy = jest.spyOn(global, 'setInterval'); const transport = new TestTransport({ flushInterval: 0 }); const reporter = new TestReporter(); const flushSpy = jest.spyOn(transport as any, 'flush'); jest.advanceTimersByTime(3100); expect(setIntervalSpy).not.toHaveBeenCalled(); transport.init(reporter); jest.advanceTimersByTime(3100); expect(setIntervalSpy).not.toHaveBeenCalled(); expect((transport as any).flushIntervalTimer).toBeUndefined(); // flush 正确按间隔调用 expect(flushSpy).not.toHaveBeenCalled(); }); it('filter records correctly', () => { const transport = new TestTransport({ filter: (record) => record.type === ReportItemType.ERROR, }); // const transportSendSpy = jest.spyOn(transport, 'send'); const reporter = new TestReporter(); transport.init(reporter); const records = [ { type: ReportItemType.ERROR, ctime: 1, data: {}, context: { base: { user: {}, }, env: {}, scope: {}, }, }, { type: ReportItemType.BEHAVIOR, ctime: 2, data: {}, context: { base: { user: {}, }, env: {}, scope: {}, }, }, { type: ReportItemType.ERROR, ctime: 3, data: {}, context: { base: { user: {}, }, env: {}, scope: {}, }, }, { type: ReportItemType.BEHAVIOR, ctime: 4, data: {}, context: { base: { user: {}, }, env: {}, scope: {}, }, }, ]; transport.receiveFromReporter(records); // expect(transportSendSpy).toHaveBeenLastCalledWith([records[0], records[2]]); expect((transport as any).buffer).toEqual([records[0], records[2]]); }); test('sampleRate works ok', () => { const transport = new TestTransport({ sampleRate: 0.5, // 这样有极小概率不通过测试 bufferSize: 10000, }); const reporter = new TestReporter(); transport.init(reporter); const records = new Array(1000).fill({ type: ReportItemType.ERROR, ctime: 1, data: {}, context: { base: { user: {}, }, env: {}, scope: {}, }, }); transport.receiveFromReporter(records); // const consumedRecords = transport.consume(); // expect(consumedRecords.length > 0 && consumedRecords.length < 100).toBeTruthy(); expect((transport as any).buffer.length > 0 && (transport as any).buffer.length < 1000).toBeTruthy(); }); test('filter combined with sampleRate works ok', () => { const transport = new TestTransport({ filter: (record) => record.type === ReportItemType.ERROR, sampleRate: 0.5, // 这样有极小概率不通过测试 bufferSize: 10000, }); const reporter = new TestReporter(); transport.init(reporter); const records: ReportItemWithContext[] = []; for (let i = 0; i < 1000; i++) { if (i % 2 === 0) { records.push({ type: ReportItemType.ERROR, ctime: 1, data: {}, context: { base: { user: {}, }, env: {}, scope: {}, }, }); } else { records.push({ type: ReportItemType.BEHAVIOR, ctime: 1, data: {}, context: { base: { user: {}, }, env: {}, scope: {}, }, }); } } transport.receiveFromReporter(records); // const consumedRecords = transport.consume(); // expect(consumedRecords.length > 0 && consumedRecords.length < 50).toBeTruthy(); expect((transport as any).buffer.length > 0 && (transport as any).buffer.length < 500).toBeTruthy(); }); test('frequencyLimit works ok when perSeconds is 0', () => { /** * 限制上报总数 5 条 * 1. 第一次上报 3 条,应该 ok * 2. 等待 5s 后上报 3 条,应该只有两条报上去 * 3. 等待 5s 后上报 1 条,应该报不上去 */ const transport = new TestTransport({ bufferSize: 0, flushInterval: 0, frequencyLimit: { perSeconds: 0, max: 5, }, }); const reporter = new TestReporter(); transport.init(reporter); const MOCK_RECORD = { type: ReportItemType.BEHAVIOR, ctime: 2, data: {}, context: { base: { user: {}, }, env: {}, scope: {}, }, }; transport.receiveFromReporter([MOCK_RECORD, MOCK_RECORD, MOCK_RECORD]); expect(transport.consume().length).toBe(3); jest.advanceTimersByTime(5000); transport.receiveFromReporter([MOCK_RECORD, MOCK_RECORD, MOCK_RECORD]); expect(transport.consume().length).toBe(2); jest.advanceTimersByTime(5000); transport.receiveFromReporter([MOCK_RECORD]); expect(transport.consume().length).toBe(0); }); test('frequencyLimit works ok when perSeconds is not 0', () => { /** * 限制上报总数 5 条,perSeconds 5s * 1. 第一次上报 3 条,应该 ok * 2. 等待 3s 后上报 3 条,应该只有两条报上去 * 3. 等待 3s 后上报 6 条,应该报 5 条上去 * 4. 再立刻上报 1 条,应该报不上去 * 5. 等待 5s 后上报 1 条,应该报上去 */ const transport = new TestTransport({ bufferSize: 0, flushInterval: 0, frequencyLimit: { perSeconds: 5, max: 5, }, }); const reporter = new TestReporter(); transport.init(reporter); const MOCK_RECORD = { type: ReportItemType.BEHAVIOR, ctime: 2, data: {}, context: { base: { user: {}, }, env: {}, scope: {}, }, }; transport.receiveFromReporter([MOCK_RECORD, MOCK_RECORD, MOCK_RECORD]); expect(transport.consume().length).toBe(3); jest.advanceTimersByTime(3000); transport.receiveFromReporter([MOCK_RECORD, MOCK_RECORD, MOCK_RECORD]); expect(transport.consume().length).toBe(2); jest.advanceTimersByTime(3000); transport.receiveFromReporter([MOCK_RECORD, MOCK_RECORD, MOCK_RECORD, MOCK_RECORD, MOCK_RECORD, MOCK_RECORD]); expect(transport.consume().length).toBe(5); transport.receiveFromReporter([MOCK_RECORD]); expect(transport.consume().length).toBe(0); jest.advanceTimersByTime(5000); transport.receiveFromReporter([MOCK_RECORD]); expect(transport.consume().length).toBe(1); }); }); // describe('BuiltinTransports', () => { // const builtinTransports = new BuiltinTransports({ // Test: TestTransport, // }); // it('returns TestTransport instance when called with id', () => { // const t = builtinTransports.getTransport('Test'); // expect(t).toBeInstanceOf(TestTransport); // }); // // 若有可传入参数的内置 Transport,这里的实例换一下 // it('returns ConsoleTransport instance when called with [id, options]', () => { // const t = builtinTransports.getTransport(['Test', { foo: 'bar' }]); // expect(t).toBeInstanceOf(TestTransport); // }); // it('will throw when called with an wrong id', () => { // expect(() => builtinTransports.getTransport('foo' as any)).toThrowError(/Unknown builtin transport: foo/); // expect(() => builtinTransports.getTransport(['foo', {}] as any)).toThrowError(/Unknown builtin transport: foo/); // }); // });