Mirror: The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.
at main 4.7 kB view raw
1import { 2 Source, 3 pipe, 4 fromValue, 5 fromArray, 6 toPromise, 7 delay, 8 take, 9 tap, 10 map, 11} from 'wonka'; 12 13import { Client, Operation, OperationResult, CombinedError } from '@urql/core'; 14 15import { vi, expect, it } from 'vitest'; 16import { 17 queryResponse, 18 queryOperation, 19} from '../../../packages/core/src/test-utils'; 20import { persistedExchange } from './persistedExchange'; 21 22const makeExchangeArgs = () => { 23 const operations: Operation[] = []; 24 25 const result = vi.fn( 26 (operation: Operation): OperationResult => ({ ...queryResponse, operation }) 27 ); 28 29 return { 30 operations, 31 result, 32 exchangeArgs: { 33 forward: (op$: Source<Operation>) => 34 pipe( 35 op$, 36 tap(op => operations.push(op)), 37 map(result) 38 ), 39 client: new Client({ url: '/api', exchanges: [] }), 40 } as any, 41 }; 42}; 43 44it('adds the APQ extensions correctly', async () => { 45 const { exchangeArgs } = makeExchangeArgs(); 46 47 const res = await pipe( 48 fromValue(queryOperation), 49 persistedExchange()(exchangeArgs), 50 take(1), 51 toPromise 52 ); 53 54 expect(res.operation.context.persistAttempt).toBe(true); 55 expect(res.operation.extensions).toEqual({ 56 persistedQuery: { 57 version: 1, 58 sha256Hash: expect.any(String), 59 miss: undefined, 60 }, 61 }); 62}); 63 64it('retries query when persisted query resulted in miss', async () => { 65 const { result, operations, exchangeArgs } = makeExchangeArgs(); 66 67 result.mockImplementationOnce(operation => ({ 68 ...queryResponse, 69 operation, 70 error: new CombinedError({ 71 graphQLErrors: [{ message: 'PersistedQueryNotFound' }], 72 }), 73 })); 74 75 const res = await pipe( 76 fromValue(queryOperation), 77 persistedExchange()(exchangeArgs), 78 take(1), 79 toPromise 80 ); 81 82 expect(res.operation.context.persistAttempt).toBe(true); 83 expect(operations.length).toBe(2); 84 85 expect(operations[1].extensions).toEqual({ 86 persistedQuery: { 87 version: 1, 88 sha256Hash: expect.any(String), 89 miss: true, 90 }, 91 }); 92}); 93 94it('retries query persisted query resulted in unsupported', async () => { 95 const { result, operations, exchangeArgs } = makeExchangeArgs(); 96 97 result.mockImplementationOnce(operation => ({ 98 ...queryResponse, 99 operation, 100 error: new CombinedError({ 101 graphQLErrors: [{ message: 'PersistedQueryNotSupported' }], 102 }), 103 })); 104 105 await pipe( 106 fromArray([queryOperation, queryOperation]), 107 delay(0), 108 persistedExchange()(exchangeArgs), 109 take(2), 110 toPromise 111 ); 112 113 expect(operations.length).toBe(3); 114 115 expect(operations[1].extensions).toEqual({ 116 persistedQuery: undefined, 117 }); 118 119 expect(operations[2].extensions).toEqual(undefined); 120}); 121 122it('fails gracefully when an invalid result with `PersistedQueryNotFound` is always delivered', async () => { 123 const { result, operations, exchangeArgs } = makeExchangeArgs(); 124 125 result.mockImplementation(operation => ({ 126 ...queryResponse, 127 operation, 128 error: new CombinedError({ 129 graphQLErrors: [{ message: 'PersistedQueryNotFound' }], 130 }), 131 })); 132 133 const res = await pipe( 134 fromValue(queryOperation), 135 persistedExchange()(exchangeArgs), 136 take(1), 137 toPromise 138 ); 139 140 expect(res.operation.context.persistAttempt).toBe(true); 141 expect(operations.length).toBe(2); 142 143 expect(operations[1].extensions).toEqual({ 144 persistedQuery: { 145 version: 1, 146 sha256Hash: expect.any(String), 147 miss: true, 148 }, 149 }); 150 151 expect(console.warn).toHaveBeenLastCalledWith( 152 expect.stringMatching(/two misses/i) 153 ); 154}); 155 156it('skips operation when generateHash returns a nullish value', async () => { 157 const { result, operations, exchangeArgs } = makeExchangeArgs(); 158 159 result.mockImplementationOnce(operation => ({ 160 ...queryResponse, 161 operation, 162 data: null, 163 })); 164 165 const res = await pipe( 166 fromValue(queryOperation), 167 persistedExchange({ generateHash: async () => null })(exchangeArgs), 168 take(1), 169 toPromise 170 ); 171 172 expect(res.operation.context.persistAttempt).toBe(true); 173 expect(operations.length).toBe(1); 174 expect(operations[0]).not.toHaveProperty('extensions.persistedQuery'); 175}); 176 177it.each([true, false, 'force', 'within-url-limit'] as const)( 178 'sets `context.preferGetMethod` to %s when `options.preferGetForPersistedQueries` is %s', 179 async preferGetMethodValue => { 180 const { exchangeArgs } = makeExchangeArgs(); 181 182 const res = await pipe( 183 fromValue(queryOperation), 184 persistedExchange({ preferGetForPersistedQueries: preferGetMethodValue })( 185 exchangeArgs 186 ), 187 take(1), 188 toPromise 189 ); 190 191 expect(res.operation.context.preferGetMethod).toBe(preferGetMethodValue); 192 } 193);