|
1 | 1 | import { createConsentManager } from './consent'; |
2 | 2 | import type { HttpSend } from './transport'; |
3 | 3 |
|
4 | | -function createMockSend(): jest.MockedFunction<HttpSend> { |
| 4 | +function createMockSend() { |
5 | 5 | return jest.fn<ReturnType<HttpSend>, Parameters<HttpSend>>().mockResolvedValue({ ok: true }); |
6 | 6 | } |
7 | 7 |
|
@@ -117,4 +117,85 @@ describe('createConsentManager', () => { |
117 | 117 |
|
118 | 118 | Object.defineProperty(navigator, 'doNotTrack', { value: '0', configurable: true }); |
119 | 119 | }); |
| 120 | + |
| 121 | + describe('onError callback', () => { |
| 122 | + it('fires onError with mapped CONSENT_SYNC_FAILED on consent PUT failure', async () => { |
| 123 | + const queue = createMockQueue(); |
| 124 | + const send = jest.fn<ReturnType<HttpSend>, Parameters<HttpSend>>().mockResolvedValue({ |
| 125 | + ok: false, |
| 126 | + error: { |
| 127 | + status: 503, |
| 128 | + endpoint: 'https://api.dev.immutable.com/v1/audience/tracking-consent', |
| 129 | + body: { code: 'SERVICE_UNAVAILABLE' }, |
| 130 | + }, |
| 131 | + }); |
| 132 | + const onError = jest.fn(); |
| 133 | + const manager = createConsentManager(queue, send, 'pk_test', 'anon-1', 'dev', 'pixel', 'none', onError); |
| 134 | + |
| 135 | + manager.setLevel('anonymous'); |
| 136 | + |
| 137 | + // notifyBackend's .then() runs on the microtask queue. |
| 138 | + await Promise.resolve(); |
| 139 | + await Promise.resolve(); |
| 140 | + |
| 141 | + expect(onError).toHaveBeenCalledTimes(1); |
| 142 | + const err = onError.mock.calls[0][0]; |
| 143 | + expect(err.code).toBe('CONSENT_SYNC_FAILED'); |
| 144 | + expect(err.status).toBe(503); |
| 145 | + expect(err.message).toBe('Consent sync failed with status 503'); |
| 146 | + }); |
| 147 | + |
| 148 | + it('fires onError with NETWORK_ERROR on network failure', async () => { |
| 149 | + const queue = createMockQueue(); |
| 150 | + const send = jest.fn<ReturnType<HttpSend>, Parameters<HttpSend>>().mockResolvedValue({ |
| 151 | + ok: false, |
| 152 | + error: { |
| 153 | + status: 0, |
| 154 | + endpoint: 'https://api.dev.immutable.com/v1/audience/tracking-consent', |
| 155 | + cause: new TypeError('Failed to fetch'), |
| 156 | + }, |
| 157 | + }); |
| 158 | + const onError = jest.fn(); |
| 159 | + const manager = createConsentManager(queue, send, 'pk_test', 'anon-1', 'dev', 'pixel', 'none', onError); |
| 160 | + |
| 161 | + manager.setLevel('anonymous'); |
| 162 | + await Promise.resolve(); |
| 163 | + await Promise.resolve(); |
| 164 | + |
| 165 | + expect(onError).toHaveBeenCalledTimes(1); |
| 166 | + const err = onError.mock.calls[0][0]; |
| 167 | + expect(err.code).toBe('NETWORK_ERROR'); |
| 168 | + expect(err.message).toBe('Network error syncing consent'); |
| 169 | + }); |
| 170 | + |
| 171 | + it('does not fire onError on successful consent sync', async () => { |
| 172 | + const queue = createMockQueue(); |
| 173 | + const send = createMockSend(); |
| 174 | + const onError = jest.fn(); |
| 175 | + const manager = createConsentManager(queue, send, 'pk_test', 'anon-1', 'dev', 'pixel', 'none', onError); |
| 176 | + |
| 177 | + manager.setLevel('anonymous'); |
| 178 | + await Promise.resolve(); |
| 179 | + await Promise.resolve(); |
| 180 | + |
| 181 | + expect(onError).not.toHaveBeenCalled(); |
| 182 | + }); |
| 183 | + |
| 184 | + it('swallows exceptions thrown from the onError callback', async () => { |
| 185 | + const queue = createMockQueue(); |
| 186 | + const send = jest.fn<ReturnType<HttpSend>, Parameters<HttpSend>>().mockResolvedValue({ |
| 187 | + ok: false, |
| 188 | + error: { status: 500, endpoint: 'x', body: null }, |
| 189 | + }); |
| 190 | + const onError = jest.fn().mockImplementation(() => { throw new Error('callback boom'); }); |
| 191 | + const manager = createConsentManager(queue, send, 'pk_test', 'anon-1', 'dev', 'pixel', 'none', onError); |
| 192 | + |
| 193 | + // Synchronous call must not throw even though the .then() handler will. |
| 194 | + expect(() => manager.setLevel('anonymous')).not.toThrow(); |
| 195 | + |
| 196 | + await Promise.resolve(); |
| 197 | + await Promise.resolve(); |
| 198 | + expect(onError).toHaveBeenCalled(); |
| 199 | + }); |
| 200 | + }); |
120 | 201 | }); |
0 commit comments