Repository: cli/cli
File: pkg/cmd/api/api_test.go (Go)

Code before:
type stubAuthConfig struct {
	config.AuthConfig
}

var _ gh.AuthConfig = (*stubAuthConfig)(nil)

func (c *stubAuthConfig) ActiveToken(host string) (string, string) {
	return "token", "stub"
}

func Test_apiRun_cache(t *testing.T) {
	// Given we have a test server that spies on the number of requests it receives
	requestCount := 0
	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		requestCount++
		w.WriteHeader(http.StatusNoContent)
	}))
	t.Cleanup(s.Close)

	ios, _, stdout, stderr := iostreams.Test()
	options := ApiOptions{
		IO: ios,
		Config: func() (cfg gh.Config, err error) {
			return &ghmock.ConfigMock{
				AuthenticationFunc: func() gh.AuthConfig {
					return &stubAuthConfig{}
				},
				// Cached responses are stored in a tempdir that gets automatically cleaned up
				CacheDirFunc: func() string {
					return t.TempDir()
				},
			}, nil
		},
		// You might think that we want to set Host: s.URL here, but you'd be wrong.
		// The host field is later used to evaluate an API URL e.g. https://api.host.com/graphql
		// The RequestPath field is used exactly as is, for the request if it includes a host.
		RequestPath: s.URL,
		CacheTTL:    time.Minute,
	}

	// When we run the API behaviour twice
	require.NoError(t, apiRun(&options))
	require.NoError(t, apiRun(&options))

	// We only get one request to the http server because it uses the cached response
	assert.Equal(t, 1, requestCount)
	assert.Equal(t, "", stdout.String(), "stdout")
	assert.Equal(t, "", stderr.String(), "stderr")
}

func Test_openUserFile(t *testing.T) {

Review comment (security):
Since `cfg` isn't exported, I couldn't do something like `&config.AuthConfig{cfg: cfg}` which otherwise probably makes sense. Considered exporting `Cfg` but it seemed too invasive so used a stub pattern I found in another test instead.

Code after:
func Test_apiRun_cache(t *testing.T) {
	// Given we have a test server that spies on the number of requests it receives
	requestCount := 0
	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		requestCount++
		w.WriteHeader(http.StatusNoContent)
	}))
	t.Cleanup(s.Close)

	ios, _, stdout, stderr := iostreams.Test()
	options := ApiOptions{
		IO: ios,
		Config: func() (gh.Config, error) {
			return &ghmock.ConfigMock{
				AuthenticationFunc: func() gh.AuthConfig {
					cfg := &config.AuthConfig{}
					// Required because the http client tries to get the active token and otherwise
					// this goes down to to go-gh config and panics. Pretty bad solution, it would
					// be better if this were black box.
					cfg.SetActiveToken("token", "stub")
					return cfg
				},
				// Cached responses are stored in a tempdir that gets automatically cleaned up
				CacheDirFunc: func() string {
					return t.TempDir()
				},
			}, nil
		},
		// You might think that we want to set Host: s.URL here, but you'd be wrong.
		// The host field is later used to evaluate an API URL e.g. https://api.host.com/graphql
		// The RequestPath field is used exactly as is, for the request if it includes a host.
		RequestPath: s.URL,
		CacheTTL:    time.Minute,
	}

	// When we run the API behaviour twice
	require.NoError(t, apiRun(&options))
	require.NoError(t, apiRun(&options))

	// We only get one request to the http server because it uses the cached response
	assert.Equal(t, 1, requestCount)
	assert.Equal(t, "", stdout.String(), "stdout")
	assert.Equal(t, "", stderr.String(), "stderr")
}

func Test_openUserFile(t *testing.T) {
	f, err := os.CreateTemp(t.TempDir(), "gh-test")
	if err != nil {
		t.Fatal(err)
	}
	defer f.Close()