cli/pkg/cmd/variable/get/get_test.go
Andy Feller c088951944 Fix host handling in variable and secret delete
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.
2024-08-02 15:11:22 -04:00

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())
}