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