@@ -3,12 +3,13 @@ import {
33 GitDirectoryResource ,
44 BundledResource ,
55} from './resources' ;
6- import { expect , describe , it , vi , beforeEach } from 'vitest' ;
6+ import { expect , describe , it , vi , beforeEach , afterEach } from 'vitest' ;
77import { StreamedFile } from '@php-wasm/stream-compression' ;
88import { mkdtemp , rm , writeFile , mkdir } from 'fs/promises' ;
99import { tmpdir } from 'os' ;
1010import { join } from 'path' ;
1111import { execSync , type ExecSyncOptions } from 'child_process' ;
12+ import { GitAuthenticationError } from '@wp-playground/storage' ;
1213
1314describe ( 'UrlResource' , ( ) => {
1415 it ( 'should create a new instance of UrlResource' , ( ) => {
@@ -232,6 +233,95 @@ describe('GitDirectoryResource', () => {
232233 expect ( name ) . toBe ( 'https-github.com-WordPress-link-manager-trunk' ) ;
233234 } ) ;
234235 } ) ;
236+
237+ describe ( 'authentication error handling' , ( ) => {
238+ let originalFetch : typeof global . fetch ;
239+
240+ beforeEach ( ( ) => {
241+ originalFetch = global . fetch ;
242+ } ) ;
243+
244+ afterEach ( ( ) => {
245+ global . fetch = originalFetch ;
246+ } ) ;
247+
248+ it ( 'should unwrap CORS URL in GitAuthenticationError' , async ( ) => {
249+ global . fetch = vi . fn ( ) . mockResolvedValue ( {
250+ ok : false ,
251+ status : 401 ,
252+ statusText : 'Unauthorized' ,
253+ } ) ;
254+
255+ const githubUrl = 'https://github.com/user/private-repo' ;
256+ const resource = new GitDirectoryResource (
257+ {
258+ resource : 'git:directory' ,
259+ url : githubUrl ,
260+ ref : 'main' ,
261+ } ,
262+ undefined ,
263+ {
264+ corsProxy : 'https://cors-proxy.com/' ,
265+ }
266+ ) ;
267+
268+ await expect ( resource . resolve ( ) ) . rejects . toMatchObject ( {
269+ name : 'GitAuthenticationError' ,
270+ repoUrl : githubUrl ,
271+ status : 401 ,
272+ } ) ;
273+ } ) ;
274+
275+ it ( 'should preserve GitHub URL in GitAuthenticationError without CORS proxy' , async ( ) => {
276+ global . fetch = vi . fn ( ) . mockResolvedValue ( {
277+ ok : false ,
278+ status : 401 ,
279+ statusText : 'Unauthorized' ,
280+ } ) ;
281+
282+ const githubUrl = 'https://github.com/user/private-repo' ;
283+ const resource = new GitDirectoryResource ( {
284+ resource : 'git:directory' ,
285+ url : githubUrl ,
286+ ref : 'main' ,
287+ } ) ;
288+
289+ await expect ( resource . resolve ( ) ) . rejects . toMatchObject ( {
290+ name : 'GitAuthenticationError' ,
291+ repoUrl : githubUrl ,
292+ status : 401 ,
293+ } ) ;
294+ } ) ;
295+
296+ it ( 'should call gitAdditionalHeadersCallback without CORS proxy' , async ( ) => {
297+ const githubUrl = 'https://github.com/user/private-repo' ;
298+ const headerCallback = vi . fn ( ) . mockReturnValue ( {
299+ Authorization : 'Bearer test-token' ,
300+ } ) ;
301+
302+ const resource = new GitDirectoryResource (
303+ {
304+ resource : 'git:directory' ,
305+ url : githubUrl ,
306+ ref : 'main' ,
307+ } ,
308+ undefined ,
309+ {
310+ additionalHeaders : headerCallback ,
311+ }
312+ ) ;
313+
314+ // Call resolve - it will fail but that's okay, we just want to verify the callback
315+ try {
316+ await resource . resolve ( ) ;
317+ } catch {
318+ // Expected to fail - we're not mocking the entire git resolution
319+ }
320+
321+ // Verify the callback was called with the GitHub URL (not CORS-wrapped)
322+ expect ( headerCallback ) . toHaveBeenCalledWith ( githubUrl ) ;
323+ } ) ;
324+ } ) ;
235325} ) ;
236326
237327describe ( 'BlueprintResource' , ( ) => {
0 commit comments