|
1 | 1 | /** @vitest-environment node */ |
2 | 2 | import { beforeEach, describe, expect, it, vi } from 'vitest' |
| 3 | +import { PayloadSizeLimitError } from '@/lib/core/utils/stream-limits' |
3 | 4 |
|
4 | 5 | const { mockSecureFetch, mockValidateUrl } = vi.hoisted(() => ({ |
5 | 6 | mockSecureFetch: vi.fn(), |
@@ -93,7 +94,7 @@ describe('Oracle EPM guarded client', () => { |
93 | 94 | { logDetails: false } |
94 | 95 | ) |
95 | 96 | expect(mockSecureFetch).toHaveBeenCalledWith( |
96 | | - expect.any(String), |
| 97 | + 'https://epm.example.com/gateway/acme/SyntheticAlpha/rest/v3/jobs/job%20with%20spaces?limit=25', |
97 | 98 | '203.0.113.10', |
98 | 99 | expect.objectContaining({ |
99 | 100 | method: 'GET', |
@@ -146,6 +147,50 @@ describe('Oracle EPM guarded client', () => { |
146 | 147 | expect(mockValidateUrl).not.toHaveBeenCalled() |
147 | 148 | }) |
148 | 149 |
|
| 150 | + it('preserves valid surrogate pairs in encoded path and query parameters', async () => { |
| 151 | + const endpoint = routes.defineEndpoint({ |
| 152 | + method: 'GET', |
| 153 | + version: 'v3', |
| 154 | + path: [oracleEpmLiteral('files'), oracleEpmPathParameter('fileId', { maxBytes: 32 })], |
| 155 | + query: { label: oracleEpmQuery.string({ maxBytes: 32 }) }, |
| 156 | + body: 'none', |
| 157 | + response: 'json', |
| 158 | + timeoutMs: 2_000, |
| 159 | + maxResponseBytes: 1_024, |
| 160 | + }) |
| 161 | + const client = createOracleEpmClient({ |
| 162 | + instanceUrl: 'https://epm.example.com', |
| 163 | + accessToken: Buffer.from('u:p').toString('base64'), |
| 164 | + }) |
| 165 | + await client.request(endpoint, { |
| 166 | + pathParams: { fileId: 'report-😀' }, |
| 167 | + query: { label: 'locked-🔒' }, |
| 168 | + }) |
| 169 | + expect(mockSecureFetch.mock.calls[0][0]).toBe( |
| 170 | + 'https://epm.example.com/SyntheticAlpha/rest/v3/files/report-%F0%9F%98%80?label=locked-%F0%9F%94%92' |
| 171 | + ) |
| 172 | + }) |
| 173 | + |
| 174 | + it('preserves streamed response size failures as payload-too-large errors', async () => { |
| 175 | + mockSecureFetch.mockResolvedValue({ |
| 176 | + ...secureResponse({}), |
| 177 | + json: vi.fn().mockRejectedValue( |
| 178 | + new PayloadSizeLimitError({ |
| 179 | + label: 'secure fetch response', |
| 180 | + maxBytes: 4_096, |
| 181 | + observedBytes: 4_097, |
| 182 | + }) |
| 183 | + ), |
| 184 | + }) |
| 185 | + const client = createOracleEpmClient({ |
| 186 | + instanceUrl: 'https://epm.example.com', |
| 187 | + accessToken: Buffer.from('u:p').toString('base64'), |
| 188 | + }) |
| 189 | + await expect(client.request(getJob, { pathParams: { jobId: '42' } })).rejects.toMatchObject({ |
| 190 | + category: 'payload_too_large', |
| 191 | + }) |
| 192 | + }) |
| 193 | + |
149 | 194 | it('suppresses arbitrary provider bodies in failed requests', async () => { |
150 | 195 | mockSecureFetch.mockResolvedValue( |
151 | 196 | secureResponse({ |
@@ -310,6 +355,8 @@ describe('Oracle EPM guarded client', () => { |
310 | 355 | 'https://epm.example.com/gateway/SyntheticAlpha/rest/v3/files/abc?token=x&token=y', |
311 | 356 | 'https://epm.example.com/gateway/SyntheticAlpha/rest/v3/files/abc?unknown=x', |
312 | 357 | 'https://epm.example.com/gateway/SyntheticAlpha/rest/v3/files/abc?token=x#fragment', |
| 358 | + 'https://epm.example.com/gateway/SyntheticAlpha/rest/v3/files/ab\nc?token=x', |
| 359 | + 'https://epm.example.com/gateway/SyntheticAlpha/rest/v3/files/\uD800?token=x', |
313 | 360 | ])('rejects unsafe returned link %j', (href) => { |
314 | 361 | const policy = routes.defineReturnedLinkPolicy({ |
315 | 362 | relation: 'download', |
|
0 commit comments