This change updates `gh variable delete` and `gh secret delete` to use the host associated with the repository rather than the default host. Along with these changes is minor refactoring of the tests to ensure appropriate Dotcom vs GHES targeting works as expected. Minor edit to `gh variable get` testing to simplify some conditional logic in test setup.
318 lines
8.5 KiB
Go
318 lines
8.5 KiB
Go
package get
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"net/http"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/cli/cli/v2/internal/config"
|
|
"github.com/cli/cli/v2/internal/gh"
|
|
"github.com/cli/cli/v2/internal/ghrepo"
|
|
"github.com/cli/cli/v2/pkg/cmd/variable/shared"
|
|
"github.com/cli/cli/v2/pkg/cmdutil"
|
|
"github.com/cli/cli/v2/pkg/httpmock"
|
|
"github.com/cli/cli/v2/pkg/iostreams"
|
|
"github.com/google/shlex"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestNewCmdGet(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
cli string
|
|
wants GetOptions
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "repo",
|
|
cli: "FOO",
|
|
wants: GetOptions{
|
|
OrgName: "",
|
|
VariableName: "FOO",
|
|
},
|
|
},
|
|
{
|
|
name: "org",
|
|
cli: "-o TestOrg BAR",
|
|
wants: GetOptions{
|
|
OrgName: "TestOrg",
|
|
VariableName: "BAR",
|
|
},
|
|
},
|
|
{
|
|
name: "env",
|
|
cli: "-e Development BAZ",
|
|
wants: GetOptions{
|
|
EnvName: "Development",
|
|
VariableName: "BAZ",
|
|
},
|
|
},
|
|
{
|
|
name: "org and env",
|
|
cli: "-o TestOrg -e Development QUX",
|
|
wantErr: cmdutil.FlagErrorf("%s", "specify only one of `--org` or `--env`"),
|
|
},
|
|
{
|
|
name: "json",
|
|
cli: "--json name,value FOO",
|
|
wants: GetOptions{
|
|
VariableName: "FOO",
|
|
Exporter: func() cmdutil.Exporter {
|
|
exporter := cmdutil.NewJSONExporter()
|
|
exporter.SetFields([]string{"name", "value"})
|
|
return exporter
|
|
}(),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
ios, _, _, _ := iostreams.Test()
|
|
f := &cmdutil.Factory{
|
|
IOStreams: ios,
|
|
}
|
|
|
|
argv, err := shlex.Split(tt.cli)
|
|
assert.NoError(t, err)
|
|
|
|
var gotOpts *GetOptions
|
|
cmd := NewCmdGet(f, func(opts *GetOptions) error {
|
|
gotOpts = opts
|
|
return nil
|
|
})
|
|
cmd.SetArgs(argv)
|
|
cmd.SetIn(&bytes.Buffer{})
|
|
cmd.SetOut(&bytes.Buffer{})
|
|
cmd.SetErr(&bytes.Buffer{})
|
|
|
|
_, err = cmd.ExecuteC()
|
|
if tt.wantErr != nil {
|
|
require.Equal(t, err, tt.wantErr)
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, tt.wants.OrgName, gotOpts.OrgName)
|
|
require.Equal(t, tt.wants.EnvName, gotOpts.EnvName)
|
|
require.Equal(t, tt.wants.VariableName, gotOpts.VariableName)
|
|
require.Equal(t, tt.wants.Exporter, gotOpts.Exporter)
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_getRun(t *testing.T) {
|
|
tf, _ := time.Parse(time.RFC3339, "2024-01-01T00:00:00Z")
|
|
|
|
tests := []struct {
|
|
name string
|
|
opts *GetOptions
|
|
host string
|
|
httpStubs func(*httpmock.Registry)
|
|
jsonFields []string
|
|
wantOut string
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "getting repo variable",
|
|
opts: &GetOptions{
|
|
VariableName: "VARIABLE_ONE",
|
|
},
|
|
host: "github.com",
|
|
httpStubs: func(reg *httpmock.Registry) {
|
|
reg.Register(httpmock.WithHost(httpmock.REST("GET", "repos/owner/repo/actions/variables/VARIABLE_ONE"), "api.github.com"),
|
|
httpmock.JSONResponse(shared.Variable{
|
|
Value: "repo_var",
|
|
}))
|
|
},
|
|
wantOut: "repo_var\n",
|
|
},
|
|
{
|
|
name: "getting GHES repo variable",
|
|
opts: &GetOptions{
|
|
VariableName: "VARIABLE_ONE",
|
|
},
|
|
host: "example.com",
|
|
httpStubs: func(reg *httpmock.Registry) {
|
|
reg.Register(httpmock.WithHost(httpmock.REST("GET", "api/v3/repos/owner/repo/actions/variables/VARIABLE_ONE"), "example.com"),
|
|
httpmock.JSONResponse(shared.Variable{
|
|
Value: "repo_var",
|
|
}))
|
|
},
|
|
wantOut: "repo_var\n",
|
|
},
|
|
{
|
|
name: "getting org variable",
|
|
opts: &GetOptions{
|
|
OrgName: "TestOrg",
|
|
VariableName: "VARIABLE_ONE",
|
|
},
|
|
host: "github.com",
|
|
httpStubs: func(reg *httpmock.Registry) {
|
|
reg.Register(httpmock.REST("GET", "orgs/TestOrg/actions/variables/VARIABLE_ONE"),
|
|
httpmock.JSONResponse(shared.Variable{
|
|
Value: "org_var",
|
|
}))
|
|
},
|
|
wantOut: "org_var\n",
|
|
},
|
|
{
|
|
name: "getting env variable",
|
|
opts: &GetOptions{
|
|
EnvName: "Development",
|
|
VariableName: "VARIABLE_ONE",
|
|
},
|
|
host: "github.com",
|
|
httpStubs: func(reg *httpmock.Registry) {
|
|
reg.Register(httpmock.WithHost(httpmock.REST("GET", "repos/owner/repo/environments/Development/variables/VARIABLE_ONE"), "api.github.com"),
|
|
httpmock.JSONResponse(shared.Variable{
|
|
Value: "env_var",
|
|
}))
|
|
},
|
|
wantOut: "env_var\n",
|
|
},
|
|
{
|
|
name: "getting GHES env variable",
|
|
opts: &GetOptions{
|
|
EnvName: "Development",
|
|
VariableName: "VARIABLE_ONE",
|
|
},
|
|
host: "example.com",
|
|
httpStubs: func(reg *httpmock.Registry) {
|
|
reg.Register(httpmock.WithHost(httpmock.REST("GET", "api/v3/repos/owner/repo/environments/Development/variables/VARIABLE_ONE"), "example.com"),
|
|
httpmock.JSONResponse(shared.Variable{
|
|
Value: "env_var",
|
|
}))
|
|
},
|
|
wantOut: "env_var\n",
|
|
},
|
|
{
|
|
name: "json",
|
|
opts: &GetOptions{
|
|
VariableName: "VARIABLE_ONE",
|
|
},
|
|
host: "github.com",
|
|
jsonFields: []string{"name", "value", "visibility", "updatedAt", "createdAt", "numSelectedRepos", "selectedReposURL"},
|
|
httpStubs: func(reg *httpmock.Registry) {
|
|
reg.Register(httpmock.REST("GET", "repos/owner/repo/actions/variables/VARIABLE_ONE"),
|
|
httpmock.JSONResponse(shared.Variable{
|
|
Name: "VARIABLE_ONE",
|
|
Value: "repo_var",
|
|
UpdatedAt: tf,
|
|
CreatedAt: tf,
|
|
Visibility: shared.Organization,
|
|
SelectedReposURL: "path/to/fetch/selected/repos",
|
|
NumSelectedRepos: 0, // This should be populated in a second API call.
|
|
}))
|
|
reg.Register(httpmock.REST("GET", "path/to/fetch/selected/repos"),
|
|
httpmock.JSONResponse(map[string]interface{}{
|
|
"total_count": 99,
|
|
}))
|
|
},
|
|
wantOut: `{
|
|
"name": "VARIABLE_ONE",
|
|
"value": "repo_var",
|
|
"updatedAt": "2024-01-01T00:00:00Z",
|
|
"createdAt": "2024-01-01T00:00:00Z",
|
|
"visibility": "organization",
|
|
"selectedReposURL": "path/to/fetch/selected/repos",
|
|
"numSelectedRepos": 99
|
|
}`,
|
|
},
|
|
{
|
|
name: "when the variable is not found, an error is returned",
|
|
opts: &GetOptions{
|
|
VariableName: "VARIABLE_ONE",
|
|
},
|
|
host: "github.com",
|
|
httpStubs: func(reg *httpmock.Registry) {
|
|
reg.Register(httpmock.REST("GET", "repos/owner/repo/actions/variables/VARIABLE_ONE"),
|
|
httpmock.StatusStringResponse(404, "not found"),
|
|
)
|
|
},
|
|
wantErr: fmt.Errorf("variable VARIABLE_ONE was not found"),
|
|
},
|
|
{
|
|
name: "when getting any variable from API fails, the error is bubbled with context",
|
|
opts: &GetOptions{
|
|
VariableName: "VARIABLE_ONE",
|
|
},
|
|
host: "github.com",
|
|
httpStubs: func(reg *httpmock.Registry) {
|
|
reg.Register(httpmock.REST("GET", "repos/owner/repo/actions/variables/VARIABLE_ONE"),
|
|
httpmock.StatusStringResponse(400, "not found"),
|
|
)
|
|
},
|
|
wantErr: fmt.Errorf("failed to get variable VARIABLE_ONE: HTTP 400 (https://api.github.com/repos/owner/repo/actions/variables/VARIABLE_ONE)"),
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
var runTest = func(tty bool) func(t *testing.T) {
|
|
return func(t *testing.T) {
|
|
reg := &httpmock.Registry{}
|
|
tt.httpStubs(reg)
|
|
defer reg.Verify(t)
|
|
|
|
ios, _, stdout, _ := iostreams.Test()
|
|
ios.SetStdoutTTY(tty)
|
|
|
|
tt.opts.IO = ios
|
|
tt.opts.BaseRepo = func() (ghrepo.Interface, error) {
|
|
return ghrepo.FromFullNameWithHost("owner/repo", tt.host)
|
|
}
|
|
tt.opts.HttpClient = func() (*http.Client, error) {
|
|
return &http.Client{Transport: reg}, nil
|
|
}
|
|
tt.opts.Config = func() (gh.Config, error) {
|
|
return config.NewBlankConfig(), nil
|
|
}
|
|
|
|
if tt.jsonFields != nil {
|
|
exporter := cmdutil.NewJSONExporter()
|
|
exporter.SetFields(tt.jsonFields)
|
|
tt.opts.Exporter = exporter
|
|
}
|
|
|
|
err := getRun(tt.opts)
|
|
if err != nil {
|
|
require.EqualError(t, tt.wantErr, err.Error())
|
|
return
|
|
}
|
|
|
|
require.NoError(t, err)
|
|
if tt.jsonFields != nil {
|
|
require.JSONEq(t, tt.wantOut, stdout.String())
|
|
} else {
|
|
require.Equal(t, tt.wantOut, stdout.String())
|
|
}
|
|
}
|
|
}
|
|
|
|
t.Run(tt.name+" tty", runTest(true))
|
|
t.Run(tt.name+" no-tty", runTest(false))
|
|
}
|
|
}
|
|
|
|
func TestExportVariables(t *testing.T) {
|
|
ios, _, stdout, _ := iostreams.Test()
|
|
tf, _ := time.Parse(time.RFC3339, "2024-01-01T00:00:00Z")
|
|
vs := &shared.Variable{
|
|
Name: "v1",
|
|
Value: "test1",
|
|
UpdatedAt: tf,
|
|
CreatedAt: tf,
|
|
Visibility: shared.All,
|
|
SelectedReposURL: "https://someurl.com",
|
|
NumSelectedRepos: 1,
|
|
}
|
|
exporter := cmdutil.NewJSONExporter()
|
|
exporter.SetFields(shared.VariableJSONFields)
|
|
require.NoError(t, exporter.Write(ios, vs))
|
|
require.JSONEq(t,
|
|
`{"name":"v1","numSelectedRepos":1,"selectedReposURL":"https://someurl.com","updatedAt":"2024-01-01T00:00:00Z","createdAt":"2024-01-01T00:00:00Z","value":"test1","visibility":"all"}`,
|
|
stdout.String())
|
|
}
|