package api
import (
"io"
"net/http"
"os"
"strings"
"testing"
cmdutil "gitcode.com/gitcode-cli/cli/pkg/cmdutil"
"gitcode.com/gitcode-cli/cli/pkg/testutil"
)
func TestNewCmdAPIParsesFlags(t *testing.T) {
cmd := NewCmdAPI(cmdutil.TestFactory(), func(opts *Options) error {
if opts.Endpoint != "repos/owner/repo" {
t.Fatalf("Endpoint = %q", opts.Endpoint)
}
if opts.Method != "PATCH" {
t.Fatalf("Method = %q", opts.Method)
}
if len(opts.Headers) != 1 || opts.Headers[0] != "X-Test: yes" {
t.Fatalf("Headers = %#v", opts.Headers)
}
if opts.Input != "body.json" {
t.Fatalf("Input = %q", opts.Input)
}
return nil
})
cmd.SetArgs([]string{"repos/owner/repo", "--method", "PATCH", "--header", "X-Test: yes", "--input", "body.json"})
if err := cmd.Execute(); err != nil {
t.Fatalf("Execute() error = %v", err)
}
}
func TestRunCallsRawAPI(t *testing.T) {
t.Setenv("GC_TOKEN", "test-token")
ioStreams, in, out, _ := testutil.NewTestIOStreams()
in.WriteString(`{"title":"updated"}`)
var gotPath string
var gotMethod string
var gotAuth string
var gotHeader string
var gotBody string
client := testutil.NewTestHTTPClient(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gotPath = r.URL.Path
gotMethod = r.Method
gotAuth = r.Header.Get("Authorization")
gotHeader = r.Header.Get("X-Test")
body, err := io.ReadAll(r.Body)
if err != nil {
t.Fatalf("ReadAll() error = %v", err)
}
gotBody = string(body)
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"ok":true}`))
}))
err := run(&Options{
IO: ioStreams,
HttpClient: func() (*http.Client, error) { return client, nil },
Endpoint: "repos/owner/repo/pulls/1",
Method: "patch",
Headers: []string{"X-Test: yes"},
Input: "-",
})
if err != nil {
t.Fatalf("run() error = %v", err)
}
if gotPath != "/api/v5/repos/owner/repo/pulls/1" {
t.Fatalf("path = %q", gotPath)
}
if gotMethod != http.MethodPatch {
t.Fatalf("method = %q", gotMethod)
}
if gotAuth != "Bearer test-token" {
t.Fatalf("Authorization = %q", gotAuth)
}
if gotHeader != "yes" {
t.Fatalf("X-Test = %q", gotHeader)
}
if gotBody != `{"title":"updated"}` {
t.Fatalf("body = %q", gotBody)
}
if strings.TrimSpace(out.String()) != `{"ok":true}` {
t.Fatalf("output = %q", out.String())
}
}
func TestRunDefaultsToPostWhenInputProvided(t *testing.T) {
t.Setenv("GC_TOKEN", "test-token")
ioStreams, in, out, _ := testutil.NewTestIOStreams()
in.WriteString(`{"title":"created"}`)
var gotMethod string
client := testutil.NewTestHTTPClient(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gotMethod = r.Method
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"ok":true}`))
}))
err := run(&Options{
IO: ioStreams,
HttpClient: func() (*http.Client, error) { return client, nil },
Endpoint: "repos/owner/repo/issues",
Method: http.MethodGet,
Input: "-",
})
if err != nil {
t.Fatalf("run() error = %v", err)
}
if gotMethod != http.MethodPost {
t.Fatalf("method = %q, want POST", gotMethod)
}
if strings.TrimSpace(out.String()) != `{"ok":true}` {
t.Fatalf("output = %q", out.String())
}
}
func TestRunRejectsForeignURLHost(t *testing.T) {
t.Setenv("GC_TOKEN", "test-token")
ioStreams, _, _, _ := testutil.NewTestIOStreams()
client := testutil.NewTestHTTPClient(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
t.Fatalf("unexpected request to %s", r.URL.String())
}))
err := run(&Options{
IO: ioStreams,
HttpClient: func() (*http.Client, error) { return client, nil },
Endpoint: "https://example.com/api/v5/repos/owner/repo",
Method: http.MethodGet,
})
if err == nil || !strings.Contains(err.Error(), "api endpoint host") {
t.Fatalf("run() error = %v, want host rejection", err)
}
}
func TestParseHeadersRejectsInvalidHeader(t *testing.T) {
_, err := parseHeaders([]string{"missing-colon"})
if err == nil || !strings.Contains(err.Error(), "--header") {
t.Fatalf("parseHeaders() error = %v, want host rejection", err)
}
}
func TestReadInputEmptyReturnsNil(t *testing.T) {
r, err := readInput(&Options{Input: ""})
if err != nil {
t.Fatalf("readInput() error = %v", err)
}
if r != nil {
t.Errorf("readInput() = %v, want nil", r)
}
}
func TestReadInputStreamsFile(t *testing.T) {
tmpFile, err := os.CreateTemp("", "input*.json")
if err != nil {
t.Fatalf("CreateTemp() error = %v", err)
}
defer os.Remove(tmpFile.Name())
want := `{"k":"v"}`
if _, err := tmpFile.WriteString(want); err != nil {
t.Fatalf("WriteString() error = %v", err)
}
tmpFile.Close()
r, err := readInput(&Options{Input: tmpFile.Name()})
if err != nil {
t.Fatalf("readInput() error = %v", err)
}
if r == nil {
t.Fatal("readInput() returned nil reader")
}
defer func() {
if rc, ok := r.(io.Closer); ok {
_ = rc.Close()
}
}()
data, err := io.ReadAll(r)
if err != nil {
t.Fatalf("ReadAll() error = %v", err)
}
if string(data) != want {
t.Errorf("readInput() data = %q, want %q", string(data), want)
}
}