github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/pkg/cmd/pr/checks/checks_test.go (about)

     1  package checks
     2  
     3  import (
     4  	"bytes"
     5  	"net/http"
     6  	"reflect"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/ungtb10d/cli/v2/api"
    11  	"github.com/ungtb10d/cli/v2/internal/browser"
    12  	"github.com/ungtb10d/cli/v2/internal/ghrepo"
    13  	"github.com/ungtb10d/cli/v2/internal/run"
    14  	"github.com/ungtb10d/cli/v2/pkg/cmd/pr/shared"
    15  	"github.com/ungtb10d/cli/v2/pkg/cmdutil"
    16  	"github.com/ungtb10d/cli/v2/pkg/httpmock"
    17  	"github.com/ungtb10d/cli/v2/pkg/iostreams"
    18  	"github.com/google/shlex"
    19  	"github.com/stretchr/testify/assert"
    20  )
    21  
    22  func TestNewCmdChecks(t *testing.T) {
    23  	tests := []struct {
    24  		name       string
    25  		cli        string
    26  		wants      ChecksOptions
    27  		wantsError string
    28  	}{
    29  		{
    30  			name: "no arguments",
    31  			cli:  "",
    32  			wants: ChecksOptions{
    33  				Interval: time.Duration(10000000000),
    34  			},
    35  		},
    36  		{
    37  			name: "pr argument",
    38  			cli:  "1234",
    39  			wants: ChecksOptions{
    40  				SelectorArg: "1234",
    41  				Interval:    time.Duration(10000000000),
    42  			},
    43  		},
    44  		{
    45  			name: "watch flag",
    46  			cli:  "--watch",
    47  			wants: ChecksOptions{
    48  				Watch:    true,
    49  				Interval: time.Duration(10000000000),
    50  			},
    51  		},
    52  		{
    53  			name: "watch flag and interval flag",
    54  			cli:  "--watch --interval 5",
    55  			wants: ChecksOptions{
    56  				Watch:    true,
    57  				Interval: time.Duration(5000000000),
    58  			},
    59  		},
    60  		{
    61  			name:       "interval flag without watch flag",
    62  			cli:        "--interval 5",
    63  			wantsError: "cannot use `--interval` flag without `--watch` flag",
    64  		},
    65  		{
    66  			name: "required flag",
    67  			cli:  "--required",
    68  			wants: ChecksOptions{
    69  				Required: true,
    70  				Interval: time.Duration(10000000000),
    71  			},
    72  		},
    73  	}
    74  
    75  	for _, tt := range tests {
    76  		t.Run(tt.name, func(t *testing.T) {
    77  			ios, _, _, _ := iostreams.Test()
    78  			f := &cmdutil.Factory{
    79  				IOStreams: ios,
    80  			}
    81  
    82  			argv, err := shlex.Split(tt.cli)
    83  			assert.NoError(t, err)
    84  
    85  			var gotOpts *ChecksOptions
    86  			cmd := NewCmdChecks(f, func(opts *ChecksOptions) error {
    87  				gotOpts = opts
    88  				return nil
    89  			})
    90  			cmd.SetArgs(argv)
    91  			cmd.SetIn(&bytes.Buffer{})
    92  			cmd.SetOut(&bytes.Buffer{})
    93  			cmd.SetErr(&bytes.Buffer{})
    94  
    95  			_, err = cmd.ExecuteC()
    96  			if tt.wantsError != "" {
    97  				assert.EqualError(t, err, tt.wantsError)
    98  				return
    99  			}
   100  			assert.NoError(t, err)
   101  			assert.Equal(t, tt.wants.SelectorArg, gotOpts.SelectorArg)
   102  			assert.Equal(t, tt.wants.Watch, gotOpts.Watch)
   103  			assert.Equal(t, tt.wants.Interval, gotOpts.Interval)
   104  			assert.Equal(t, tt.wants.Required, gotOpts.Required)
   105  		})
   106  	}
   107  }
   108  
   109  func Test_checksRun(t *testing.T) {
   110  	tests := []struct {
   111  		name      string
   112  		tty       bool
   113  		watch     bool
   114  		required  bool
   115  		httpStubs func(*httpmock.Registry)
   116  		wantOut   string
   117  		wantErr   string
   118  	}{
   119  		{
   120  			name: "no commits",
   121  			tty:  true,
   122  			httpStubs: func(reg *httpmock.Registry) {
   123  				reg.Register(
   124  					httpmock.GraphQL(`query PullRequestStatusChecks\b`),
   125  					httpmock.StringResponse(`{"data":{"node":{}}}`),
   126  				)
   127  			},
   128  			wantOut: "",
   129  			wantErr: "no commit found on the pull request",
   130  		},
   131  		{
   132  			name: "no checks",
   133  			tty:  true,
   134  			httpStubs: func(reg *httpmock.Registry) {
   135  				reg.Register(
   136  					httpmock.GraphQL(`query PullRequestStatusChecks\b`),
   137  					httpmock.StringResponse(`{"data":{"node":{"statusCheckRollup":{"nodes":[{"commit":{"oid": "abc"}}]}}}}`),
   138  				)
   139  			},
   140  			wantOut: "",
   141  			wantErr: "no checks reported on the 'trunk' branch",
   142  		},
   143  		{
   144  			name: "some failing",
   145  			tty:  true,
   146  			httpStubs: func(reg *httpmock.Registry) {
   147  				reg.Register(
   148  					httpmock.GraphQL(`query PullRequestStatusChecks\b`),
   149  					httpmock.FileResponse("./fixtures/someFailing.json"),
   150  				)
   151  			},
   152  			wantOut: "Some checks were not successful\n1 failing, 1 successful, 0 skipped, and 1 pending checks\n\nX  sad tests   1m26s  sweet link\n✓  cool tests  1m26s  sweet link\n*  slow tests  1m26s  sweet link\n",
   153  			wantErr: "SilentError",
   154  		},
   155  		{
   156  			name: "some pending",
   157  			tty:  true,
   158  			httpStubs: func(reg *httpmock.Registry) {
   159  				reg.Register(
   160  					httpmock.GraphQL(`query PullRequestStatusChecks\b`),
   161  					httpmock.FileResponse("./fixtures/somePending.json"),
   162  				)
   163  			},
   164  			wantOut: "Some checks are still pending\n0 failing, 2 successful, 0 skipped, and 1 pending checks\n\n✓  cool tests  1m26s  sweet link\n✓  rad tests   1m26s  sweet link\n*  slow tests  1m26s  sweet link\n",
   165  			wantErr: "SilentError",
   166  		},
   167  		{
   168  			name: "all passing",
   169  			tty:  true,
   170  			httpStubs: func(reg *httpmock.Registry) {
   171  				reg.Register(
   172  					httpmock.GraphQL(`query PullRequestStatusChecks\b`),
   173  					httpmock.FileResponse("./fixtures/allPassing.json"),
   174  				)
   175  			},
   176  			wantOut: "All checks were successful\n0 failing, 3 successful, 0 skipped, and 0 pending checks\n\n✓  awesome tests  1m26s  sweet link\n✓  cool tests     1m26s  sweet link\n✓  rad tests      1m26s  sweet link\n",
   177  			wantErr: "",
   178  		},
   179  		{
   180  			name:  "watch all passing",
   181  			tty:   true,
   182  			watch: true,
   183  			httpStubs: func(reg *httpmock.Registry) {
   184  				reg.Register(
   185  					httpmock.GraphQL(`query PullRequestStatusChecks\b`),
   186  					httpmock.FileResponse("./fixtures/allPassing.json"),
   187  				)
   188  			},
   189  			wantOut: "\x1b[?1049hAll checks were successful\n0 failing, 3 successful, 0 skipped, and 0 pending checks\n\n✓  awesome tests  1m26s  sweet link\n✓  cool tests     1m26s  sweet link\n✓  rad tests      1m26s  sweet link\n\x1b[?1049lAll checks were successful\n0 failing, 3 successful, 0 skipped, and 0 pending checks\n\n✓  awesome tests  1m26s  sweet link\n✓  cool tests     1m26s  sweet link\n✓  rad tests      1m26s  sweet link\n",
   190  			wantErr: "",
   191  		},
   192  		{
   193  			name: "with statuses",
   194  			tty:  true,
   195  			httpStubs: func(reg *httpmock.Registry) {
   196  				reg.Register(
   197  					httpmock.GraphQL(`query PullRequestStatusChecks\b`),
   198  					httpmock.FileResponse("./fixtures/withStatuses.json"),
   199  				)
   200  			},
   201  			wantOut: "Some checks were not successful\n1 failing, 2 successful, 0 skipped, and 0 pending checks\n\nX  a status           sweet link\n✓  cool tests  1m26s  sweet link\n✓  rad tests   1m26s  sweet link\n",
   202  			wantErr: "SilentError",
   203  		},
   204  		{
   205  			name: "no checks",
   206  			httpStubs: func(reg *httpmock.Registry) {
   207  				reg.Register(
   208  					httpmock.GraphQL(`query PullRequestStatusChecks\b`),
   209  					httpmock.StringResponse(`{"data":{"node":{"statusCheckRollup":{"nodes":[{"commit":{"oid": "abc"}}]}}}}`),
   210  				)
   211  			},
   212  			wantOut: "",
   213  			wantErr: "no checks reported on the 'trunk' branch",
   214  		},
   215  		{
   216  			name: "some failing",
   217  			httpStubs: func(reg *httpmock.Registry) {
   218  				reg.Register(
   219  					httpmock.GraphQL(`query PullRequestStatusChecks\b`),
   220  					httpmock.FileResponse("./fixtures/someFailing.json"),
   221  				)
   222  			},
   223  			wantOut: "sad tests\tfail\t1m26s\tsweet link\ncool tests\tpass\t1m26s\tsweet link\nslow tests\tpending\t1m26s\tsweet link\n",
   224  			wantErr: "SilentError",
   225  		},
   226  		{
   227  			name: "some pending",
   228  			httpStubs: func(reg *httpmock.Registry) {
   229  				reg.Register(
   230  					httpmock.GraphQL(`query PullRequestStatusChecks\b`),
   231  					httpmock.FileResponse("./fixtures/somePending.json"),
   232  				)
   233  			},
   234  			wantOut: "cool tests\tpass\t1m26s\tsweet link\nrad tests\tpass\t1m26s\tsweet link\nslow tests\tpending\t1m26s\tsweet link\n",
   235  			wantErr: "SilentError",
   236  		},
   237  		{
   238  			name: "all passing",
   239  			httpStubs: func(reg *httpmock.Registry) {
   240  				reg.Register(
   241  					httpmock.GraphQL(`query PullRequestStatusChecks\b`),
   242  					httpmock.FileResponse("./fixtures/allPassing.json"),
   243  				)
   244  			},
   245  			wantOut: "awesome tests\tpass\t1m26s\tsweet link\ncool tests\tpass\t1m26s\tsweet link\nrad tests\tpass\t1m26s\tsweet link\n",
   246  			wantErr: "",
   247  		},
   248  		{
   249  			name: "with statuses",
   250  			httpStubs: func(reg *httpmock.Registry) {
   251  				reg.Register(
   252  					httpmock.GraphQL(`query PullRequestStatusChecks\b`),
   253  					httpmock.FileResponse("./fixtures/withStatuses.json"),
   254  				)
   255  			},
   256  			wantOut: "a status\tfail\t0\tsweet link\ncool tests\tpass\t1m26s\tsweet link\nrad tests\tpass\t1m26s\tsweet link\n",
   257  			wantErr: "SilentError",
   258  		},
   259  		{
   260  			name: "some skipped",
   261  			httpStubs: func(reg *httpmock.Registry) {
   262  				reg.Register(
   263  					httpmock.GraphQL(`query PullRequestStatusChecks\b`),
   264  					httpmock.FileResponse("./fixtures/someSkipping.json"),
   265  				)
   266  			},
   267  			tty:     true,
   268  			wantOut: "All checks were successful\n0 failing, 1 successful, 2 skipped, and 0 pending checks\n\n✓  cool tests  1m26s  sweet link\n-  rad tests   1m26s  sweet link\n-  skip tests  1m26s  sweet link\n",
   269  			wantErr: "",
   270  		},
   271  		{
   272  			name: "only required",
   273  			httpStubs: func(reg *httpmock.Registry) {
   274  				reg.Register(
   275  					httpmock.GraphQL(`query PullRequestStatusChecks\b`),
   276  					httpmock.FileResponse("./fixtures/onlyRequired.json"),
   277  				)
   278  			},
   279  			tty:      true,
   280  			wantOut:  "All checks were successful\n0 failing, 1 successful, 0 skipped, and 0 pending checks\n\n✓  cool tests  1m26s  sweet link\n",
   281  			wantErr:  "",
   282  			required: true,
   283  		},
   284  		{
   285  			name: "no required checks",
   286  			httpStubs: func(reg *httpmock.Registry) {
   287  				reg.Register(
   288  					httpmock.GraphQL(`query PullRequestStatusChecks\b`),
   289  					httpmock.FileResponse("./fixtures/someSkipping.json"),
   290  				)
   291  			},
   292  			wantOut:  "",
   293  			wantErr:  "no required checks reported on the 'trunk' branch",
   294  			required: true,
   295  		},
   296  	}
   297  
   298  	for _, tt := range tests {
   299  		t.Run(tt.name, func(t *testing.T) {
   300  			ios, _, stdout, _ := iostreams.Test()
   301  			ios.SetStdoutTTY(tt.tty)
   302  			ios.SetAlternateScreenBufferEnabled(tt.tty)
   303  
   304  			reg := &httpmock.Registry{}
   305  			defer reg.Verify(t)
   306  			if tt.httpStubs != nil {
   307  				tt.httpStubs(reg)
   308  			}
   309  
   310  			response := &api.PullRequest{Number: 123, HeadRefName: "trunk"}
   311  			opts := &ChecksOptions{
   312  				HttpClient: func() (*http.Client, error) {
   313  					return &http.Client{Transport: reg}, nil
   314  				},
   315  				IO:          ios,
   316  				SelectorArg: "123",
   317  				Finder:      shared.NewMockFinder("123", response, ghrepo.New("OWNER", "REPO")),
   318  				Watch:       tt.watch,
   319  				Required:    tt.required,
   320  			}
   321  
   322  			err := checksRun(opts)
   323  			if tt.wantErr != "" {
   324  				assert.EqualError(t, err, tt.wantErr)
   325  			} else {
   326  				assert.NoError(t, err)
   327  			}
   328  
   329  			assert.Equal(t, tt.wantOut, stdout.String())
   330  		})
   331  	}
   332  }
   333  
   334  func TestChecksRun_web(t *testing.T) {
   335  	tests := []struct {
   336  		name       string
   337  		isTTY      bool
   338  		wantStderr string
   339  		wantStdout string
   340  		wantBrowse string
   341  	}{
   342  		{
   343  			name:       "tty",
   344  			isTTY:      true,
   345  			wantStderr: "Opening github.com/OWNER/REPO/pull/123/checks in your browser.\n",
   346  			wantStdout: "",
   347  			wantBrowse: "https://github.com/OWNER/REPO/pull/123/checks",
   348  		},
   349  		{
   350  			name:       "nontty",
   351  			isTTY:      false,
   352  			wantStderr: "",
   353  			wantStdout: "",
   354  			wantBrowse: "https://github.com/OWNER/REPO/pull/123/checks",
   355  		},
   356  	}
   357  	for _, tc := range tests {
   358  		t.Run(tc.name, func(t *testing.T) {
   359  			browser := &browser.Stub{}
   360  
   361  			ios, _, stdout, stderr := iostreams.Test()
   362  			ios.SetStdoutTTY(tc.isTTY)
   363  			ios.SetStdinTTY(tc.isTTY)
   364  			ios.SetStderrTTY(tc.isTTY)
   365  
   366  			_, teardown := run.Stub()
   367  			defer teardown(t)
   368  
   369  			err := checksRunWebMode(&ChecksOptions{
   370  				IO:          ios,
   371  				Browser:     browser,
   372  				WebMode:     true,
   373  				SelectorArg: "123",
   374  				Finder:      shared.NewMockFinder("123", &api.PullRequest{Number: 123}, ghrepo.New("OWNER", "REPO")),
   375  			})
   376  			assert.NoError(t, err)
   377  			assert.Equal(t, tc.wantStdout, stdout.String())
   378  			assert.Equal(t, tc.wantStderr, stderr.String())
   379  			browser.Verify(t, tc.wantBrowse)
   380  		})
   381  	}
   382  }
   383  
   384  func TestEliminateDupulicates(t *testing.T) {
   385  	tests := []struct {
   386  		name          string
   387  		checkContexts []api.CheckContext
   388  		want          []api.CheckContext
   389  	}{
   390  		{
   391  			name: "duplicate CheckRun (lint)",
   392  			checkContexts: []api.CheckContext{
   393  				{
   394  					TypeName:    "CheckRun",
   395  					Name:        "build (ubuntu-latest)",
   396  					Status:      "COMPLETED",
   397  					Conclusion:  "SUCCESS",
   398  					StartedAt:   time.Date(2022, 1, 1, 1, 1, 1, 1, time.UTC),
   399  					CompletedAt: time.Date(2022, 1, 1, 1, 1, 1, 1, time.UTC),
   400  					DetailsURL:  "https://github.com/ungtb10d/cli/runs/1",
   401  				},
   402  				{
   403  					TypeName:    "CheckRun",
   404  					Name:        "lint",
   405  					Status:      "COMPLETED",
   406  					Conclusion:  "FAILURE",
   407  					StartedAt:   time.Date(2022, 1, 1, 1, 1, 1, 1, time.UTC),
   408  					CompletedAt: time.Date(2022, 1, 1, 1, 1, 1, 1, time.UTC),
   409  					DetailsURL:  "https://github.com/ungtb10d/cli/runs/2",
   410  				},
   411  				{
   412  					TypeName:    "CheckRun",
   413  					Name:        "lint",
   414  					Status:      "COMPLETED",
   415  					Conclusion:  "SUCCESS",
   416  					StartedAt:   time.Date(2022, 2, 2, 2, 2, 2, 2, time.UTC),
   417  					CompletedAt: time.Date(2022, 2, 2, 2, 2, 2, 2, time.UTC),
   418  					DetailsURL:  "https://github.com/ungtb10d/cli/runs/3",
   419  				},
   420  			},
   421  			want: []api.CheckContext{
   422  				{
   423  					TypeName:    "CheckRun",
   424  					Name:        "lint",
   425  					Status:      "COMPLETED",
   426  					Conclusion:  "SUCCESS",
   427  					StartedAt:   time.Date(2022, 2, 2, 2, 2, 2, 2, time.UTC),
   428  					CompletedAt: time.Date(2022, 2, 2, 2, 2, 2, 2, time.UTC),
   429  					DetailsURL:  "https://github.com/ungtb10d/cli/runs/3",
   430  				},
   431  				{
   432  					TypeName:    "CheckRun",
   433  					Name:        "build (ubuntu-latest)",
   434  					Status:      "COMPLETED",
   435  					Conclusion:  "SUCCESS",
   436  					StartedAt:   time.Date(2022, 1, 1, 1, 1, 1, 1, time.UTC),
   437  					CompletedAt: time.Date(2022, 1, 1, 1, 1, 1, 1, time.UTC),
   438  					DetailsURL:  "https://github.com/ungtb10d/cli/runs/1",
   439  				},
   440  			},
   441  		},
   442  		{
   443  			name: "duplicate StatusContext (Windows GPU)",
   444  			checkContexts: []api.CheckContext{
   445  				{
   446  					TypeName:    "StatusContext",
   447  					Name:        "",
   448  					Context:     "Windows GPU",
   449  					State:       "FAILURE",
   450  					Status:      "",
   451  					Conclusion:  "",
   452  					StartedAt:   time.Date(2022, 1, 1, 1, 1, 1, 1, time.UTC),
   453  					CompletedAt: time.Date(2022, 1, 1, 1, 1, 1, 1, time.UTC),
   454  					DetailsURL:  "",
   455  					TargetURL:   "https://github.com/ungtb10d/cli/2",
   456  				},
   457  				{
   458  					TypeName:    "StatusContext",
   459  					Name:        "",
   460  					Context:     "Windows GPU",
   461  					State:       "SUCCESS",
   462  					Status:      "",
   463  					Conclusion:  "",
   464  					StartedAt:   time.Date(2022, 2, 2, 2, 2, 2, 2, time.UTC),
   465  					CompletedAt: time.Date(2022, 2, 2, 2, 2, 2, 2, time.UTC),
   466  					DetailsURL:  "",
   467  					TargetURL:   "https://github.com/ungtb10d/cli/3",
   468  				},
   469  				{
   470  					TypeName:    "StatusContext",
   471  					Name:        "",
   472  					Context:     "Linux GPU",
   473  					State:       "SUCCESS",
   474  					Status:      "",
   475  					Conclusion:  "",
   476  					StartedAt:   time.Date(2022, 1, 1, 1, 1, 1, 1, time.UTC),
   477  					CompletedAt: time.Date(2022, 1, 1, 1, 1, 1, 1, time.UTC),
   478  					DetailsURL:  "",
   479  					TargetURL:   "https://github.com/ungtb10d/cli/1",
   480  				},
   481  			},
   482  			want: []api.CheckContext{
   483  				{
   484  					TypeName:    "StatusContext",
   485  					Name:        "",
   486  					Context:     "Windows GPU",
   487  					State:       "SUCCESS",
   488  					Status:      "",
   489  					Conclusion:  "",
   490  					StartedAt:   time.Date(2022, 2, 2, 2, 2, 2, 2, time.UTC),
   491  					CompletedAt: time.Date(2022, 2, 2, 2, 2, 2, 2, time.UTC),
   492  					DetailsURL:  "",
   493  					TargetURL:   "https://github.com/ungtb10d/cli/3",
   494  				},
   495  				{
   496  					TypeName:    "StatusContext",
   497  					Name:        "",
   498  					Context:     "Linux GPU",
   499  					State:       "SUCCESS",
   500  					Status:      "",
   501  					Conclusion:  "",
   502  					StartedAt:   time.Date(2022, 1, 1, 1, 1, 1, 1, time.UTC),
   503  					CompletedAt: time.Date(2022, 1, 1, 1, 1, 1, 1, time.UTC),
   504  					DetailsURL:  "",
   505  					TargetURL:   "https://github.com/ungtb10d/cli/1",
   506  				},
   507  			},
   508  		},
   509  		{
   510  			name: "unique CheckContext",
   511  			checkContexts: []api.CheckContext{
   512  				{
   513  					TypeName:    "CheckRun",
   514  					Name:        "build (ubuntu-latest)",
   515  					Status:      "COMPLETED",
   516  					Conclusion:  "SUCCESS",
   517  					StartedAt:   time.Date(2022, 1, 1, 1, 1, 1, 1, time.UTC),
   518  					CompletedAt: time.Date(2022, 1, 1, 1, 1, 1, 1, time.UTC),
   519  					DetailsURL:  "https://github.com/ungtb10d/cli/runs/1",
   520  				},
   521  				{
   522  					TypeName:    "StatusContext",
   523  					Name:        "",
   524  					Context:     "Windows GPU",
   525  					State:       "SUCCESS",
   526  					Status:      "",
   527  					Conclusion:  "",
   528  					StartedAt:   time.Date(2022, 1, 1, 1, 1, 1, 1, time.UTC),
   529  					CompletedAt: time.Date(2022, 1, 1, 1, 1, 1, 1, time.UTC),
   530  					DetailsURL:  "",
   531  					TargetURL:   "https://github.com/ungtb10d/cli/2",
   532  				},
   533  				{
   534  					TypeName:    "StatusContext",
   535  					Name:        "",
   536  					Context:     "Linux GPU",
   537  					State:       "SUCCESS",
   538  					Status:      "",
   539  					Conclusion:  "",
   540  					StartedAt:   time.Date(2022, 1, 1, 1, 1, 1, 1, time.UTC),
   541  					CompletedAt: time.Date(2022, 1, 1, 1, 1, 1, 1, time.UTC),
   542  					DetailsURL:  "",
   543  					TargetURL:   "https://github.com/ungtb10d/cli/3",
   544  				},
   545  			},
   546  			want: []api.CheckContext{
   547  				{
   548  					TypeName:    "CheckRun",
   549  					Name:        "build (ubuntu-latest)",
   550  					Status:      "COMPLETED",
   551  					Conclusion:  "SUCCESS",
   552  					StartedAt:   time.Date(2022, 1, 1, 1, 1, 1, 1, time.UTC),
   553  					CompletedAt: time.Date(2022, 1, 1, 1, 1, 1, 1, time.UTC),
   554  					DetailsURL:  "https://github.com/ungtb10d/cli/runs/1",
   555  				},
   556  				{
   557  					TypeName:    "StatusContext",
   558  					Name:        "",
   559  					Context:     "Windows GPU",
   560  					State:       "SUCCESS",
   561  					Status:      "",
   562  					Conclusion:  "",
   563  					StartedAt:   time.Date(2022, 1, 1, 1, 1, 1, 1, time.UTC),
   564  					CompletedAt: time.Date(2022, 1, 1, 1, 1, 1, 1, time.UTC),
   565  					DetailsURL:  "",
   566  					TargetURL:   "https://github.com/ungtb10d/cli/2",
   567  				},
   568  				{
   569  					TypeName:    "StatusContext",
   570  					Name:        "",
   571  					Context:     "Linux GPU",
   572  					State:       "SUCCESS",
   573  					Status:      "",
   574  					Conclusion:  "",
   575  					StartedAt:   time.Date(2022, 1, 1, 1, 1, 1, 1, time.UTC),
   576  					CompletedAt: time.Date(2022, 1, 1, 1, 1, 1, 1, time.UTC),
   577  					DetailsURL:  "",
   578  					TargetURL:   "https://github.com/ungtb10d/cli/3",
   579  				},
   580  			},
   581  		},
   582  	}
   583  
   584  	for _, tt := range tests {
   585  		t.Run(tt.name, func(t *testing.T) {
   586  			got := eliminateDuplicates(tt.checkContexts)
   587  			if !reflect.DeepEqual(tt.want, got) {
   588  				t.Errorf("got eliminateDuplicates %+v, want %+v\n", got, tt.want)
   589  			}
   590  		})
   591  	}
   592  }