package edit import ( "testing" "github.com/cli/cli/v2/pkg/cmd/project/shared/queries" "github.com/cli/cli/v2/pkg/cmdutil" "github.com/cli/cli/v2/pkg/iostreams" "github.com/google/shlex" "github.com/stretchr/testify/assert" "gopkg.in/h2non/gock.v1" ) func TestNewCmdEdit(t *testing.T) { tests := []struct { name string cli string wants editOpts wantsErr bool wantsErrMsg string wantsExporter bool }{ { name: "not-a-number", cli: "x", wantsErr: true, wantsErrMsg: "invalid number: x", }, { name: "visibility-error", cli: "--visibility v", wantsErr: true, wantsErrMsg: "invalid argument \"v\" for \"--visibility\" flag: valid values are {PUBLIC|PRIVATE}", }, { name: "no-args", cli: "", wantsErr: true, wantsErrMsg: "no fields to edit", }, { name: "title", cli: "--title t", wants: editOpts{ title: "t", }, }, { name: "number", cli: "123 --title t", wants: editOpts{ number: 123, title: "t", }, }, { name: "owner", cli: "--owner monalisa --title t", wants: editOpts{ owner: "monalisa", title: "t", }, }, { name: "readme", cli: "--readme r", wants: editOpts{ readme: "r", }, }, { name: "description", cli: "--description d", wants: editOpts{ shortDescription: "d", }, }, { name: "visibility", cli: "--visibility PUBLIC", wants: editOpts{ visibility: "PUBLIC", }, }, { name: "json", cli: "--format json --title t", wants: editOpts{ title: "t", }, wantsExporter: true, }, } t.Setenv("GH_TOKEN", "auth-token") 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 editOpts cmd := NewCmdEdit(f, func(config editConfig) error { gotOpts = config.opts return nil }) cmd.SetArgs(argv) _, err = cmd.ExecuteC() if tt.wantsErr { assert.Error(t, err) assert.Equal(t, tt.wantsErrMsg, err.Error()) return } assert.NoError(t, err) assert.Equal(t, tt.wants.number, gotOpts.number) assert.Equal(t, tt.wants.owner, gotOpts.owner) assert.Equal(t, tt.wants.visibility, gotOpts.visibility) assert.Equal(t, tt.wants.title, gotOpts.title) assert.Equal(t, tt.wants.readme, gotOpts.readme) assert.Equal(t, tt.wants.shortDescription, gotOpts.shortDescription) assert.Equal(t, tt.wantsExporter, gotOpts.exporter != nil) }) } } func TestRunUpdate_User(t *testing.T) { defer gock.Off() gock.Observe(gock.DumpRequest) // get user ID gock.New("https://api.github.com"). Post("/graphql"). MatchType("json"). JSON(map[string]interface{}{ "query": "query UserOrgOwner.*", "variables": map[string]interface{}{ "login": "monalisa", }, }). Reply(200). JSON(map[string]interface{}{ "data": map[string]interface{}{ "user": map[string]interface{}{ "id": "an ID", }, }, "errors": []interface{}{ map[string]interface{}{ "type": "NOT_FOUND", "path": []string{"organization"}, }, }, }) // get user project ID gock.New("https://api.github.com"). Post("/graphql"). MatchType("json"). JSON(map[string]interface{}{ "query": "query UserProject.*", "variables": map[string]interface{}{ "login": "monalisa", "number": 1, "firstItems": 0, "afterItems": nil, "firstFields": 0, "afterFields": nil, }, }). Reply(200). JSON(map[string]interface{}{ "data": map[string]interface{}{ "user": map[string]interface{}{ "projectV2": map[string]string{ "id": "an ID", }, }, }, }) // edit project gock.New("https://api.github.com"). Post("/graphql"). BodyString(`{"query":"mutation UpdateProjectV2.*"variables":{"afterFields":null,"afterItems":null,"firstFields":0,"firstItems":0,"input":{"projectId":"an ID","title":"a new title","shortDescription":"a new description","readme":"a new readme","public":true}}}`). Reply(200). JSON(map[string]interface{}{ "data": map[string]interface{}{ "updateProjectV2": map[string]interface{}{ "projectV2": map[string]interface{}{ "title": "a title", "url": "http://a-url.com", "owner": map[string]string{ "login": "monalisa", }, }, }, }, }) client := queries.NewTestClient() ios, _, stdout, _ := iostreams.Test() ios.SetStdoutTTY(true) config := editConfig{ opts: editOpts{ number: 1, owner: "monalisa", title: "a new title", shortDescription: "a new description", visibility: "PUBLIC", readme: "a new readme", }, client: client, io: ios, } err := runEdit(config) assert.NoError(t, err) assert.Equal( t, "http://a-url.com\n", stdout.String()) } func TestRunUpdate_Org(t *testing.T) { defer gock.Off() gock.Observe(gock.DumpRequest) // get org ID gock.New("https://api.github.com"). Post("/graphql"). MatchType("json"). JSON(map[string]interface{}{ "query": "query UserOrgOwner.*", "variables": map[string]interface{}{ "login": "github", }, }). Reply(200). JSON(map[string]interface{}{ "data": map[string]interface{}{ "organization": map[string]interface{}{ "id": "an ID", }, }, "errors": []interface{}{ map[string]interface{}{ "type": "NOT_FOUND", "path": []string{"user"}, }, }, }) // get org project ID gock.New("https://api.github.com"). Post("/graphql"). MatchType("json"). JSON(map[string]interface{}{ "query": "query OrgProject.*", "variables": map[string]interface{}{ "login": "github", "number": 1, "firstItems": 0, "afterItems": nil, "firstFields": 0, "afterFields": nil, }, }). Reply(200). JSON(map[string]interface{}{ "data": map[string]interface{}{ "organization": map[string]interface{}{ "projectV2": map[string]string{ "id": "an ID", }, }, }, }) // edit project gock.New("https://api.github.com"). Post("/graphql"). BodyString(`{"query":"mutation UpdateProjectV2.*"variables":{"afterFields":null,"afterItems":null,"firstFields":0,"firstItems":0,"input":{"projectId":"an ID","title":"a new title","shortDescription":"a new description","readme":"a new readme","public":true}}}`). Reply(200). JSON(map[string]interface{}{ "data": map[string]interface{}{ "updateProjectV2": map[string]interface{}{ "projectV2": map[string]interface{}{ "title": "a title", "url": "http://a-url.com", "owner": map[string]string{ "login": "monalisa", }, }, }, }, }) client := queries.NewTestClient() ios, _, stdout, _ := iostreams.Test() ios.SetStdoutTTY(true) config := editConfig{ opts: editOpts{ number: 1, owner: "github", title: "a new title", shortDescription: "a new description", visibility: "PUBLIC", readme: "a new readme", }, client: client, io: ios, } err := runEdit(config) assert.NoError(t, err) assert.Equal( t, "http://a-url.com\n", stdout.String()) } func TestRunUpdate_Me(t *testing.T) { defer gock.Off() // get viewer ID gock.New("https://api.github.com"). Post("/graphql"). MatchType("json"). JSON(map[string]interface{}{ "query": "query ViewerOwner.*", }). Reply(200). JSON(map[string]interface{}{ "data": map[string]interface{}{ "viewer": map[string]interface{}{ "id": "an ID", }, }, }) gock.Observe(gock.DumpRequest) // get viewer project ID gock.New("https://api.github.com"). Post("/graphql"). MatchType("json"). JSON(map[string]interface{}{ "query": "query ViewerProject.*", "variables": map[string]interface{}{ "number": 1, "firstItems": 0, "afterItems": nil, "firstFields": 0, "afterFields": nil, }, }). Reply(200). JSON(map[string]interface{}{ "data": map[string]interface{}{ "viewer": map[string]interface{}{ "projectV2": map[string]string{ "id": "an ID", }, }, }, }) // edit project gock.New("https://api.github.com"). Post("/graphql"). BodyString(`{"query":"mutation UpdateProjectV2.*"variables":{"afterFields":null,"afterItems":null,"firstFields":0,"firstItems":0,"input":{"projectId":"an ID","title":"a new title","shortDescription":"a new description","readme":"a new readme","public":false}}}`). Reply(200). JSON(map[string]interface{}{ "data": map[string]interface{}{ "updateProjectV2": map[string]interface{}{ "projectV2": map[string]interface{}{ "title": "a title", "url": "http://a-url.com", "owner": map[string]string{ "login": "me", }, }, }, }, }) client := queries.NewTestClient() ios, _, stdout, _ := iostreams.Test() ios.SetStdoutTTY(true) config := editConfig{ opts: editOpts{ number: 1, owner: "@me", title: "a new title", shortDescription: "a new description", visibility: "PRIVATE", readme: "a new readme", }, client: client, io: ios, } err := runEdit(config) assert.NoError(t, err) assert.Equal( t, "http://a-url.com\n", stdout.String()) } func TestRunUpdate_OmitParams(t *testing.T) { defer gock.Off() gock.Observe(gock.DumpRequest) // get user ID gock.New("https://api.github.com"). Post("/graphql"). MatchType("json"). JSON(map[string]interface{}{ "query": "query UserOrgOwner.*", "variables": map[string]interface{}{ "login": "monalisa", }, }). Reply(200). JSON(map[string]interface{}{ "data": map[string]interface{}{ "user": map[string]interface{}{ "id": "an ID", }, }, "errors": []interface{}{ map[string]interface{}{ "type": "NOT_FOUND", "path": []string{"organization"}, }, }, }) // get user project ID gock.New("https://api.github.com"). Post("/graphql"). MatchType("json"). JSON(map[string]interface{}{ "query": "query UserProject.*", "variables": map[string]interface{}{ "login": "monalisa", "number": 1, "firstItems": 0, "afterItems": nil, "firstFields": 0, "afterFields": nil, }, }). Reply(200). JSON(map[string]interface{}{ "data": map[string]interface{}{ "user": map[string]interface{}{ "projectV2": map[string]string{ "id": "an ID", }, }, }, }) // Update project gock.New("https://api.github.com"). Post("/graphql"). BodyString(`{"query":"mutation UpdateProjectV2.*"variables":{"afterFields":null,"afterItems":null,"firstFields":0,"firstItems":0,"input":{"projectId":"an ID","title":"another title"}}}`). Reply(200). JSON(map[string]interface{}{ "data": map[string]interface{}{ "updateProjectV2": map[string]interface{}{ "projectV2": map[string]interface{}{ "title": "a title", "url": "http://a-url.com", "owner": map[string]string{ "login": "monalisa", }, }, }, }, }) client := queries.NewTestClient() ios, _, stdout, _ := iostreams.Test() ios.SetStdoutTTY(true) config := editConfig{ opts: editOpts{ number: 1, owner: "monalisa", title: "another title", }, client: client, io: ios, } err := runEdit(config) assert.NoError(t, err) assert.Equal( t, "http://a-url.com\n", stdout.String()) } func TestRunUpdate_JSON(t *testing.T) { defer gock.Off() gock.Observe(gock.DumpRequest) // get user ID gock.New("https://api.github.com"). Post("/graphql"). MatchType("json"). JSON(map[string]interface{}{ "query": "query UserOrgOwner.*", "variables": map[string]interface{}{ "login": "monalisa", }, }). Reply(200). JSON(map[string]interface{}{ "data": map[string]interface{}{ "user": map[string]interface{}{ "id": "an ID", }, }, "errors": []interface{}{ map[string]interface{}{ "type": "NOT_FOUND", "path": []string{"organization"}, }, }, }) // get user project ID gock.New("https://api.github.com"). Post("/graphql"). MatchType("json"). JSON(map[string]interface{}{ "query": "query UserProject.*", "variables": map[string]interface{}{ "login": "monalisa", "number": 1, "firstItems": 0, "afterItems": nil, "firstFields": 0, "afterFields": nil, }, }). Reply(200). JSON(map[string]interface{}{ "data": map[string]interface{}{ "user": map[string]interface{}{ "projectV2": map[string]string{ "id": "an ID", }, }, }, }) // edit project gock.New("https://api.github.com"). Post("/graphql"). BodyString(`{"query":"mutation UpdateProjectV2.*"variables":{"afterFields":null,"afterItems":null,"firstFields":0,"firstItems":0,"input":{"projectId":"an ID","title":"a new title","shortDescription":"a new description","readme":"a new readme","public":true}}}`). Reply(200). JSON(map[string]interface{}{ "data": map[string]interface{}{ "updateProjectV2": map[string]interface{}{ "projectV2": map[string]interface{}{ "number": 1, "title": "a title", "url": "http://a-url.com", "owner": map[string]string{ "login": "monalisa", }, }, }, }, }) client := queries.NewTestClient() ios, _, stdout, _ := iostreams.Test() config := editConfig{ opts: editOpts{ number: 1, owner: "monalisa", title: "a new title", shortDescription: "a new description", visibility: "PUBLIC", readme: "a new readme", exporter: cmdutil.NewJSONExporter(), }, client: client, io: ios, } err := runEdit(config) assert.NoError(t, err) assert.JSONEq( t, `{"number":1,"url":"http://a-url.com","shortDescription":"","public":false,"closed":false,"title":"a title","id":"","readme":"","items":{"totalCount":0},"fields":{"totalCount":0},"owner":{"type":"","login":"monalisa"}}`, stdout.String()) }