import type { TbankShare } from '../../../../../integrations/tbank/tbank.api'; import { selectStockScreenerInstruments } from './stock-screener-selection.rules'; const asOf = new Date('2026-10-10T12:00:00.000Z'); function share(overrides: Partial = {}): TbankShare { return { apiTradeAvailableFlag: true, buyAvailableFlag: true, currency: 'RUB', lot: 10, name: 'North Star', ticker: 'NSTR', uid: 'uid-1', ...overrides, }; } describe('selectStockScreenerInstruments', () => { it('keeps only instruments matching every selected condition', () => { const result = selectStockScreenerInstruments( { asOf, instruments: [ share({ isin: 'A', sector: 'energy' }), share({ currency: 'USD', isin: 'B', sector: 'energy', uid: 'uid-2' }), share({ isin: 'C', name: 'Other', sector: 'energy', uid: 'uid-3' }), share({ isin: 'D', sector: 'finance', uid: 'uid-4' }), ], }, { currency: 'RUB', query: 'star', sector: 'energy' }, ); expect(result).toEqual({ asOf, instruments: [share({ isin: 'A', sector: 'energy' })] }); }); it.each([ ['sector', { sector: 'energy' }, share({ sector: undefined })], ['share type', { shareType: 'common' }, share({ shareType: 'preferred' })], ['currency', { currency: 'USD' }, share({ currency: undefined })], ['exchange', { realExchange: 'MOEX' }, share({ realExchange: undefined })], ['country of risk', { countryOfRisk: 'RU' }, share({ countryOfRisk: 'US' })], ['query', { query: 'star' }, share({ name: undefined, ticker: undefined })], ] as const)('excludes an unknown or unequal selected %s', (_condition, conditions, instrument) => { const result = selectStockScreenerInstruments( { asOf, instruments: [instrument] }, conditions, ); expect(result.instruments).toEqual([]); }); it('uses an inclusive maximum lot and ignores lots when it is not selected', () => { const instruments = [ share({ isin: 'equal', lot: 10, uid: 'equal' }), share({ isin: 'large', lot: 11, uid: 'large' }), share({ isin: 'unknown', lot: undefined, uid: 'unknown' }), share({ isin: 'zero', lot: 0, uid: 'zero' }), ]; expect( selectStockScreenerInstruments({ asOf, instruments }, { maxLot: 10 }).instruments.map( ({ uid }) => uid, ), ).toEqual(['equal']); expect( selectStockScreenerInstruments({ asOf, instruments }, {}).instruments.map(({ uid }) => uid), ).toEqual(['equal', 'large', 'unknown', 'zero']); }); it('chooses the smallest matching uid as the complete representative of an ISIN', () => { const chosen = share({ isin: ' same ', lot: 1, name: 'Chosen', uid: 'a' }); const result = selectStockScreenerInstruments( { asOf, instruments: [share({ isin: 'SAME', lot: 99, name: 'Sibling', uid: 'z' }), chosen], }, {}, ); expect(result.instruments).toEqual([chosen]); expect(result.instruments[0]).toMatchObject({ lot: 1, name: 'Chosen', uid: 'a' }); }); it('chooses the sole matching mode of an issue', () => { const matching = share({ isin: 'same', sector: 'energy', uid: 'matching' }); const result = selectStockScreenerInstruments( { asOf, instruments: [share({ isin: 'same', sector: 'finance', uid: 'smaller-uid' }), matching], }, { sector: 'energy' }, ); expect(result.instruments).toEqual([matching]); }); it('keeps different ISIN issues from one issuer separate', () => { const result = selectStockScreenerInstruments( { asOf, instruments: [ share({ isin: 'common', shareType: 'common', uid: 'common' }), share({ isin: 'preferred', shareType: 'preferred', uid: 'preferred' }), ], }, {}, ); expect(result.instruments.map(({ uid }) => uid)).toEqual(['common', 'preferred']); }); it('treats instruments without an ISIN as separate issues', () => { const result = selectStockScreenerInstruments( { asOf, instruments: [share({ isin: ' ', uid: 'first' }), share({ isin: undefined, uid: 'second' })], }, {}, ); expect(result.instruments.map(({ uid }) => uid)).toEqual(['first', 'second']); }); it('preserves exact whitespace-differing UIDs as separate blank-ISIN issues', () => { const result = selectStockScreenerInstruments( { asOf, instruments: [ share({ isin: ' ', uid: 'uid' }), share({ isin: undefined, uid: ' uid ' }), ], }, {}, ); expect(result.instruments.map(({ uid }) => uid)).toEqual([' uid ', 'uid']); }); it('returns the exact chosen uid for the later card link', () => { const result = selectStockScreenerInstruments( { asOf, instruments: [share({ isin: 'same', uid: 'sibling' }), share({ isin: 'SAME', uid: 'chosen' })], }, {}, ); expect(result.instruments[0]?.uid).toBe('chosen'); }); it.each([ [true, true, 1], [true, false, 0], [false, true, 0], [false, false, 0], [true, undefined, 0], [undefined, true, 0], ] as const)( 'includes only confirmed buy and API availability: buy=%s api=%s', (buyAvailableFlag, apiTradeAvailableFlag, expectedCount) => { const result = selectStockScreenerInstruments( { asOf, instruments: [share({ apiTradeAvailableFlag, buyAvailableFlag })] }, {}, ); expect(result.instruments).toHaveLength(expectedCount); }, ); it('orders representatives by ticker and then uid before paging', () => { const result = selectStockScreenerInstruments( { asOf, instruments: [ share({ isin: 'c', ticker: 'B', uid: 'z' }), share({ isin: 'a', ticker: 'A', uid: 'z' }), share({ isin: 'b', ticker: 'A', uid: 'a' }), share({ isin: 'empty', ticker: ' ', uid: 'empty' }), ], }, {}, ); expect(result.instruments.map(({ uid }) => uid)).toEqual(['empty', 'a', 'z', 'z']); }); });