@@ -2,6 +2,8 @@ package github
22
33import (
44 "context"
5+ "net/http"
6+ "net/http/httptest"
57 "testing"
68
79 "github.com/shurcooL/githubv4"
@@ -295,3 +297,216 @@ func TestAccConfigMeta(t *testing.T) {
295297 }
296298 })
297299}
300+
301+ func TestPreviewHeaderInjectorTransport_RoundTrip (t * testing.T ) {
302+ tests := []struct {
303+ name string
304+ previewHeaders map [string ]string
305+ existingHeaders map [string ]string
306+ expectedHeaders map [string ]string
307+ expectRoundTripCall bool
308+ }{
309+ {
310+ name : "empty preview headers" ,
311+ previewHeaders : map [string ]string {},
312+ existingHeaders : map [string ]string {"User-Agent" : "test" },
313+ expectedHeaders : map [string ]string {"User-Agent" : "test" },
314+ expectRoundTripCall : true ,
315+ },
316+ {
317+ name : "add new preview header" ,
318+ previewHeaders : map [string ]string {
319+ "Accept" : "application/vnd.github.v3+json" ,
320+ },
321+ existingHeaders : map [string ]string {},
322+ expectedHeaders : map [string ]string {
323+ "Accept" : "application/vnd.github.v3+json" ,
324+ },
325+ expectRoundTripCall : true ,
326+ },
327+ {
328+ name : "append to existing header" ,
329+ previewHeaders : map [string ]string {
330+ "Accept" : "application/vnd.github.preview+json" ,
331+ },
332+ existingHeaders : map [string ]string {
333+ "Accept" : "application/json" ,
334+ },
335+ expectedHeaders : map [string ]string {
336+ "Accept" : "application/json,application/vnd.github.preview+json" ,
337+ },
338+ expectRoundTripCall : true ,
339+ },
340+ {
341+ name : "preserve existing Accept application/octet-stream" ,
342+ previewHeaders : map [string ]string {
343+ "Accept" : "application/vnd.github.preview+json" ,
344+ },
345+ existingHeaders : map [string ]string {
346+ "Accept" : "application/octet-stream" ,
347+ },
348+ expectedHeaders : map [string ]string {
349+ "Accept" : "application/octet-stream" ,
350+ },
351+ expectRoundTripCall : true ,
352+ },
353+ {
354+ name : "preserve existing accept application/octet-stream (lowercase)" ,
355+ previewHeaders : map [string ]string {
356+ "accept" : "application/vnd.github.preview+json" ,
357+ },
358+ existingHeaders : map [string ]string {
359+ "accept" : "application/octet-stream" ,
360+ },
361+ expectedHeaders : map [string ]string {
362+ "Accept" : "application/octet-stream" ,
363+ },
364+ expectRoundTripCall : true ,
365+ },
366+ {
367+ name : "preserve existing Accept application/octet-stream (mixed case)" ,
368+ previewHeaders : map [string ]string {
369+ "AcCePt" : "application/vnd.github.preview+json" ,
370+ },
371+ existingHeaders : map [string ]string {
372+ "Accept" : "application/octet-stream" ,
373+ },
374+ expectedHeaders : map [string ]string {
375+ "Accept" : "application/octet-stream" ,
376+ },
377+ expectRoundTripCall : true ,
378+ },
379+ {
380+ name : "multiple preview headers" ,
381+ previewHeaders : map [string ]string {
382+ "Accept" : "application/vnd.github.v3+json" ,
383+ "X-GitHub-Api-Version" : "2022-11-28" ,
384+ },
385+ existingHeaders : map [string ]string {},
386+ expectedHeaders : map [string ]string {
387+ "Accept" : "application/vnd.github.v3+json" ,
388+ "X-GitHub-Api-Version" : "2022-11-28" ,
389+ },
390+ expectRoundTripCall : true ,
391+ },
392+ {
393+ name : "append multiple preview headers to existing" ,
394+ previewHeaders : map [string ]string {
395+ "Accept" : "application/vnd.github.v3+json" ,
396+ "X-GitHub-Api-Version" : "2022-11-28" ,
397+ },
398+ existingHeaders : map [string ]string {
399+ "Accept" : "application/json" ,
400+ "X-GitHub-Api-Version" : "2021-01-01" ,
401+ },
402+ expectedHeaders : map [string ]string {
403+ "Accept" : "application/json,application/vnd.github.v3+json" ,
404+ "X-GitHub-Api-Version" : "2021-01-01,2022-11-28" ,
405+ },
406+ expectRoundTripCall : true ,
407+ },
408+ {
409+ name : "non-accept headers always append" ,
410+ previewHeaders : map [string ]string {
411+ "X-Custom-Header" : "preview-value" ,
412+ },
413+ existingHeaders : map [string ]string {
414+ "X-Custom-Header" : "application/octet-stream" ,
415+ },
416+ expectedHeaders : map [string ]string {
417+ "X-Custom-Header" : "application/octet-stream,preview-value" ,
418+ },
419+ expectRoundTripCall : true ,
420+ },
421+ {
422+ name : "accept header with different value appends" ,
423+ previewHeaders : map [string ]string {
424+ "Accept" : "application/vnd.github.preview+json" ,
425+ },
426+ existingHeaders : map [string ]string {
427+ "Accept" : "application/json" ,
428+ },
429+ expectedHeaders : map [string ]string {
430+ "Accept" : "application/json,application/vnd.github.preview+json" ,
431+ },
432+ expectRoundTripCall : true ,
433+ },
434+ }
435+
436+ for _ , tt := range tests {
437+ t .Run (tt .name , func (t * testing.T ) {
438+ // Create a mock RoundTripper that records the request
439+ var capturedRequest * http.Request
440+ mockRT := & mockRoundTripper {
441+ roundTripFunc : func (req * http.Request ) (* http.Response , error ) {
442+ capturedRequest = req
443+ return & http.Response {
444+ StatusCode : http .StatusOK ,
445+ Body : http .NoBody ,
446+ }, nil
447+ },
448+ }
449+
450+ injector := & previewHeaderInjectorTransport {
451+ rt : mockRT ,
452+ previewHeaders : tt .previewHeaders ,
453+ }
454+
455+ // Create a test request with existing headers
456+ req := httptest .NewRequest (http .MethodGet , "https://api.github.com/test" , nil )
457+ for name , value := range tt .existingHeaders {
458+ req .Header .Set (name , value )
459+ }
460+
461+ // Execute RoundTrip
462+ resp , err := injector .RoundTrip (req )
463+
464+ // Verify no error
465+ if err != nil {
466+ t .Fatalf ("unexpected error: %v" , err )
467+ }
468+
469+ // Verify response
470+ if resp == nil {
471+ t .Fatal ("expected non-nil response" )
472+ }
473+
474+ // Verify RoundTrip was called on the underlying transport
475+ if tt .expectRoundTripCall && capturedRequest == nil {
476+ t .Fatal ("expected RoundTrip to be called on underlying transport" )
477+ }
478+
479+ // Verify headers in the captured request
480+ if capturedRequest != nil {
481+ for name , expectedValue := range tt .expectedHeaders {
482+ actualValue := capturedRequest .Header .Get (name )
483+ if actualValue != expectedValue {
484+ t .Errorf ("header %q: expected %q, got %q" , name , expectedValue , actualValue )
485+ }
486+ }
487+
488+ // Verify no unexpected headers were added
489+ for name := range capturedRequest .Header {
490+ if _ , exists := tt .expectedHeaders [name ]; ! exists {
491+ // Allow headers that were in existingHeaders but not in expectedHeaders
492+ if _ , wasExisting := tt .existingHeaders [name ]; ! wasExisting {
493+ t .Errorf ("unexpected header %q: %q" , name , capturedRequest .Header .Get (name ))
494+ }
495+ }
496+ }
497+ }
498+ })
499+ }
500+ }
501+
502+ // mockRoundTripper is a mock implementation of http.RoundTripper for testing
503+ type mockRoundTripper struct {
504+ roundTripFunc func (* http.Request ) (* http.Response , error )
505+ }
506+
507+ func (m * mockRoundTripper ) RoundTrip (req * http.Request ) (* http.Response , error ) {
508+ if m .roundTripFunc != nil {
509+ return m .roundTripFunc (req )
510+ }
511+ return & http.Response {StatusCode : http .StatusOK , Body : http .NoBody }, nil
512+ }
0 commit comments