Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
4ecbe57
feat: add github_release_asset data source
mdb Dec 14, 2024
1b6916a
chore(config): test previewHeaderInjectorTransport.RoundTrip
mdb Dec 21, 2025
a79408b
chore: github_release_asset data source tests use new test patterns
mdb Jan 8, 2026
bf8808d
chore(data_source_github_release_asset): use if err := ...; err != ni…
mdb Jan 12, 2026
44ea634
chore(data_source_github_release_asset): improve terseness
mdb Jan 12, 2026
144aff8
chore(data_source_github_release_asset_test): inline ComposeTestCheck…
mdb Jan 12, 2026
771aef7
chore(data_source_github_release_asset): source config from acc_test.go
mdb Jan 12, 2026
a663444
chore(data_source_github_release_asset): remove unnecessary TestCase.…
mdb Jan 16, 2026
dd22fca
fix(data_source_github_release_asset): use composite ID
mdb Jan 16, 2026
e454226
fix(data_source_github_release_asset): avoid client mutation
mdb Jan 16, 2026
28896b1
fix(data_source_github_release_asset): properly set updated_at
mdb Jan 16, 2026
058a3a2
feat(data_source_github_release_asset): make download configurable
mdb Jan 16, 2026
b038c0b
chore(data_source_github_release_asset): use `download_file`/`file` a…
mdb Jan 21, 2026
aeabad4
fix(data_source_github_release_asset): base64 encode `file`
mdb Jan 21, 2026
9e5b635
chore(data_source_github_release_asset): adjust file contents attributes
mdb Jan 22, 2026
6b6c797
chore(data_source_github_release_asset): improve acc test
mdb Jan 22, 2026
57edee5
chore(data_source_github_release_asset_test): appease linting
mdb Jan 27, 2026
e0eb14b
chore(config_test): appease lint errors
mdb Jan 27, 2026
502932e
chore(config): address staticcheck errors
mdb Jan 27, 2026
19bf183
fix(config_test): use canonical HTTP header names in expectedHeaders
mdb Jan 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions github/acc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ type testAccConfig struct {
testPublicRepository string
testPublicRepositoryOwner string
testPublicReleaseId int
testPublicRelaseAssetId string
testPublicRelaseAssetName string
testPublicReleaseAssetContent string
testPublicTemplateRepository string
testPublicTemplateRepositoryOwner string
testGHActionsAppInstallationId int
Expand Down Expand Up @@ -105,11 +108,16 @@ func TestMain(m *testing.M) {
}

config := testAccConfig{
baseURL: baseURL,
authMode: authMode,
testPublicRepository: "terraform-provider-github",
testPublicRepositoryOwner: "integrations",
testPublicReleaseId: 186531906,
baseURL: baseURL,
authMode: authMode,
testPublicRepository: "terraform-provider-github",
testPublicRepositoryOwner: "integrations",
testPublicReleaseId: 186531906,
// The terraform-provider-github_6.4.0_manifest.json asset ID from
// https://github.com/integrations/terraform-provider-github/releases/tag/v6.4.0
testPublicRelaseAssetId: "207956097",
testPublicRelaseAssetName: "terraform-provider-github_6.4.0_manifest.json",
testPublicReleaseAssetContent: "{\n \"version\": 1,\n \"metadata\": {\n \"protocol_versions\": [\n \"5.0\"\n ]\n }\n}",
testPublicTemplateRepository: "template-repository",
testPublicTemplateRepositoryOwner: "template-repository",
testGHActionsAppInstallationId: 15368,
Expand Down
6 changes: 5 additions & 1 deletion github/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,11 @@ func (injector *previewHeaderInjectorTransport) RoundTrip(req *http.Request) (*h
header := req.Header.Get(name)
if header == "" {
header = value
} else {
// NOTE: Some API endpoints expect a single Accept: application/octet-stream header.
// If one has been set, it's necessary to preserve it as-is, without
// appending previewHeaders value.
// See https://github.com/google/go-github/pull/3392
} else if strings.ToLower(name) != "accept" || header != "application/octet-stream" {
header = strings.Join([]string{header, value}, ",")
}
req.Header.Set(name, header)
Expand Down
214 changes: 214 additions & 0 deletions github/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package github

import (
"context"
"net/http"
"net/http/httptest"
"testing"

"github.com/shurcooL/githubv4"
Expand Down Expand Up @@ -300,3 +302,215 @@ func TestAccConfigMeta(t *testing.T) {
}
})
}

func TestPreviewHeaderInjectorTransport_RoundTrip(t *testing.T) {
tests := []struct {
name string
previewHeaders map[string]string
existingHeaders map[string]string
expectedHeaders map[string]string
expectRoundTripCall bool
}{
{
name: "empty preview headers",
previewHeaders: map[string]string{},
existingHeaders: map[string]string{"User-Agent": "test"},
expectedHeaders: map[string]string{"User-Agent": "test"},
expectRoundTripCall: true,
},
{
name: "add new preview header",
previewHeaders: map[string]string{
"Accept": "application/vnd.github.v3+json",
},
existingHeaders: map[string]string{},
expectedHeaders: map[string]string{
"Accept": "application/vnd.github.v3+json",
},
expectRoundTripCall: true,
},
{
name: "append to existing header",
previewHeaders: map[string]string{
"Accept": "application/vnd.github.preview+json",
},
existingHeaders: map[string]string{
"Accept": "application/json",
},
expectedHeaders: map[string]string{
"Accept": "application/json,application/vnd.github.preview+json",
},
expectRoundTripCall: true,
},
{
name: "preserve existing Accept application/octet-stream",
previewHeaders: map[string]string{
"Accept": "application/vnd.github.preview+json",
},
existingHeaders: map[string]string{
"Accept": "application/octet-stream",
},
expectedHeaders: map[string]string{
"Accept": "application/octet-stream",
},
expectRoundTripCall: true,
},
{
name: "preserve existing accept application/octet-stream (lowercase)",
previewHeaders: map[string]string{
"accept": "application/vnd.github.preview+json",
},
existingHeaders: map[string]string{
"accept": "application/octet-stream",
},
expectedHeaders: map[string]string{
"Accept": "application/octet-stream",
},
expectRoundTripCall: true,
},
{
name: "preserve existing Accept application/octet-stream (mixed case)",
previewHeaders: map[string]string{
"AcCePt": "application/vnd.github.preview+json",
},
existingHeaders: map[string]string{
"Accept": "application/octet-stream",
},
expectedHeaders: map[string]string{
"Accept": "application/octet-stream",
},
expectRoundTripCall: true,
},
{
name: "multiple preview headers",
previewHeaders: map[string]string{
"Accept": "application/vnd.github.v3+json",
"X-GitHub-Api-Version": "2022-11-28",
},
existingHeaders: map[string]string{},
expectedHeaders: map[string]string{
"Accept": "application/vnd.github.v3+json",
"X-Github-Api-Version": "2022-11-28",
},
expectRoundTripCall: true,
},
{
name: "append multiple preview headers to existing",
previewHeaders: map[string]string{
"Accept": "application/vnd.github.v3+json",
"X-GitHub-Api-Version": "2022-11-28",
},
existingHeaders: map[string]string{
"Accept": "application/json",
"X-GitHub-Api-Version": "2021-01-01",
},
expectedHeaders: map[string]string{
"Accept": "application/json,application/vnd.github.v3+json",
"X-Github-Api-Version": "2021-01-01,2022-11-28",
},
expectRoundTripCall: true,
},
{
name: "non-accept headers always append",
previewHeaders: map[string]string{
"X-Custom-Header": "preview-value",
},
existingHeaders: map[string]string{
"X-Custom-Header": "application/octet-stream",
},
expectedHeaders: map[string]string{
"X-Custom-Header": "application/octet-stream,preview-value",
},
expectRoundTripCall: true,
},
{
name: "accept header with different value appends",
previewHeaders: map[string]string{
"Accept": "application/vnd.github.preview+json",
},
existingHeaders: map[string]string{
"Accept": "application/json",
},
expectedHeaders: map[string]string{
"Accept": "application/json,application/vnd.github.preview+json",
},
expectRoundTripCall: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Create a mock RoundTripper that records the request
var capturedRequest *http.Request
mockRT := &mockRoundTripper{
roundTripFunc: func(req *http.Request) (*http.Response, error) {
capturedRequest = req
return &http.Response{
StatusCode: http.StatusOK,
Body: http.NoBody,
}, nil
},
}

injector := &previewHeaderInjectorTransport{
rt: mockRT,
previewHeaders: tt.previewHeaders,
}

// Create a test request with existing headers
req := httptest.NewRequest(http.MethodGet, "https://api.github.com/test", nil)
for name, value := range tt.existingHeaders {
req.Header.Set(name, value)
}

// Execute RoundTrip
resp, err := injector.RoundTrip(req)
// Verify no error
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

// Verify response
if resp == nil {
t.Fatal("expected non-nil response")
}

// Verify RoundTrip was called on the underlying transport
if tt.expectRoundTripCall && capturedRequest == nil {
t.Fatal("expected RoundTrip to be called on underlying transport")
}

// Verify headers in the captured request
if capturedRequest != nil {
for name, expectedValue := range tt.expectedHeaders {
actualValue := capturedRequest.Header.Get(name)
if actualValue != expectedValue {
t.Errorf("header %q: expected %q, got %q", name, expectedValue, actualValue)
}
}

// Verify no unexpected headers were added
for name := range capturedRequest.Header {
if _, exists := tt.expectedHeaders[name]; !exists {
// Allow headers that were in existingHeaders but not in expectedHeaders
if _, wasExisting := tt.existingHeaders[name]; !wasExisting {
t.Errorf("unexpected header %q: %q", name, capturedRequest.Header.Get(name))
}
}
}
}
})
}
}

// mockRoundTripper is a mock implementation of http.RoundTripper for testing.
type mockRoundTripper struct {
roundTripFunc func(*http.Request) (*http.Response, error)
}

func (m *mockRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
if m.roundTripFunc != nil {
return m.roundTripFunc(req)
}
return &http.Response{StatusCode: http.StatusOK, Body: http.NoBody}, nil
}
Loading