1- import { Connection } from '@solana/web3.js' ;
1+ import { fetchTransactionDetails } from '@entities/transaction-data/api/fetch-transaction-details' ;
2+ import { createSolanaRpc } from '@solana/kit' ;
23import { beforeEach , describe , expect , it , vi } from 'vitest' ;
34
4- import { Cluster } from '@/app/utils/cluster' ;
5+ import { Cluster , serverClusterUrl } from '@/app/utils/cluster' ;
56
67import { mockSingleTransferTransaction } from '../../mocks/single-transfer' ;
78import { getTx } from '../get-tx' ;
89
9- vi . mock ( '@solana/web3.js' , async ( ) => {
10- const actual = await vi . importActual ( '@solana/web3.js' ) ;
11- return {
12- ...actual ,
13- Connection : vi . fn ( ) ,
14- } ;
15- } ) ;
10+ vi . mock ( '@entities/transaction-data/api/fetch-transaction-details' , ( ) => ( {
11+ fetchTransactionDetails : vi . fn ( ) ,
12+ } ) ) ;
1613
1714vi . mock ( '../../env' , ( ) => ( {
1815 isClusterProbeEnabled : true ,
@@ -21,88 +18,90 @@ vi.mock('../../env', () => ({
2118describe ( 'getTx' , ( ) => {
2219 const mockSignature = '5yKzCuw1e9d58HcnzSL31cczfXUux2H4Ga5TAR2RcQLE5W8BiTAC9x9MvhLtc4h99sC9XxLEAjhrXyfKezdMkZFV' ;
2320
24- let mockConnection : {
25- getSignatureStatus : ReturnType < typeof vi . fn > ;
26- getParsedTransaction : ReturnType < typeof vi . fn > ;
27- } ;
21+ let mockGetSignatureStatuses : ReturnType < typeof vi . fn > ;
22+
23+ function statusResponse ( found : boolean ) {
24+ return {
25+ send : vi . fn ( ) . mockResolvedValue ( {
26+ value : [ found ? { confirmationStatus : 'confirmed' , slot : 12345n } : null ] ,
27+ } ) ,
28+ } ;
29+ }
30+
31+ function statusFailure ( error : Error ) {
32+ return { send : vi . fn ( ) . mockRejectedValue ( error ) } ;
33+ }
2834
2935 beforeEach ( ( ) => {
3036 vi . clearAllMocks ( ) ;
3137 vi . spyOn ( console , 'error' ) . mockImplementation ( ( ) => { } ) ;
3238
33- mockConnection = {
34- getParsedTransaction : vi . fn ( ) ,
35- getSignatureStatus : vi . fn ( ) ,
36- } ;
37-
38- vi . mocked ( Connection ) . mockImplementation ( function ( ) {
39- return mockConnection as unknown as Connection ;
40- } ) ;
39+ mockGetSignatureStatuses = vi . fn ( ) ;
40+ vi . mocked ( createSolanaRpc ) . mockReturnValue ( {
41+ getSignatureStatuses : mockGetSignatureStatuses ,
42+ } as unknown as ReturnType < typeof createSolanaRpc > ) ;
4143 } ) ;
4244
4345 describe ( 'successful cases' , ( ) => {
4446 it ( 'should return transaction and cluster when found' , async ( ) => {
45- mockConnection . getSignatureStatus . mockResolvedValueOnce ( {
46- value : {
47- confirmationStatus : 'confirmed' ,
48- slot : 12345 ,
49- } ,
50- } ) ;
51-
52- mockConnection . getParsedTransaction . mockResolvedValueOnce ( mockSingleTransferTransaction ) ;
47+ mockGetSignatureStatuses . mockReturnValueOnce ( statusResponse ( true ) ) ;
48+ vi . mocked ( fetchTransactionDetails ) . mockResolvedValueOnce ( mockSingleTransferTransaction ) ;
5349
5450 const result = await getTx ( mockSignature ) ;
5551
5652 expect ( result ) . toEqual ( {
5753 cluster : Cluster . MainnetBeta ,
5854 transaction : mockSingleTransferTransaction ,
5955 } ) ;
60- expect ( mockConnection . getSignatureStatus ) . toHaveBeenCalledTimes ( 1 ) ;
61- expect ( mockConnection . getParsedTransaction ) . toHaveBeenCalledTimes ( 1 ) ;
56+ expect ( mockGetSignatureStatuses ) . toHaveBeenCalledTimes ( 1 ) ;
57+ expect ( mockGetSignatureStatuses ) . toHaveBeenCalledWith ( [ mockSignature ] , {
58+ searchTransactionHistory : true ,
59+ } ) ;
60+ expect ( fetchTransactionDetails ) . toHaveBeenCalledTimes ( 1 ) ;
61+ expect ( fetchTransactionDetails ) . toHaveBeenCalledWith ( expect . any ( String ) , mockSignature ) ;
6262 } ) ;
6363
6464 it ( 'should return transaction and cluster when found on devnet' , async ( ) => {
65- mockConnection . getSignatureStatus . mockResolvedValueOnce ( { value : null } ) . mockResolvedValueOnce ( {
66- value : {
67- confirmationStatus : 'confirmed' ,
68- slot : 67890 ,
69- } ,
70- } ) ;
71-
72- mockConnection . getParsedTransaction . mockResolvedValueOnce ( mockSingleTransferTransaction ) ;
65+ mockGetSignatureStatuses
66+ . mockReturnValueOnce ( statusResponse ( false ) )
67+ . mockReturnValueOnce ( statusResponse ( true ) ) ;
68+ vi . mocked ( fetchTransactionDetails ) . mockResolvedValueOnce ( mockSingleTransferTransaction ) ;
7369
7470 const result = await getTx ( mockSignature ) ;
7571
7672 expect ( result ) . toEqual ( {
7773 cluster : Cluster . Devnet ,
7874 transaction : mockSingleTransferTransaction ,
7975 } ) ;
80- expect ( mockConnection . getSignatureStatus ) . toHaveBeenCalledTimes ( 2 ) ;
81- expect ( mockConnection . getParsedTransaction ) . toHaveBeenCalledTimes ( 1 ) ;
76+ expect ( mockGetSignatureStatuses ) . toHaveBeenCalledTimes ( 2 ) ;
77+ expect ( fetchTransactionDetails ) . toHaveBeenCalledTimes ( 1 ) ;
78+ expect ( fetchTransactionDetails ) . toHaveBeenCalledWith ( serverClusterUrl ( Cluster . Devnet ) , mockSignature ) ;
8279 } ) ;
8380 } ) ;
8481
8582 describe ( 'error handling' , ( ) => {
8683 it ( 'should throw error when cluster is not found' , async ( ) => {
87- mockConnection . getSignatureStatus . mockResolvedValue ( {
88- value : null ,
89- } ) ;
84+ mockGetSignatureStatuses . mockReturnValue ( statusResponse ( false ) ) ;
9085
9186 await expect ( getTx ( mockSignature ) ) . rejects . toThrow ( 'Cluster not found' ) ;
9287
93- expect ( mockConnection . getSignatureStatus ) . toHaveBeenCalledTimes ( 3 ) ;
88+ expect ( mockGetSignatureStatuses ) . toHaveBeenCalledTimes ( 3 ) ;
9489 } ) ;
9590
96- it ( 'should throw error when transaction is not found' , async ( ) => {
97- mockConnection . getSignatureStatus . mockResolvedValue ( {
98- value : {
99- confirmationStatus : 'confirmed' ,
100- slot : 12345 ,
101- } ,
91+ it ( 'should not report a transaction as found when the status response is empty' , async ( ) => {
92+ mockGetSignatureStatuses . mockReturnValue ( {
93+ send : vi . fn ( ) . mockResolvedValue ( { value : [ ] } ) ,
10294 } ) ;
10395
104- mockConnection . getParsedTransaction . mockResolvedValueOnce ( null ) ;
105- mockConnection . getParsedTransaction . mockResolvedValueOnce ( null ) ;
96+ await expect ( getTx ( mockSignature ) ) . rejects . toThrow ( 'Cluster not found' ) ;
97+
98+ expect ( mockGetSignatureStatuses ) . toHaveBeenCalledTimes ( 3 ) ;
99+ expect ( fetchTransactionDetails ) . not . toHaveBeenCalled ( ) ;
100+ } ) ;
101+
102+ it ( 'should throw error when transaction is not found' , async ( ) => {
103+ mockGetSignatureStatuses . mockReturnValue ( statusResponse ( true ) ) ;
104+ vi . mocked ( fetchTransactionDetails ) . mockResolvedValue ( null ) ;
106105
107106 await expect ( getTx ( mockSignature ) ) . rejects . toSatisfy ( ( error : Error ) => {
108107 return (
@@ -113,48 +112,41 @@ describe('getTx', () => {
113112 } ) ;
114113 } ) ;
115114
116- it ( 'should throw error when getParsedTransaction throws an error' , async ( ) => {
117- mockConnection . getSignatureStatus . mockResolvedValue ( {
118- value : {
119- confirmationStatus : 'confirmed' ,
120- slot : 12345 ,
121- } ,
122- } ) ;
115+ it ( 'should throw error when the transaction fetch throws an error' , async ( ) => {
116+ mockGetSignatureStatuses . mockReturnValue ( statusResponse ( true ) ) ;
123117
124118 const fetchError = new Error ( 'Failed to fetch' ) ;
125- mockConnection . getParsedTransaction . mockRejectedValueOnce ( fetchError ) ;
119+ vi . mocked ( fetchTransactionDetails ) . mockRejectedValueOnce ( fetchError ) ;
126120
127121 await expect ( getTx ( mockSignature ) ) . rejects . toSatisfy ( ( error : Error ) => {
128122 return error . message === 'Failed to fetch transaction' && error . cause === fetchError ;
129123 } ) ;
130124 } ) ;
131125
132126 it ( 'should throw immediately on mainnet network error' , async ( ) => {
133- mockConnection . getSignatureStatus . mockRejectedValueOnce ( new Error ( 'Forbidden access' ) ) ;
127+ mockGetSignatureStatuses . mockReturnValueOnce ( statusFailure ( new Error ( 'Forbidden access' ) ) ) ;
134128
135129 await expect ( getTx ( mockSignature ) ) . rejects . toThrow ( 'Failed to check the mainnet-beta' ) ;
136- expect ( mockConnection . getSignatureStatus ) . toHaveBeenCalledTimes ( 1 ) ;
130+ expect ( mockGetSignatureStatuses ) . toHaveBeenCalledTimes ( 1 ) ;
137131 } ) ;
138132
139133 it ( 'should throw on probe cluster network error' , async ( ) => {
140134 // Mainnet succeeds but tx not found
141- mockConnection . getSignatureStatus . mockResolvedValueOnce ( { value : null } ) ;
135+ mockGetSignatureStatuses . mockReturnValueOnce ( statusResponse ( false ) ) ;
142136 // Devnet fails with network error
143- mockConnection . getSignatureStatus . mockRejectedValueOnce ( new Error ( 'Network error' ) ) ;
137+ mockGetSignatureStatuses . mockReturnValueOnce ( statusFailure ( new Error ( 'Network error' ) ) ) ;
144138
145139 await expect ( getTx ( mockSignature ) ) . rejects . toThrow ( 'Failed to check the devnet' ) ;
146- expect ( mockConnection . getSignatureStatus ) . toHaveBeenCalledTimes ( 2 ) ;
140+ expect ( mockGetSignatureStatuses ) . toHaveBeenCalledTimes ( 2 ) ;
147141 } ) ;
148142 } ) ;
149143
150144 it ( 'should check all clusters' , async ( ) => {
151- mockConnection . getSignatureStatus . mockResolvedValue ( {
152- value : null ,
153- } ) ;
145+ mockGetSignatureStatuses . mockReturnValue ( statusResponse ( false ) ) ;
154146
155147 await expect ( getTx ( mockSignature ) ) . rejects . toThrow ( 'Cluster not found' ) ;
156148
157- expect ( Connection ) . toHaveBeenCalledTimes ( 3 ) ;
158- expect ( mockConnection . getSignatureStatus ) . toHaveBeenCalledTimes ( 3 ) ;
149+ expect ( createSolanaRpc ) . toHaveBeenCalledTimes ( 3 ) ;
150+ expect ( mockGetSignatureStatuses ) . toHaveBeenCalledTimes ( 3 ) ;
159151 } ) ;
160152} ) ;
0 commit comments