Aligns the feature detector field name with the downstream ApiActorsSupported flag introduced in the previous commit, so the signal has one consistent name from detection through to consumption. Also consolidates leftover TODO tags (actorIsAssignableCleanup, requestReviewsByLoginCleanup) under the single // TODO ApiActorsSupported tag so there's exactly one thing to grep for. Pure rename with no logic changes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
832 lines
21 KiB
Go
832 lines
21 KiB
Go
package featuredetection
|
|
|
|
import (
|
|
"net/http"
|
|
"testing"
|
|
|
|
"github.com/MakeNowJust/heredoc"
|
|
"github.com/cli/cli/v2/internal/gh"
|
|
"github.com/cli/cli/v2/pkg/httpmock"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestIssueFeatures(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
hostname string
|
|
queryResponse map[string]string
|
|
wantFeatures IssueFeatures
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "github.com",
|
|
hostname: "github.com",
|
|
wantFeatures: IssueFeatures{
|
|
ApiActorsSupported: true,
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ghec data residency (ghe.com)",
|
|
hostname: "stampname.ghe.com",
|
|
wantFeatures: IssueFeatures{
|
|
ApiActorsSupported: true,
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "GHE",
|
|
hostname: "git.my.org",
|
|
wantFeatures: IssueFeatures{
|
|
ApiActorsSupported: false,
|
|
},
|
|
wantErr: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
reg := &httpmock.Registry{}
|
|
defer reg.Verify(t)
|
|
httpClient := &http.Client{}
|
|
httpmock.ReplaceTripper(httpClient, reg)
|
|
for query, resp := range tt.queryResponse {
|
|
reg.Register(httpmock.GraphQL(query), httpmock.StringResponse(resp))
|
|
}
|
|
detector := detector{host: tt.hostname, httpClient: httpClient}
|
|
gotFeatures, err := detector.IssueFeatures()
|
|
if tt.wantErr {
|
|
assert.Error(t, err)
|
|
return
|
|
}
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tt.wantFeatures, gotFeatures)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestPullRequestFeatures(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
hostname string
|
|
queryResponse map[string]string
|
|
wantFeatures PullRequestFeatures
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "github.com with all features",
|
|
hostname: "github.com",
|
|
queryResponse: map[string]string{
|
|
`query PullRequest_fields\b`: heredoc.Doc(`
|
|
{
|
|
"data": {
|
|
"PullRequest": {
|
|
"fields": [
|
|
{"name": "isInMergeQueue"},
|
|
{"name": "isMergeQueueEnabled"}
|
|
]
|
|
},
|
|
"StatusCheckRollupContextConnection": {
|
|
"fields": [
|
|
{"name": "checkRunCount"},
|
|
{"name": "checkRunCountsByState"},
|
|
{"name": "statusContextCount"},
|
|
{"name": "statusContextCountsByState"}
|
|
]
|
|
}
|
|
}
|
|
}`),
|
|
`query PullRequest_fields2\b`: heredoc.Doc(`
|
|
{
|
|
"data": {
|
|
"WorkflowRun": {
|
|
"fields": [
|
|
{"name": "event"}
|
|
]
|
|
}
|
|
}
|
|
}`),
|
|
},
|
|
wantFeatures: PullRequestFeatures{
|
|
MergeQueue: true,
|
|
CheckRunAndStatusContextCounts: true,
|
|
CheckRunEvent: true,
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "github.com with no merge queue",
|
|
hostname: "github.com",
|
|
queryResponse: map[string]string{
|
|
`query PullRequest_fields\b`: heredoc.Doc(`
|
|
{
|
|
"data": {
|
|
"PullRequest": {
|
|
"fields": []
|
|
},
|
|
"StatusCheckRollupContextConnection": {
|
|
"fields": [
|
|
{"name": "checkRunCount"},
|
|
{"name": "checkRunCountsByState"},
|
|
{"name": "statusContextCount"},
|
|
{"name": "statusContextCountsByState"}
|
|
]
|
|
}
|
|
}
|
|
}`),
|
|
`query PullRequest_fields2\b`: heredoc.Doc(`
|
|
{
|
|
"data": {
|
|
"WorkflowRun": {
|
|
"fields": [
|
|
{"name": "event"}
|
|
]
|
|
}
|
|
}
|
|
}`),
|
|
},
|
|
wantFeatures: PullRequestFeatures{
|
|
MergeQueue: false,
|
|
CheckRunAndStatusContextCounts: true,
|
|
CheckRunEvent: true,
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "GHE with all features",
|
|
hostname: "git.my.org",
|
|
queryResponse: map[string]string{
|
|
`query PullRequest_fields\b`: heredoc.Doc(`
|
|
{
|
|
"data": {
|
|
"PullRequest": {
|
|
"fields": [
|
|
{"name": "isInMergeQueue"},
|
|
{"name": "isMergeQueueEnabled"}
|
|
]
|
|
},
|
|
"StatusCheckRollupContextConnection": {
|
|
"fields": [
|
|
{"name": "checkRunCount"},
|
|
{"name": "checkRunCountsByState"},
|
|
{"name": "statusContextCount"},
|
|
{"name": "statusContextCountsByState"}
|
|
]
|
|
}
|
|
}
|
|
}`),
|
|
`query PullRequest_fields2\b`: heredoc.Doc(`
|
|
{
|
|
"data": {
|
|
"WorkflowRun": {
|
|
"fields": [
|
|
{"name": "event"}
|
|
]
|
|
}
|
|
}
|
|
}`),
|
|
},
|
|
wantFeatures: PullRequestFeatures{
|
|
MergeQueue: true,
|
|
CheckRunAndStatusContextCounts: true,
|
|
CheckRunEvent: true,
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "GHE with no features",
|
|
hostname: "git.my.org",
|
|
queryResponse: map[string]string{
|
|
`query PullRequest_fields\b`: heredoc.Doc(`
|
|
{
|
|
"data": {
|
|
"PullRequest": {
|
|
"fields": []
|
|
},
|
|
"StatusCheckRollupContextConnection": {
|
|
"fields": []
|
|
}
|
|
}
|
|
}`),
|
|
`query PullRequest_fields2\b`: heredoc.Doc(`
|
|
{
|
|
"data": {
|
|
"WorkflowRun": {
|
|
"fields": []
|
|
}
|
|
}
|
|
}`),
|
|
},
|
|
wantFeatures: PullRequestFeatures{
|
|
MergeQueue: false,
|
|
CheckRunAndStatusContextCounts: false,
|
|
CheckRunEvent: false,
|
|
},
|
|
wantErr: false,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
reg := &httpmock.Registry{}
|
|
httpClient := &http.Client{}
|
|
httpmock.ReplaceTripper(httpClient, reg)
|
|
for query, resp := range tt.queryResponse {
|
|
reg.Register(httpmock.GraphQL(query), httpmock.StringResponse(resp))
|
|
}
|
|
detector := detector{host: tt.hostname, httpClient: httpClient}
|
|
gotFeatures, err := detector.PullRequestFeatures()
|
|
if tt.wantErr {
|
|
assert.Error(t, err)
|
|
return
|
|
}
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tt.wantFeatures, gotFeatures)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRepositoryFeatures(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
hostname string
|
|
queryResponse map[string]string
|
|
wantFeatures RepositoryFeatures
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "github.com",
|
|
hostname: "github.com",
|
|
wantFeatures: RepositoryFeatures{
|
|
PullRequestTemplateQuery: true,
|
|
VisibilityField: true,
|
|
AutoMerge: true,
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "ghec data residency (ghe.com)",
|
|
hostname: "stampname.ghe.com",
|
|
wantFeatures: RepositoryFeatures{
|
|
PullRequestTemplateQuery: true,
|
|
VisibilityField: true,
|
|
AutoMerge: true,
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "GHE empty response",
|
|
hostname: "git.my.org",
|
|
queryResponse: map[string]string{
|
|
`query Repository_fields\b`: `{"data": {}}`,
|
|
},
|
|
wantFeatures: RepositoryFeatures{
|
|
PullRequestTemplateQuery: false,
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "GHE has pull request template query",
|
|
hostname: "git.my.org",
|
|
queryResponse: map[string]string{
|
|
`query Repository_fields\b`: heredoc.Doc(`
|
|
{ "data": { "Repository": { "fields": [
|
|
{"name": "pullRequestTemplates"}
|
|
] } } }
|
|
`),
|
|
},
|
|
wantFeatures: RepositoryFeatures{
|
|
PullRequestTemplateQuery: true,
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "GHE has visibility field",
|
|
hostname: "git.my.org",
|
|
queryResponse: map[string]string{
|
|
`query Repository_fields\b`: heredoc.Doc(`
|
|
{ "data": { "Repository": { "fields": [
|
|
{"name": "visibility"}
|
|
] } } }
|
|
`),
|
|
},
|
|
wantFeatures: RepositoryFeatures{
|
|
VisibilityField: true,
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "GHE has automerge field",
|
|
hostname: "git.my.org",
|
|
queryResponse: map[string]string{
|
|
`query Repository_fields\b`: heredoc.Doc(`
|
|
{ "data": { "Repository": { "fields": [
|
|
{"name": "autoMergeAllowed"}
|
|
] } } }
|
|
`),
|
|
},
|
|
wantFeatures: RepositoryFeatures{
|
|
AutoMerge: true,
|
|
},
|
|
wantErr: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
reg := &httpmock.Registry{}
|
|
httpClient := &http.Client{}
|
|
httpmock.ReplaceTripper(httpClient, reg)
|
|
for query, resp := range tt.queryResponse {
|
|
reg.Register(httpmock.GraphQL(query), httpmock.StringResponse(resp))
|
|
}
|
|
detector := detector{host: tt.hostname, httpClient: httpClient}
|
|
gotFeatures, err := detector.RepositoryFeatures()
|
|
if tt.wantErr {
|
|
assert.Error(t, err)
|
|
return
|
|
}
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tt.wantFeatures, gotFeatures)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestProjectV1Support(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
hostname string
|
|
httpStubs func(*httpmock.Registry)
|
|
wantFeatures gh.ProjectsV1Support
|
|
}{
|
|
{
|
|
name: "github.com",
|
|
hostname: "github.com",
|
|
wantFeatures: gh.ProjectsV1Unsupported,
|
|
},
|
|
{
|
|
name: "ghec data residency (ghe.com)",
|
|
hostname: "stampname.ghe.com",
|
|
wantFeatures: gh.ProjectsV1Unsupported,
|
|
},
|
|
{
|
|
name: "GHE 3.16.0",
|
|
hostname: "git.my.org",
|
|
httpStubs: func(reg *httpmock.Registry) {
|
|
reg.Register(
|
|
httpmock.REST("GET", "api/v3/meta"),
|
|
httpmock.StringResponse(`{"installed_version":"3.16.0"}`),
|
|
)
|
|
},
|
|
wantFeatures: gh.ProjectsV1Supported,
|
|
},
|
|
{
|
|
name: "GHE 3.16.1",
|
|
hostname: "git.my.org",
|
|
httpStubs: func(reg *httpmock.Registry) {
|
|
reg.Register(
|
|
httpmock.REST("GET", "api/v3/meta"),
|
|
httpmock.StringResponse(`{"installed_version":"3.16.1"}`),
|
|
)
|
|
},
|
|
wantFeatures: gh.ProjectsV1Supported,
|
|
},
|
|
{
|
|
name: "GHE 3.17",
|
|
hostname: "git.my.org",
|
|
httpStubs: func(reg *httpmock.Registry) {
|
|
reg.Register(
|
|
httpmock.REST("GET", "api/v3/meta"),
|
|
httpmock.StringResponse(`{"installed_version":"3.17.0"}`),
|
|
)
|
|
},
|
|
wantFeatures: gh.ProjectsV1Unsupported,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
reg := &httpmock.Registry{}
|
|
if tt.httpStubs != nil {
|
|
tt.httpStubs(reg)
|
|
}
|
|
httpClient := &http.Client{}
|
|
httpmock.ReplaceTripper(httpClient, reg)
|
|
|
|
detector := NewDetector(httpClient, tt.hostname)
|
|
require.Equal(t, tt.wantFeatures, detector.ProjectsV1())
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAdvancedIssueSearchSupport(t *testing.T) {
|
|
withIssueAdvanced := `{"data":{"SearchType":{"enumValues":[{"name":"ISSUE"},{"name":"ISSUE_ADVANCED"},{"name":"REPOSITORY"},{"name":"USER"},{"name":"DISCUSSION"}]}}}`
|
|
withoutIssueAdvanced := `{"data":{"SearchType":{"enumValues":[{"name":"ISSUE"},{"name":"REPOSITORY"},{"name":"USER"},{"name":"DISCUSSION"}]}}}`
|
|
|
|
tests := []struct {
|
|
name string
|
|
hostname string
|
|
httpStubs func(*httpmock.Registry)
|
|
wantFeatures SearchFeatures
|
|
}{
|
|
{
|
|
name: "github.com, before ISSUE_ADVANCED cleanup",
|
|
hostname: "github.com",
|
|
httpStubs: func(reg *httpmock.Registry) {
|
|
reg.Register(
|
|
httpmock.GraphQL(`query SearchType_enumValues\b`),
|
|
httpmock.StringResponse(withIssueAdvanced),
|
|
)
|
|
},
|
|
wantFeatures: advancedIssueSearchSupportedAsOptIn,
|
|
},
|
|
{
|
|
name: "github.com, after ISSUE_ADVANCED cleanup",
|
|
hostname: "github.com",
|
|
httpStubs: func(reg *httpmock.Registry) {
|
|
reg.Register(
|
|
httpmock.GraphQL(`query SearchType_enumValues\b`),
|
|
httpmock.StringResponse(withoutIssueAdvanced),
|
|
)
|
|
},
|
|
wantFeatures: advancedIssueSearchSupportedAsOnlyBackend,
|
|
},
|
|
{
|
|
name: "ghec data residency (ghe.com), before ISSUE_ADVANCED cleanup",
|
|
hostname: "stampname.ghe.com",
|
|
httpStubs: func(reg *httpmock.Registry) {
|
|
reg.Register(
|
|
httpmock.GraphQL(`query SearchType_enumValues\b`),
|
|
httpmock.StringResponse(withIssueAdvanced),
|
|
)
|
|
},
|
|
wantFeatures: advancedIssueSearchSupportedAsOptIn,
|
|
},
|
|
{
|
|
name: "ghec data residency (ghe.com), after ISSUE_ADVANCED cleanup",
|
|
hostname: "stampname.ghe.com",
|
|
httpStubs: func(reg *httpmock.Registry) {
|
|
reg.Register(
|
|
httpmock.GraphQL(`query SearchType_enumValues\b`),
|
|
httpmock.StringResponse(withoutIssueAdvanced),
|
|
)
|
|
},
|
|
wantFeatures: advancedIssueSearchSupportedAsOnlyBackend,
|
|
},
|
|
{
|
|
name: "GHE 3.18, before ISSUE_ADVANCED cleanup",
|
|
hostname: "git.my.org",
|
|
httpStubs: func(reg *httpmock.Registry) {
|
|
reg.Register(
|
|
httpmock.REST("GET", "api/v3/meta"),
|
|
httpmock.StringResponse(`{"installed_version":"3.18.0"}`),
|
|
)
|
|
reg.Register(
|
|
httpmock.GraphQL(`query SearchType_enumValues\b`),
|
|
httpmock.StringResponse(withIssueAdvanced),
|
|
)
|
|
},
|
|
wantFeatures: advancedIssueSearchSupportedAsOptIn,
|
|
},
|
|
{
|
|
name: "GHE 3.18, after ISSUE_ADVANCED cleanup",
|
|
hostname: "git.my.org",
|
|
httpStubs: func(reg *httpmock.Registry) {
|
|
reg.Register(
|
|
httpmock.REST("GET", "api/v3/meta"),
|
|
httpmock.StringResponse(`{"installed_version":"3.18.0"}`),
|
|
)
|
|
reg.Register(
|
|
httpmock.GraphQL(`query SearchType_enumValues\b`),
|
|
httpmock.StringResponse(withoutIssueAdvanced),
|
|
)
|
|
},
|
|
wantFeatures: advancedIssueSearchSupportedAsOnlyBackend,
|
|
},
|
|
{
|
|
name: "GHE >3.18, before ISSUE_ADVANCED cleanup",
|
|
hostname: "git.my.org",
|
|
httpStubs: func(reg *httpmock.Registry) {
|
|
reg.Register(
|
|
httpmock.REST("GET", "api/v3/meta"),
|
|
httpmock.StringResponse(`{"installed_version":"3.18.1"}`),
|
|
)
|
|
reg.Register(
|
|
httpmock.GraphQL(`query SearchType_enumValues\b`),
|
|
httpmock.StringResponse(withIssueAdvanced),
|
|
)
|
|
},
|
|
wantFeatures: advancedIssueSearchSupportedAsOptIn,
|
|
},
|
|
{
|
|
name: "GHE >3.18, after ISSUE_ADVANCED cleanup",
|
|
hostname: "git.my.org",
|
|
httpStubs: func(reg *httpmock.Registry) {
|
|
reg.Register(
|
|
httpmock.REST("GET", "api/v3/meta"),
|
|
httpmock.StringResponse(`{"installed_version":"3.18.1"}`),
|
|
)
|
|
reg.Register(
|
|
httpmock.GraphQL(`query SearchType_enumValues\b`),
|
|
httpmock.StringResponse(withoutIssueAdvanced),
|
|
)
|
|
},
|
|
wantFeatures: advancedIssueSearchSupportedAsOnlyBackend,
|
|
},
|
|
{
|
|
name: "GHE <3.18 (no advanced issue search support)",
|
|
hostname: "git.my.org",
|
|
httpStubs: func(reg *httpmock.Registry) {
|
|
reg.Register(
|
|
httpmock.REST("GET", "api/v3/meta"),
|
|
httpmock.StringResponse(`{"installed_version":"3.17.999"}`),
|
|
)
|
|
},
|
|
wantFeatures: advancedIssueSearchNotSupported,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
reg := &httpmock.Registry{}
|
|
if tt.httpStubs != nil {
|
|
tt.httpStubs(reg)
|
|
}
|
|
httpClient := &http.Client{}
|
|
httpmock.ReplaceTripper(httpClient, reg)
|
|
|
|
detector := NewDetector(httpClient, tt.hostname)
|
|
|
|
features, err := detector.SearchFeatures()
|
|
require.NoError(t, err)
|
|
require.Equal(t, tt.wantFeatures, features)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestProjectFeatures(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
hostname string
|
|
queryResponse map[string]string
|
|
wantFeatures ProjectFeatures
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "github.com",
|
|
hostname: "github.com",
|
|
wantFeatures: ProjectFeatures{
|
|
ProjectItemQuery: true,
|
|
},
|
|
},
|
|
{
|
|
name: "ghec data residency (ghe.com)",
|
|
hostname: "stampname.ghe.com",
|
|
wantFeatures: ProjectFeatures{
|
|
ProjectItemQuery: true,
|
|
},
|
|
},
|
|
{
|
|
name: "GHE empty response",
|
|
hostname: "git.my.org",
|
|
queryResponse: map[string]string{
|
|
`query ProjectV2_fields\b`: `{"data": {}}`,
|
|
},
|
|
wantFeatures: ProjectFeatures{},
|
|
},
|
|
{
|
|
name: "GHE items field without query arg",
|
|
hostname: "git.my.org",
|
|
queryResponse: map[string]string{
|
|
`query ProjectV2_fields\b`: heredoc.Doc(`
|
|
{ "data": { "ProjectV2": { "fields": [
|
|
{"name": "items", "args": [
|
|
{"name": "after"},
|
|
{"name": "first"}
|
|
]}
|
|
] } } }
|
|
`),
|
|
},
|
|
wantFeatures: ProjectFeatures{},
|
|
},
|
|
{
|
|
name: "GHE items field with query arg",
|
|
hostname: "git.my.org",
|
|
queryResponse: map[string]string{
|
|
`query ProjectV2_fields\b`: heredoc.Doc(`
|
|
{ "data": { "ProjectV2": { "fields": [
|
|
{"name": "items", "args": [
|
|
{"name": "after"},
|
|
{"name": "first"},
|
|
{"name": "query"}
|
|
]}
|
|
] } } }
|
|
`),
|
|
},
|
|
wantFeatures: ProjectFeatures{
|
|
ProjectItemQuery: true,
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
reg := &httpmock.Registry{}
|
|
defer reg.Verify(t)
|
|
httpClient := &http.Client{}
|
|
httpmock.ReplaceTripper(httpClient, reg)
|
|
for query, resp := range tt.queryResponse {
|
|
reg.Register(httpmock.GraphQL(query), httpmock.StringResponse(resp))
|
|
}
|
|
detector := detector{host: tt.hostname, httpClient: httpClient}
|
|
gotFeatures, err := detector.ProjectFeatures()
|
|
if tt.wantErr {
|
|
assert.Error(t, err)
|
|
return
|
|
}
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tt.wantFeatures, gotFeatures)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestReleaseFeatures(t *testing.T) {
|
|
withImmutableReleaseSupport := `{"data":{"Release":{"fields":[{"name":"author"},{"name":"name"},{"name":"immutable"}]}}}`
|
|
withoutImmutableReleaseSupport := `{"data":{"Release":{"fields":[{"name":"author"},{"name":"name"}]}}}`
|
|
|
|
tests := []struct {
|
|
name string
|
|
hostname string
|
|
httpStubs func(*httpmock.Registry)
|
|
wantFeatures ReleaseFeatures
|
|
}{
|
|
{
|
|
// This is not a real case as `github.com` supports immutable releases.
|
|
name: "github.com, immutable releases unsupported",
|
|
hostname: "github.com",
|
|
httpStubs: func(reg *httpmock.Registry) {
|
|
reg.Register(
|
|
httpmock.GraphQL(`query Release_fields\b`),
|
|
httpmock.StringResponse(withoutImmutableReleaseSupport),
|
|
)
|
|
},
|
|
wantFeatures: ReleaseFeatures{
|
|
ImmutableReleases: false,
|
|
},
|
|
},
|
|
{
|
|
name: "github.com, immutable releases supported",
|
|
hostname: "github.com",
|
|
httpStubs: func(reg *httpmock.Registry) {
|
|
reg.Register(
|
|
httpmock.GraphQL(`query Release_fields\b`),
|
|
httpmock.StringResponse(withImmutableReleaseSupport),
|
|
)
|
|
},
|
|
wantFeatures: ReleaseFeatures{
|
|
ImmutableReleases: true,
|
|
},
|
|
},
|
|
{
|
|
// This is not a real case as `github.com` supports immutable releases.
|
|
name: "ghec data residency (ghe.com), immutable releases unsupported",
|
|
hostname: "stampname.ghe.com",
|
|
httpStubs: func(reg *httpmock.Registry) {
|
|
reg.Register(
|
|
httpmock.GraphQL(`query Release_fields\b`),
|
|
httpmock.StringResponse(withoutImmutableReleaseSupport),
|
|
)
|
|
},
|
|
wantFeatures: ReleaseFeatures{
|
|
ImmutableReleases: false,
|
|
},
|
|
},
|
|
{
|
|
name: "ghec data residency (ghe.com), immutable releases supported",
|
|
hostname: "stampname.ghe.com",
|
|
httpStubs: func(reg *httpmock.Registry) {
|
|
reg.Register(
|
|
httpmock.GraphQL(`query Release_fields\b`),
|
|
httpmock.StringResponse(withImmutableReleaseSupport),
|
|
)
|
|
},
|
|
wantFeatures: ReleaseFeatures{
|
|
ImmutableReleases: true,
|
|
},
|
|
},
|
|
{
|
|
name: "GHE, immutable releases unsupported",
|
|
hostname: "git.my.org",
|
|
httpStubs: func(reg *httpmock.Registry) {
|
|
reg.Register(
|
|
httpmock.GraphQL(`query Release_fields\b`),
|
|
httpmock.StringResponse(withoutImmutableReleaseSupport),
|
|
)
|
|
},
|
|
wantFeatures: ReleaseFeatures{
|
|
ImmutableReleases: false,
|
|
},
|
|
},
|
|
{
|
|
name: "GHE, immutable releases supported",
|
|
hostname: "git.my.org",
|
|
httpStubs: func(reg *httpmock.Registry) {
|
|
reg.Register(
|
|
httpmock.GraphQL(`query Release_fields\b`),
|
|
httpmock.StringResponse(withImmutableReleaseSupport),
|
|
)
|
|
},
|
|
wantFeatures: ReleaseFeatures{
|
|
ImmutableReleases: true,
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
reg := &httpmock.Registry{}
|
|
if tt.httpStubs != nil {
|
|
tt.httpStubs(reg)
|
|
}
|
|
httpClient := &http.Client{}
|
|
httpmock.ReplaceTripper(httpClient, reg)
|
|
|
|
detector := NewDetector(httpClient, tt.hostname)
|
|
|
|
features, err := detector.ReleaseFeatures()
|
|
require.NoError(t, err)
|
|
require.Equal(t, tt.wantFeatures, features)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestActionsFeatures(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
hostname string
|
|
httpStubs func(*httpmock.Registry)
|
|
wantFeatures ActionsFeatures
|
|
}{
|
|
{
|
|
name: "github.com, workflow dispatch run details supported",
|
|
hostname: "github.com",
|
|
wantFeatures: ActionsFeatures{
|
|
DispatchRunDetails: true,
|
|
},
|
|
},
|
|
{
|
|
name: "ghec data residency (ghe.com), workflow dispatch run details supported",
|
|
hostname: "stampname.ghe.com",
|
|
wantFeatures: ActionsFeatures{
|
|
DispatchRunDetails: true,
|
|
},
|
|
},
|
|
{
|
|
name: "GHE 3.20, workflow dispatch run details not supported",
|
|
hostname: "git.my.org",
|
|
httpStubs: func(reg *httpmock.Registry) {
|
|
reg.Register(
|
|
httpmock.REST("GET", "api/v3/meta"),
|
|
httpmock.StringResponse(`{"installed_version":"3.20.999"}`),
|
|
)
|
|
},
|
|
wantFeatures: ActionsFeatures{
|
|
DispatchRunDetails: false,
|
|
},
|
|
},
|
|
{
|
|
name: "GHE 3.21, workflow dispatch run details supported",
|
|
hostname: "git.my.org",
|
|
httpStubs: func(reg *httpmock.Registry) {
|
|
reg.Register(
|
|
httpmock.REST("GET", "api/v3/meta"),
|
|
httpmock.StringResponse(`{"installed_version":"3.21.0"}`),
|
|
)
|
|
},
|
|
wantFeatures: ActionsFeatures{
|
|
DispatchRunDetails: true,
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
reg := &httpmock.Registry{}
|
|
if tt.httpStubs != nil {
|
|
tt.httpStubs(reg)
|
|
}
|
|
httpClient := &http.Client{}
|
|
httpmock.ReplaceTripper(httpClient, reg)
|
|
|
|
detector := NewDetector(httpClient, tt.hostname)
|
|
|
|
features, err := detector.ActionsFeatures()
|
|
require.NoError(t, err)
|
|
require.Equal(t, tt.wantFeatures, features)
|
|
})
|
|
}
|
|
}
|