| 
 | 1 | +import {  | 
 | 2 | +    SOLANA_ERROR__JSON_RPC__INTERNAL_ERROR,  | 
 | 3 | +    SOLANA_ERROR__JSON_RPC__INVALID_PARAMS,  | 
 | 4 | +    SOLANA_ERROR__JSON_RPC__INVALID_REQUEST,  | 
 | 5 | +    SOLANA_ERROR__JSON_RPC__METHOD_NOT_FOUND,  | 
 | 6 | +    SOLANA_ERROR__JSON_RPC__PARSE_ERROR,  | 
 | 7 | +    SOLANA_ERROR__JSON_RPC__SCAN_ERROR,  | 
 | 8 | +    SOLANA_ERROR__JSON_RPC__SERVER_ERROR_BLOCK_CLEANED_UP,  | 
 | 9 | +    SOLANA_ERROR__JSON_RPC__SERVER_ERROR_BLOCK_NOT_AVAILABLE,  | 
 | 10 | +    SOLANA_ERROR__JSON_RPC__SERVER_ERROR_BLOCK_STATUS_NOT_AVAILABLE_YET,  | 
 | 11 | +    SOLANA_ERROR__JSON_RPC__SERVER_ERROR_KEY_EXCLUDED_FROM_SECONDARY_INDEX,  | 
 | 12 | +    SOLANA_ERROR__JSON_RPC__SERVER_ERROR_LONG_TERM_STORAGE_SLOT_SKIPPED,  | 
 | 13 | +    SOLANA_ERROR__JSON_RPC__SERVER_ERROR_MIN_CONTEXT_SLOT_NOT_REACHED,  | 
 | 14 | +    SOLANA_ERROR__JSON_RPC__SERVER_ERROR_NO_SNAPSHOT,  | 
 | 15 | +    SOLANA_ERROR__JSON_RPC__SERVER_ERROR_NODE_UNHEALTHY,  | 
 | 16 | +    SOLANA_ERROR__JSON_RPC__SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE,  | 
 | 17 | +    SOLANA_ERROR__JSON_RPC__SERVER_ERROR_SLOT_SKIPPED,  | 
 | 18 | +    SOLANA_ERROR__JSON_RPC__SERVER_ERROR_TRANSACTION_HISTORY_NOT_AVAILABLE,  | 
 | 19 | +    SOLANA_ERROR__JSON_RPC__SERVER_ERROR_TRANSACTION_PRECOMPILE_VERIFICATION_FAILURE,  | 
 | 20 | +    SOLANA_ERROR__JSON_RPC__SERVER_ERROR_TRANSACTION_SIGNATURE_LEN_MISMATCH,  | 
 | 21 | +    SOLANA_ERROR__JSON_RPC__SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE,  | 
 | 22 | +    SOLANA_ERROR__JSON_RPC__SERVER_ERROR_UNSUPPORTED_TRANSACTION_VERSION,  | 
 | 23 | +    SolanaErrorCode,  | 
 | 24 | +} from '../codes';  | 
 | 25 | +import { SolanaErrorContext } from '../context';  | 
 | 26 | +import { SolanaError } from '../error';  | 
 | 27 | +import { getSolanaErrorFromJsonRpcError } from '../json-rpc-error';  | 
 | 28 | +import { getSolanaErrorFromTransactionError } from '../transaction-error';  | 
 | 29 | + | 
 | 30 | +jest.mock('../transaction-error.ts');  | 
 | 31 | + | 
 | 32 | +describe('getSolanaErrorFromJsonRpcError', () => {  | 
 | 33 | +    it('produces a `SolanaError` with the same code as the one given', () => {  | 
 | 34 | +        const code = 123 as SolanaErrorCode;  | 
 | 35 | +        const error = getSolanaErrorFromJsonRpcError({ code, message: 'o no' });  | 
 | 36 | +        expect(error).toHaveProperty('context.__code', 123);  | 
 | 37 | +    });  | 
 | 38 | +    describe.each([  | 
 | 39 | +        SOLANA_ERROR__JSON_RPC__SERVER_ERROR_MIN_CONTEXT_SLOT_NOT_REACHED,  | 
 | 40 | +        SOLANA_ERROR__JSON_RPC__SERVER_ERROR_NODE_UNHEALTHY,  | 
 | 41 | +    ])('given a %s JSON-RPC error known to have data', jsonRpcErrorCode => {  | 
 | 42 | +        const expectedData = { baz: 'bat', foo: 'bar' } as unknown as SolanaErrorContext[SolanaErrorCode];  | 
 | 43 | +        it('does not set the server message on context', () => {  | 
 | 44 | +            const error = getSolanaErrorFromJsonRpcError({  | 
 | 45 | +                code: jsonRpcErrorCode,  | 
 | 46 | +                data: expectedData,  | 
 | 47 | +                message: 'o no',  | 
 | 48 | +            });  | 
 | 49 | +            expect(error).not.toHaveProperty('context.__serverMessage');  | 
 | 50 | +        });  | 
 | 51 | +        it('produces a `SolanaError` with that data as context', () => {  | 
 | 52 | +            const error = getSolanaErrorFromJsonRpcError({  | 
 | 53 | +                code: jsonRpcErrorCode,  | 
 | 54 | +                data: expectedData,  | 
 | 55 | +                message: 'o no',  | 
 | 56 | +            });  | 
 | 57 | +            expect(error).toHaveProperty('context', expect.objectContaining(expectedData));  | 
 | 58 | +        });  | 
 | 59 | +    });  | 
 | 60 | +    describe.each([  | 
 | 61 | +        SOLANA_ERROR__JSON_RPC__INTERNAL_ERROR,  | 
 | 62 | +        SOLANA_ERROR__JSON_RPC__INVALID_PARAMS,  | 
 | 63 | +        SOLANA_ERROR__JSON_RPC__INVALID_REQUEST,  | 
 | 64 | +        SOLANA_ERROR__JSON_RPC__METHOD_NOT_FOUND,  | 
 | 65 | +        SOLANA_ERROR__JSON_RPC__PARSE_ERROR,  | 
 | 66 | +        SOLANA_ERROR__JSON_RPC__SCAN_ERROR,  | 
 | 67 | +        SOLANA_ERROR__JSON_RPC__SERVER_ERROR_BLOCK_CLEANED_UP,  | 
 | 68 | +        SOLANA_ERROR__JSON_RPC__SERVER_ERROR_BLOCK_NOT_AVAILABLE,  | 
 | 69 | +        SOLANA_ERROR__JSON_RPC__SERVER_ERROR_BLOCK_STATUS_NOT_AVAILABLE_YET,  | 
 | 70 | +        SOLANA_ERROR__JSON_RPC__SERVER_ERROR_KEY_EXCLUDED_FROM_SECONDARY_INDEX,  | 
 | 71 | +        SOLANA_ERROR__JSON_RPC__SERVER_ERROR_LONG_TERM_STORAGE_SLOT_SKIPPED,  | 
 | 72 | +        SOLANA_ERROR__JSON_RPC__SERVER_ERROR_SLOT_SKIPPED,  | 
 | 73 | +        SOLANA_ERROR__JSON_RPC__SERVER_ERROR_TRANSACTION_PRECOMPILE_VERIFICATION_FAILURE,  | 
 | 74 | +        SOLANA_ERROR__JSON_RPC__SERVER_ERROR_UNSUPPORTED_TRANSACTION_VERSION,  | 
 | 75 | +    ])(  | 
 | 76 | +        'given a %s JSON-RPC error known to have no data but important context in the server message',  | 
 | 77 | +        jsonRpcErrorCode => {  | 
 | 78 | +            it('produces a `SolanaError` with the server message on the context', () => {  | 
 | 79 | +                const error = getSolanaErrorFromJsonRpcError({ code: jsonRpcErrorCode, message: 'o no' });  | 
 | 80 | +                expect(error).toHaveProperty('context.__serverMessage', 'o no');  | 
 | 81 | +            });  | 
 | 82 | +        },  | 
 | 83 | +    );  | 
 | 84 | +    describe.each([  | 
 | 85 | +        SOLANA_ERROR__JSON_RPC__SERVER_ERROR_MIN_CONTEXT_SLOT_NOT_REACHED,  | 
 | 86 | +        SOLANA_ERROR__JSON_RPC__SERVER_ERROR_NO_SNAPSHOT,  | 
 | 87 | +        SOLANA_ERROR__JSON_RPC__SERVER_ERROR_NODE_UNHEALTHY,  | 
 | 88 | +        SOLANA_ERROR__JSON_RPC__SERVER_ERROR_TRANSACTION_HISTORY_NOT_AVAILABLE,  | 
 | 89 | +        SOLANA_ERROR__JSON_RPC__SERVER_ERROR_TRANSACTION_SIGNATURE_LEN_MISMATCH,  | 
 | 90 | +        SOLANA_ERROR__JSON_RPC__SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE,  | 
 | 91 | +    ])(  | 
 | 92 | +        'given a %s JSON-RPC error known to have neither data nor important context in the server message',  | 
 | 93 | +        jsonRpcErrorCode => {  | 
 | 94 | +            it('produces a `SolanaError` without the server message on the context', () => {  | 
 | 95 | +                const error = getSolanaErrorFromJsonRpcError({ code: jsonRpcErrorCode, message: 'o no' });  | 
 | 96 | +                expect(error).not.toHaveProperty('context.__serverMessage', 'o no');  | 
 | 97 | +            });  | 
 | 98 | +        },  | 
 | 99 | +    );  | 
 | 100 | +    describe.each([[1, 2, 3], Symbol('a symbol'), 1, 1n, true, false])('when given non-object data like `%s`', data => {  | 
 | 101 | +        it('does not add the data to `context`', () => {  | 
 | 102 | +            const error = getSolanaErrorFromJsonRpcError({  | 
 | 103 | +                code: 123,  | 
 | 104 | +                data,  | 
 | 105 | +                message: 'o no',  | 
 | 106 | +            });  | 
 | 107 | +            expect(error).toHaveProperty(  | 
 | 108 | +                'context',  | 
 | 109 | +                // Implies exact match; `context` contains nothing but the `__code`  | 
 | 110 | +                { __code: 123 },  | 
 | 111 | +            );  | 
 | 112 | +        });  | 
 | 113 | +    });  | 
 | 114 | +    describe('when passed a preflight failure', () => {  | 
 | 115 | +        it('produces a `SolanaError` with the transaction error as the `cause`', () => {  | 
 | 116 | +            const mockErrorResult = Symbol() as unknown as SolanaError;  | 
 | 117 | +            jest.mocked(getSolanaErrorFromTransactionError).mockReturnValue(mockErrorResult);  | 
 | 118 | +            const error = getSolanaErrorFromJsonRpcError({  | 
 | 119 | +                code: SOLANA_ERROR__JSON_RPC__SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE,  | 
 | 120 | +                data: { err: Symbol() },  | 
 | 121 | +                message: 'o no',  | 
 | 122 | +            });  | 
 | 123 | +            expect(error.cause).toBe(mockErrorResult);  | 
 | 124 | +        });  | 
 | 125 | +        it('produces a `SolanaError` with the preflight failure data (minus the `err` property) as the context', () => {  | 
 | 126 | +            const preflightErrorData = { bar: 2, baz: 3, foo: 1 };  | 
 | 127 | +            const error = getSolanaErrorFromJsonRpcError({  | 
 | 128 | +                code: SOLANA_ERROR__JSON_RPC__SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE,  | 
 | 129 | +                data: { ...preflightErrorData, err: Symbol() },  | 
 | 130 | +                message: 'o no',  | 
 | 131 | +            });  | 
 | 132 | +            expect(error.context).toEqual({  | 
 | 133 | +                __code: SOLANA_ERROR__JSON_RPC__SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE,  | 
 | 134 | +                ...preflightErrorData,  | 
 | 135 | +            });  | 
 | 136 | +        });  | 
 | 137 | +        it('delegates `err` to the transaction error getter', () => {  | 
 | 138 | +            const transactionError = Symbol();  | 
 | 139 | +            getSolanaErrorFromJsonRpcError({  | 
 | 140 | +                code: SOLANA_ERROR__JSON_RPC__SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE,  | 
 | 141 | +                data: { err: transactionError },  | 
 | 142 | +                message: 'o no',  | 
 | 143 | +            });  | 
 | 144 | +            expect(getSolanaErrorFromTransactionError).toHaveBeenCalledWith(transactionError);  | 
 | 145 | +        });  | 
 | 146 | +    });  | 
 | 147 | +});  | 
0 commit comments