github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/api/queries_repo_test.go (about)

     1  package api
     2  
     3  import (
     4  	"io"
     5  	"net/http"
     6  	"strings"
     7  	"testing"
     8  
     9  	"github.com/ungtb10d/cli/v2/internal/ghrepo"
    10  	"github.com/ungtb10d/cli/v2/pkg/httpmock"
    11  )
    12  
    13  func TestGitHubRepo_notFound(t *testing.T) {
    14  	httpReg := &httpmock.Registry{}
    15  	defer httpReg.Verify(t)
    16  
    17  	httpReg.Register(
    18  		httpmock.GraphQL(`query RepositoryInfo\b`),
    19  		httpmock.StringResponse(`{ "data": { "repository": null } }`))
    20  
    21  	client := newTestClient(httpReg)
    22  	repo, err := GitHubRepo(client, ghrepo.New("OWNER", "REPO"))
    23  	if err == nil {
    24  		t.Fatal("GitHubRepo did not return an error")
    25  	}
    26  	if wants := "GraphQL: Could not resolve to a Repository with the name 'OWNER/REPO'."; err.Error() != wants {
    27  		t.Errorf("GitHubRepo error: want %q, got %q", wants, err.Error())
    28  	}
    29  	if repo != nil {
    30  		t.Errorf("GitHubRepo: expected nil repo, got %v", repo)
    31  	}
    32  }
    33  
    34  func Test_RepoMetadata(t *testing.T) {
    35  	http := &httpmock.Registry{}
    36  	client := newTestClient(http)
    37  
    38  	repo, _ := ghrepo.FromFullName("OWNER/REPO")
    39  	input := RepoMetadataInput{
    40  		Assignees:  true,
    41  		Reviewers:  true,
    42  		Labels:     true,
    43  		Projects:   true,
    44  		Milestones: true,
    45  	}
    46  
    47  	http.Register(
    48  		httpmock.GraphQL(`query RepositoryAssignableUsers\b`),
    49  		httpmock.StringResponse(`
    50  		{ "data": { "repository": { "assignableUsers": {
    51  			"nodes": [
    52  				{ "login": "hubot", "id": "HUBOTID" },
    53  				{ "login": "MonaLisa", "id": "MONAID" }
    54  			],
    55  			"pageInfo": { "hasNextPage": false }
    56  		} } } }
    57  		`))
    58  	http.Register(
    59  		httpmock.GraphQL(`query RepositoryLabelList\b`),
    60  		httpmock.StringResponse(`
    61  		{ "data": { "repository": { "labels": {
    62  			"nodes": [
    63  				{ "name": "feature", "id": "FEATUREID" },
    64  				{ "name": "TODO", "id": "TODOID" },
    65  				{ "name": "bug", "id": "BUGID" }
    66  			],
    67  			"pageInfo": { "hasNextPage": false }
    68  		} } } }
    69  		`))
    70  	http.Register(
    71  		httpmock.GraphQL(`query RepositoryMilestoneList\b`),
    72  		httpmock.StringResponse(`
    73  		{ "data": { "repository": { "milestones": {
    74  			"nodes": [
    75  				{ "title": "GA", "id": "GAID" },
    76  				{ "title": "Big One.oh", "id": "BIGONEID" }
    77  			],
    78  			"pageInfo": { "hasNextPage": false }
    79  		} } } }
    80  		`))
    81  	http.Register(
    82  		httpmock.GraphQL(`query RepositoryProjectList\b`),
    83  		httpmock.StringResponse(`
    84  		{ "data": { "repository": { "projects": {
    85  			"nodes": [
    86  				{ "name": "Cleanup", "id": "CLEANUPID" },
    87  				{ "name": "Roadmap", "id": "ROADMAPID" }
    88  			],
    89  			"pageInfo": { "hasNextPage": false }
    90  		} } } }
    91  		`))
    92  	http.Register(
    93  		httpmock.GraphQL(`query OrganizationProjectList\b`),
    94  		httpmock.StringResponse(`
    95  		{ "data": { "organization": { "projects": {
    96  			"nodes": [
    97  				{ "name": "Triage", "id": "TRIAGEID" }
    98  			],
    99  			"pageInfo": { "hasNextPage": false }
   100  		} } } }
   101  		`))
   102  	http.Register(
   103  		httpmock.GraphQL(`query OrganizationTeamList\b`),
   104  		httpmock.StringResponse(`
   105  		{ "data": { "organization": { "teams": {
   106  			"nodes": [
   107  				{ "slug": "owners", "id": "OWNERSID" },
   108  				{ "slug": "Core", "id": "COREID" }
   109  			],
   110  			"pageInfo": { "hasNextPage": false }
   111  		} } } }
   112  		`))
   113  
   114  	result, err := RepoMetadata(client, repo, input)
   115  	if err != nil {
   116  		t.Fatalf("unexpected error: %v", err)
   117  	}
   118  
   119  	expectedMemberIDs := []string{"MONAID", "HUBOTID"}
   120  	memberIDs, err := result.MembersToIDs([]string{"monalisa", "hubot"})
   121  	if err != nil {
   122  		t.Errorf("error resolving members: %v", err)
   123  	}
   124  	if !sliceEqual(memberIDs, expectedMemberIDs) {
   125  		t.Errorf("expected members %v, got %v", expectedMemberIDs, memberIDs)
   126  	}
   127  
   128  	expectedTeamIDs := []string{"COREID", "OWNERSID"}
   129  	teamIDs, err := result.TeamsToIDs([]string{"OWNER/core", "/owners"})
   130  	if err != nil {
   131  		t.Errorf("error resolving teams: %v", err)
   132  	}
   133  	if !sliceEqual(teamIDs, expectedTeamIDs) {
   134  		t.Errorf("expected teams %v, got %v", expectedTeamIDs, teamIDs)
   135  	}
   136  
   137  	expectedLabelIDs := []string{"BUGID", "TODOID"}
   138  	labelIDs, err := result.LabelsToIDs([]string{"bug", "todo"})
   139  	if err != nil {
   140  		t.Errorf("error resolving labels: %v", err)
   141  	}
   142  	if !sliceEqual(labelIDs, expectedLabelIDs) {
   143  		t.Errorf("expected labels %v, got %v", expectedLabelIDs, labelIDs)
   144  	}
   145  
   146  	expectedProjectIDs := []string{"TRIAGEID", "ROADMAPID"}
   147  	projectIDs, err := result.ProjectsToIDs([]string{"triage", "roadmap"})
   148  	if err != nil {
   149  		t.Errorf("error resolving projects: %v", err)
   150  	}
   151  	if !sliceEqual(projectIDs, expectedProjectIDs) {
   152  		t.Errorf("expected projects %v, got %v", expectedProjectIDs, projectIDs)
   153  	}
   154  
   155  	expectedMilestoneID := "BIGONEID"
   156  	milestoneID, err := result.MilestoneToID("big one.oh")
   157  	if err != nil {
   158  		t.Errorf("error resolving milestone: %v", err)
   159  	}
   160  	if milestoneID != expectedMilestoneID {
   161  		t.Errorf("expected milestone %v, got %v", expectedMilestoneID, milestoneID)
   162  	}
   163  }
   164  
   165  func Test_ProjectsToPaths(t *testing.T) {
   166  	expectedProjectPaths := []string{"OWNER/REPO/PROJECT_NUMBER", "ORG/PROJECT_NUMBER"}
   167  	projects := []RepoProject{
   168  		{ID: "id1", Name: "My Project", ResourcePath: "/OWNER/REPO/projects/PROJECT_NUMBER"},
   169  		{ID: "id2", Name: "Org Project", ResourcePath: "/orgs/ORG/projects/PROJECT_NUMBER"},
   170  		{ID: "id3", Name: "Project", ResourcePath: "/orgs/ORG/projects/PROJECT_NUMBER_2"},
   171  	}
   172  	projectNames := []string{"My Project", "Org Project"}
   173  
   174  	projectPaths, err := ProjectsToPaths(projects, projectNames)
   175  	if err != nil {
   176  		t.Errorf("error resolving projects: %v", err)
   177  	}
   178  	if !sliceEqual(projectPaths, expectedProjectPaths) {
   179  		t.Errorf("expected projects %v, got %v", expectedProjectPaths, projectPaths)
   180  	}
   181  }
   182  
   183  func Test_ProjectNamesToPaths(t *testing.T) {
   184  	http := &httpmock.Registry{}
   185  	client := newTestClient(http)
   186  
   187  	repo, _ := ghrepo.FromFullName("OWNER/REPO")
   188  
   189  	http.Register(
   190  		httpmock.GraphQL(`query RepositoryProjectList\b`),
   191  		httpmock.StringResponse(`
   192  		{ "data": { "repository": { "projects": {
   193  			"nodes": [
   194  				{ "name": "Cleanup", "id": "CLEANUPID", "resourcePath": "/OWNER/REPO/projects/1" },
   195  				{ "name": "Roadmap", "id": "ROADMAPID", "resourcePath": "/OWNER/REPO/projects/2" }
   196  			],
   197  			"pageInfo": { "hasNextPage": false }
   198  		} } } }
   199  		`))
   200  	http.Register(
   201  		httpmock.GraphQL(`query OrganizationProjectList\b`),
   202  		httpmock.StringResponse(`
   203  			{ "data": { "organization": { "projects": {
   204  				"nodes": [
   205  					{ "name": "Triage", "id": "TRIAGEID", "resourcePath": "/orgs/ORG/projects/1"  }
   206  				],
   207  				"pageInfo": { "hasNextPage": false }
   208  			} } } }
   209  			`))
   210  
   211  	projectPaths, err := ProjectNamesToPaths(client, repo, []string{"Triage", "Roadmap"})
   212  	if err != nil {
   213  		t.Fatalf("unexpected error: %v", err)
   214  	}
   215  
   216  	expectedProjectPaths := []string{"ORG/1", "OWNER/REPO/2"}
   217  	if !sliceEqual(projectPaths, expectedProjectPaths) {
   218  		t.Errorf("expected projects paths %v, got %v", expectedProjectPaths, projectPaths)
   219  	}
   220  }
   221  
   222  func Test_RepoResolveMetadataIDs(t *testing.T) {
   223  	http := &httpmock.Registry{}
   224  	client := newTestClient(http)
   225  
   226  	repo, _ := ghrepo.FromFullName("OWNER/REPO")
   227  	input := RepoResolveInput{
   228  		Assignees: []string{"monalisa", "hubot"},
   229  		Reviewers: []string{"monalisa", "octocat", "OWNER/core", "/robots"},
   230  		Labels:    []string{"bug", "help wanted"},
   231  	}
   232  
   233  	expectedQuery := `query RepositoryResolveMetadataIDs {
   234  u000: user(login:"monalisa"){id,login}
   235  u001: user(login:"hubot"){id,login}
   236  u002: user(login:"octocat"){id,login}
   237  repository(owner:"OWNER",name:"REPO"){
   238  l000: label(name:"bug"){id,name}
   239  l001: label(name:"help wanted"){id,name}
   240  }
   241  organization(login:"OWNER"){
   242  t000: team(slug:"core"){id,slug}
   243  t001: team(slug:"robots"){id,slug}
   244  }
   245  }
   246  `
   247  	responseJSON := `
   248  	{ "data": {
   249  		"u000": { "login": "MonaLisa", "id": "MONAID" },
   250  		"u001": { "login": "hubot", "id": "HUBOTID" },
   251  		"u002": { "login": "octocat", "id": "OCTOID" },
   252  		"repository": {
   253  			"l000": { "name": "bug", "id": "BUGID" },
   254  			"l001": { "name": "Help Wanted", "id": "HELPID" }
   255  		},
   256  		"organization": {
   257  			"t000": { "slug": "core", "id": "COREID" },
   258  			"t001": { "slug": "Robots", "id": "ROBOTID" }
   259  		}
   260  	} }
   261  	`
   262  
   263  	http.Register(
   264  		httpmock.GraphQL(`query RepositoryResolveMetadataIDs\b`),
   265  		httpmock.GraphQLQuery(responseJSON, func(q string, _ map[string]interface{}) {
   266  			if q != expectedQuery {
   267  				t.Errorf("expected query %q, got %q", expectedQuery, q)
   268  			}
   269  		}))
   270  
   271  	result, err := RepoResolveMetadataIDs(client, repo, input)
   272  	if err != nil {
   273  		t.Fatalf("unexpected error: %v", err)
   274  	}
   275  
   276  	expectedMemberIDs := []string{"MONAID", "HUBOTID", "OCTOID"}
   277  	memberIDs, err := result.MembersToIDs([]string{"monalisa", "hubot", "octocat"})
   278  	if err != nil {
   279  		t.Errorf("error resolving members: %v", err)
   280  	}
   281  	if !sliceEqual(memberIDs, expectedMemberIDs) {
   282  		t.Errorf("expected members %v, got %v", expectedMemberIDs, memberIDs)
   283  	}
   284  
   285  	expectedTeamIDs := []string{"COREID", "ROBOTID"}
   286  	teamIDs, err := result.TeamsToIDs([]string{"/core", "/robots"})
   287  	if err != nil {
   288  		t.Errorf("error resolving teams: %v", err)
   289  	}
   290  	if !sliceEqual(teamIDs, expectedTeamIDs) {
   291  		t.Errorf("expected members %v, got %v", expectedTeamIDs, teamIDs)
   292  	}
   293  
   294  	expectedLabelIDs := []string{"BUGID", "HELPID"}
   295  	labelIDs, err := result.LabelsToIDs([]string{"bug", "help wanted"})
   296  	if err != nil {
   297  		t.Errorf("error resolving labels: %v", err)
   298  	}
   299  	if !sliceEqual(labelIDs, expectedLabelIDs) {
   300  		t.Errorf("expected members %v, got %v", expectedLabelIDs, labelIDs)
   301  	}
   302  }
   303  
   304  func sliceEqual(a, b []string) bool {
   305  	if len(a) != len(b) {
   306  		return false
   307  	}
   308  
   309  	for i := range a {
   310  		if a[i] != b[i] {
   311  			return false
   312  		}
   313  	}
   314  
   315  	return true
   316  }
   317  
   318  func Test_RepoMilestones(t *testing.T) {
   319  	tests := []struct {
   320  		state   string
   321  		want    string
   322  		wantErr bool
   323  	}{
   324  		{
   325  			state: "open",
   326  			want:  `"states":["OPEN"]`,
   327  		},
   328  		{
   329  			state: "closed",
   330  			want:  `"states":["CLOSED"]`,
   331  		},
   332  		{
   333  			state: "all",
   334  			want:  `"states":["OPEN","CLOSED"]`,
   335  		},
   336  		{
   337  			state:   "invalid state",
   338  			wantErr: true,
   339  		},
   340  	}
   341  	for _, tt := range tests {
   342  		var query string
   343  		reg := &httpmock.Registry{}
   344  		reg.Register(httpmock.MatchAny, func(req *http.Request) (*http.Response, error) {
   345  			buf := new(strings.Builder)
   346  			_, err := io.Copy(buf, req.Body)
   347  			if err != nil {
   348  				return nil, err
   349  			}
   350  			query = buf.String()
   351  			return httpmock.StringResponse("{}")(req)
   352  		})
   353  		client := newTestClient(reg)
   354  
   355  		_, err := RepoMilestones(client, ghrepo.New("OWNER", "REPO"), tt.state)
   356  		if (err != nil) != tt.wantErr {
   357  			t.Errorf("RepoMilestones() error = %v, wantErr %v", err, tt.wantErr)
   358  			return
   359  		}
   360  		if !strings.Contains(query, tt.want) {
   361  			t.Errorf("query does not contain %v", tt.want)
   362  		}
   363  	}
   364  }
   365  
   366  func TestDisplayName(t *testing.T) {
   367  	tests := []struct {
   368  		name     string
   369  		assignee RepoAssignee
   370  		want     string
   371  	}{
   372  		{
   373  			name:     "assignee with name",
   374  			assignee: RepoAssignee{"123", "octocat123", "Octavious Cath"},
   375  			want:     "octocat123 (Octavious Cath)",
   376  		},
   377  		{
   378  			name:     "assignee without name",
   379  			assignee: RepoAssignee{"123", "octocat123", ""},
   380  			want:     "octocat123",
   381  		},
   382  	}
   383  	for _, tt := range tests {
   384  		actual := tt.assignee.DisplayName()
   385  		if actual != tt.want {
   386  			t.Errorf("display name was %s wanted %s", actual, tt.want)
   387  		}
   388  	}
   389  }