github.com/google/go-github/v71@v71.0.0/github/actions_workflow_jobs_test.go (about)

     1  // Copyright 2020 The go-github AUTHORS. All rights reserved.
     2  //
     3  // Use of this source code is governed by a BSD-style
     4  // license that can be found in the LICENSE file.
     5  
     6  package github
     7  
     8  import (
     9  	"context"
    10  	"errors"
    11  	"fmt"
    12  	"net/http"
    13  	"net/url"
    14  	"strings"
    15  	"testing"
    16  	"time"
    17  
    18  	"github.com/google/go-cmp/cmp"
    19  )
    20  
    21  func TestActionsService_ListWorkflowJobs(t *testing.T) {
    22  	t.Parallel()
    23  	client, mux, _ := setup(t)
    24  
    25  	mux.HandleFunc("/repos/o/r/actions/runs/29679449/jobs", func(w http.ResponseWriter, r *http.Request) {
    26  		testMethod(t, r, "GET")
    27  		testFormValues(t, r, values{"per_page": "2", "page": "2"})
    28  		fmt.Fprint(w, `{"total_count":4,"jobs":[{"id":399444496,"run_id":29679449,"started_at":"2019-01-02T15:04:05Z","completed_at":"2020-01-02T15:04:05Z"},{"id":399444497,"run_id":29679449,"started_at":"2019-01-02T15:04:05Z","completed_at":"2020-01-02T15:04:05Z"}]}`)
    29  	})
    30  
    31  	opts := &ListWorkflowJobsOptions{ListOptions: ListOptions{Page: 2, PerPage: 2}}
    32  	ctx := context.Background()
    33  	jobs, _, err := client.Actions.ListWorkflowJobs(ctx, "o", "r", 29679449, opts)
    34  	if err != nil {
    35  		t.Errorf("Actions.ListWorkflowJobs returned error: %v", err)
    36  	}
    37  
    38  	want := &Jobs{
    39  		TotalCount: Ptr(4),
    40  		Jobs: []*WorkflowJob{
    41  			{ID: Ptr(int64(399444496)), RunID: Ptr(int64(29679449)), StartedAt: &Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)}, CompletedAt: &Timestamp{time.Date(2020, time.January, 02, 15, 04, 05, 0, time.UTC)}},
    42  			{ID: Ptr(int64(399444497)), RunID: Ptr(int64(29679449)), StartedAt: &Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)}, CompletedAt: &Timestamp{time.Date(2020, time.January, 02, 15, 04, 05, 0, time.UTC)}},
    43  		},
    44  	}
    45  	if !cmp.Equal(jobs, want) {
    46  		t.Errorf("Actions.ListWorkflowJobs returned %+v, want %+v", jobs, want)
    47  	}
    48  
    49  	const methodName = "ListWorkflowJobs"
    50  	testBadOptions(t, methodName, func() (err error) {
    51  		_, _, err = client.Actions.ListWorkflowJobs(ctx, "\n", "\n", 29679449, opts)
    52  		return err
    53  	})
    54  
    55  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
    56  		got, resp, err := client.Actions.ListWorkflowJobs(ctx, "o", "r", 29679449, opts)
    57  		if got != nil {
    58  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
    59  		}
    60  		return resp, err
    61  	})
    62  }
    63  
    64  func TestActionsService_ListWorkflowJobs_Filter(t *testing.T) {
    65  	t.Parallel()
    66  	client, mux, _ := setup(t)
    67  
    68  	mux.HandleFunc("/repos/o/r/actions/runs/29679449/jobs", func(w http.ResponseWriter, r *http.Request) {
    69  		testMethod(t, r, "GET")
    70  		testFormValues(t, r, values{"filter": "all", "per_page": "2", "page": "2"})
    71  		fmt.Fprint(w, `{"total_count":4,"jobs":[{"id":399444496,"run_id":29679449,"started_at":"2019-01-02T15:04:05Z","completed_at":"2020-01-02T15:04:05Z"},{"id":399444497,"run_id":29679449,"started_at":"2019-01-02T15:04:05Z","completed_at":"2020-01-02T15:04:05Z"}]}`)
    72  	})
    73  
    74  	opts := &ListWorkflowJobsOptions{Filter: "all", ListOptions: ListOptions{Page: 2, PerPage: 2}}
    75  	ctx := context.Background()
    76  	jobs, _, err := client.Actions.ListWorkflowJobs(ctx, "o", "r", 29679449, opts)
    77  	if err != nil {
    78  		t.Errorf("Actions.ListWorkflowJobs returned error: %v", err)
    79  	}
    80  
    81  	want := &Jobs{
    82  		TotalCount: Ptr(4),
    83  		Jobs: []*WorkflowJob{
    84  			{ID: Ptr(int64(399444496)), RunID: Ptr(int64(29679449)), StartedAt: &Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)}, CompletedAt: &Timestamp{time.Date(2020, time.January, 02, 15, 04, 05, 0, time.UTC)}},
    85  			{ID: Ptr(int64(399444497)), RunID: Ptr(int64(29679449)), StartedAt: &Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)}, CompletedAt: &Timestamp{time.Date(2020, time.January, 02, 15, 04, 05, 0, time.UTC)}},
    86  		},
    87  	}
    88  	if !cmp.Equal(jobs, want) {
    89  		t.Errorf("Actions.ListWorkflowJobs returned %+v, want %+v", jobs, want)
    90  	}
    91  }
    92  
    93  func TestActionsService_ListWorkflowJobsAttempt(t *testing.T) {
    94  	t.Parallel()
    95  	client, mux, _ := setup(t)
    96  
    97  	mux.HandleFunc("/repos/o/r/actions/runs/29679449/attempts/1/jobs", func(w http.ResponseWriter, r *http.Request) {
    98  		testMethod(t, r, "GET")
    99  		testFormValues(t, r, values{"per_page": "2", "page": "2"})
   100  		fmt.Fprint(w, `{"total_count":4,"jobs":[{"id":399444496,"run_id":29679449,"started_at":"2019-01-02T15:04:05Z","completed_at":"2020-01-02T15:04:05Z","run_attempt":2},{"id":399444497,"run_id":29679449,"started_at":"2019-01-02T15:04:05Z","completed_at":"2020-01-02T15:04:05Z","run_attempt":2}]}`)
   101  	})
   102  	opts := &ListOptions{Page: 2, PerPage: 2}
   103  	ctx := context.Background()
   104  	jobs, _, err := client.Actions.ListWorkflowJobsAttempt(ctx, "o", "r", 29679449, 1, opts)
   105  	if err != nil {
   106  		t.Errorf("Actions.ListWorkflowJobsAttempt returned error: %v", err)
   107  	}
   108  
   109  	want := &Jobs{
   110  		TotalCount: Ptr(4),
   111  		Jobs: []*WorkflowJob{
   112  			{
   113  				ID:          Ptr(int64(399444496)),
   114  				RunID:       Ptr(int64(29679449)),
   115  				StartedAt:   &Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)},
   116  				CompletedAt: &Timestamp{time.Date(2020, time.January, 02, 15, 04, 05, 0, time.UTC)},
   117  				RunAttempt:  Ptr(int64(2)),
   118  			},
   119  			{
   120  				ID:          Ptr(int64(399444497)),
   121  				RunID:       Ptr(int64(29679449)),
   122  				StartedAt:   &Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)},
   123  				CompletedAt: &Timestamp{time.Date(2020, time.January, 02, 15, 04, 05, 0, time.UTC)},
   124  				RunAttempt:  Ptr(int64(2)),
   125  			},
   126  		},
   127  	}
   128  	if !cmp.Equal(jobs, want) {
   129  		t.Errorf("Actions.ListWorkflowJobsAttempt returned %+v, want %+v", jobs, want)
   130  	}
   131  
   132  	const methodName = "ListWorkflowJobsAttempt"
   133  	testBadOptions(t, methodName, func() (err error) {
   134  		_, _, err = client.Actions.ListWorkflowJobsAttempt(ctx, "\n", "\n", 29679449, 1, opts)
   135  		return err
   136  	})
   137  
   138  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   139  		got, resp, err := client.Actions.ListWorkflowJobsAttempt(ctx, "o", "r", 29679449, 1, opts)
   140  		if got != nil {
   141  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   142  		}
   143  		return resp, err
   144  	})
   145  }
   146  
   147  func TestActionsService_GetWorkflowJobByID(t *testing.T) {
   148  	t.Parallel()
   149  	client, mux, _ := setup(t)
   150  
   151  	mux.HandleFunc("/repos/o/r/actions/jobs/399444496", func(w http.ResponseWriter, r *http.Request) {
   152  		testMethod(t, r, "GET")
   153  		fmt.Fprint(w, `{"id":399444496,"started_at":"2019-01-02T15:04:05Z","completed_at":"2020-01-02T15:04:05Z"}`)
   154  	})
   155  
   156  	ctx := context.Background()
   157  	job, _, err := client.Actions.GetWorkflowJobByID(ctx, "o", "r", 399444496)
   158  	if err != nil {
   159  		t.Errorf("Actions.GetWorkflowJobByID returned error: %v", err)
   160  	}
   161  
   162  	want := &WorkflowJob{
   163  		ID:          Ptr(int64(399444496)),
   164  		StartedAt:   &Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)},
   165  		CompletedAt: &Timestamp{time.Date(2020, time.January, 02, 15, 04, 05, 0, time.UTC)},
   166  	}
   167  	if !cmp.Equal(job, want) {
   168  		t.Errorf("Actions.GetWorkflowJobByID returned %+v, want %+v", job, want)
   169  	}
   170  
   171  	const methodName = "GetWorkflowJobByID"
   172  	testBadOptions(t, methodName, func() (err error) {
   173  		_, _, err = client.Actions.GetWorkflowJobByID(ctx, "\n", "\n", 399444496)
   174  		return err
   175  	})
   176  
   177  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   178  		got, resp, err := client.Actions.GetWorkflowJobByID(ctx, "o", "r", 399444496)
   179  		if got != nil {
   180  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   181  		}
   182  		return resp, err
   183  	})
   184  }
   185  
   186  func TestActionsService_GetWorkflowJobLogs(t *testing.T) {
   187  	t.Parallel()
   188  	tcs := []struct {
   189  		name              string
   190  		respectRateLimits bool
   191  	}{
   192  		{
   193  			name:              "withoutRateLimits",
   194  			respectRateLimits: false,
   195  		},
   196  		{
   197  			name:              "withRateLimits",
   198  			respectRateLimits: true,
   199  		},
   200  	}
   201  
   202  	for _, tc := range tcs {
   203  		t.Run(tc.name, func(t *testing.T) {
   204  			t.Parallel()
   205  			client, mux, _ := setup(t)
   206  			client.RateLimitRedirectionalEndpoints = tc.respectRateLimits
   207  
   208  			mux.HandleFunc("/repos/o/r/actions/jobs/399444496/logs", func(w http.ResponseWriter, r *http.Request) {
   209  				testMethod(t, r, "GET")
   210  				http.Redirect(w, r, "http://github.com/a", http.StatusFound)
   211  			})
   212  
   213  			ctx := context.Background()
   214  			url, resp, err := client.Actions.GetWorkflowJobLogs(ctx, "o", "r", 399444496, 1)
   215  			if err != nil {
   216  				t.Errorf("Actions.GetWorkflowJobLogs returned error: %v", err)
   217  			}
   218  			if resp.StatusCode != http.StatusFound {
   219  				t.Errorf("Actions.GetWorkflowJobLogs returned status: %d, want %d", resp.StatusCode, http.StatusFound)
   220  			}
   221  			want := "http://github.com/a"
   222  			if url.String() != want {
   223  				t.Errorf("Actions.GetWorkflowJobLogs returned %+v, want %+v", url.String(), want)
   224  			}
   225  
   226  			const methodName = "GetWorkflowJobLogs"
   227  			testBadOptions(t, methodName, func() (err error) {
   228  				_, _, err = client.Actions.GetWorkflowJobLogs(ctx, "\n", "\n", 399444496, 1)
   229  				return err
   230  			})
   231  
   232  			// Add custom round tripper
   233  			client.client.Transport = roundTripperFunc(func(r *http.Request) (*http.Response, error) {
   234  				return nil, errors.New("failed to get workflow logs")
   235  			})
   236  			// propagate custom round tripper to client without CheckRedirect
   237  			client.initialize()
   238  			testBadOptions(t, methodName, func() (err error) {
   239  				_, _, err = client.Actions.GetWorkflowJobLogs(ctx, "o", "r", 399444496, 1)
   240  				return err
   241  			})
   242  		})
   243  	}
   244  }
   245  
   246  func TestActionsService_GetWorkflowJobLogs_StatusMovedPermanently_dontFollowRedirects(t *testing.T) {
   247  	t.Parallel()
   248  	tcs := []struct {
   249  		name              string
   250  		respectRateLimits bool
   251  	}{
   252  		{
   253  			name:              "withoutRateLimits",
   254  			respectRateLimits: false,
   255  		},
   256  		{
   257  			name:              "withRateLimits",
   258  			respectRateLimits: true,
   259  		},
   260  	}
   261  
   262  	for _, tc := range tcs {
   263  		t.Run(tc.name, func(t *testing.T) {
   264  			t.Parallel()
   265  			client, mux, _ := setup(t)
   266  			client.RateLimitRedirectionalEndpoints = tc.respectRateLimits
   267  
   268  			mux.HandleFunc("/repos/o/r/actions/jobs/399444496/logs", func(w http.ResponseWriter, r *http.Request) {
   269  				testMethod(t, r, "GET")
   270  				http.Redirect(w, r, "http://github.com/a", http.StatusMovedPermanently)
   271  			})
   272  
   273  			ctx := context.Background()
   274  			_, resp, _ := client.Actions.GetWorkflowJobLogs(ctx, "o", "r", 399444496, 0)
   275  			if resp.StatusCode != http.StatusMovedPermanently {
   276  				t.Errorf("Actions.GetWorkflowJobLogs returned status: %d, want %d", resp.StatusCode, http.StatusMovedPermanently)
   277  			}
   278  		})
   279  	}
   280  }
   281  
   282  func TestActionsService_GetWorkflowJobLogs_StatusMovedPermanently_followRedirects(t *testing.T) {
   283  	t.Parallel()
   284  	tcs := []struct {
   285  		name              string
   286  		respectRateLimits bool
   287  	}{
   288  		{
   289  			name:              "withoutRateLimits",
   290  			respectRateLimits: false,
   291  		},
   292  		{
   293  			name:              "withRateLimits",
   294  			respectRateLimits: true,
   295  		},
   296  	}
   297  
   298  	for _, tc := range tcs {
   299  		t.Run(tc.name, func(t *testing.T) {
   300  			t.Parallel()
   301  			client, mux, serverURL := setup(t)
   302  			client.RateLimitRedirectionalEndpoints = tc.respectRateLimits
   303  
   304  			// Mock a redirect link, which leads to an archive link
   305  			mux.HandleFunc("/repos/o/r/actions/jobs/399444496/logs", func(w http.ResponseWriter, r *http.Request) {
   306  				testMethod(t, r, "GET")
   307  				redirectURL, _ := url.Parse(serverURL + baseURLPath + "/redirect")
   308  				http.Redirect(w, r, redirectURL.String(), http.StatusMovedPermanently)
   309  			})
   310  
   311  			mux.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) {
   312  				testMethod(t, r, "GET")
   313  				http.Redirect(w, r, "http://github.com/a", http.StatusFound)
   314  			})
   315  
   316  			ctx := context.Background()
   317  			url, resp, err := client.Actions.GetWorkflowJobLogs(ctx, "o", "r", 399444496, 1)
   318  			if err != nil {
   319  				t.Errorf("Actions.GetWorkflowJobLogs returned error: %v", err)
   320  			}
   321  
   322  			if resp.StatusCode != http.StatusFound {
   323  				t.Errorf("Actions.GetWorkflowJobLogs returned status: %d, want %d", resp.StatusCode, http.StatusFound)
   324  			}
   325  
   326  			want := "http://github.com/a"
   327  			if url.String() != want {
   328  				t.Errorf("Actions.GetWorkflowJobLogs returned %+v, want %+v", url.String(), want)
   329  			}
   330  		})
   331  	}
   332  }
   333  
   334  func TestActionsService_GetWorkflowJobLogs_unexpectedCode(t *testing.T) {
   335  	t.Parallel()
   336  	tcs := []struct {
   337  		name              string
   338  		respectRateLimits bool
   339  	}{
   340  		{
   341  			name:              "withoutRateLimits",
   342  			respectRateLimits: false,
   343  		},
   344  		{
   345  			name:              "withRateLimits",
   346  			respectRateLimits: true,
   347  		},
   348  	}
   349  
   350  	for _, tc := range tcs {
   351  		t.Run(tc.name, func(t *testing.T) {
   352  			t.Parallel()
   353  			client, mux, serverURL := setup(t)
   354  			client.RateLimitRedirectionalEndpoints = tc.respectRateLimits
   355  
   356  			// Mock a redirect link, which leads to an archive link
   357  			mux.HandleFunc("/repos/o/r/actions/jobs/399444496/logs", func(w http.ResponseWriter, r *http.Request) {
   358  				testMethod(t, r, "GET")
   359  				redirectURL, _ := url.Parse(serverURL + baseURLPath + "/redirect")
   360  				http.Redirect(w, r, redirectURL.String(), http.StatusMovedPermanently)
   361  			})
   362  
   363  			mux.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) {
   364  				testMethod(t, r, "GET")
   365  				w.WriteHeader(http.StatusNoContent)
   366  			})
   367  
   368  			ctx := context.Background()
   369  			url, resp, err := client.Actions.GetWorkflowJobLogs(ctx, "o", "r", 399444496, 1)
   370  			if err == nil {
   371  				t.Fatalf("Actions.GetWorkflowJobLogs should return error on unexpected code")
   372  			}
   373  			if !strings.Contains(err.Error(), "unexpected status code") {
   374  				t.Error("Actions.GetWorkflowJobLogs should return unexpected status code")
   375  			}
   376  			if got, want := resp.Response.StatusCode, http.StatusNoContent; got != want {
   377  				t.Errorf("Actions.GetWorkflowJobLogs return status %d, want %d", got, want)
   378  			}
   379  			if url != nil {
   380  				t.Errorf("Actions.GetWorkflowJobLogs return %+v, want nil", url)
   381  			}
   382  		})
   383  	}
   384  }
   385  
   386  func TestTaskStep_Marshal(t *testing.T) {
   387  	t.Parallel()
   388  	testJSONMarshal(t, &TaskStep{}, "{}")
   389  
   390  	u := &TaskStep{
   391  		Name:        Ptr("n"),
   392  		Status:      Ptr("s"),
   393  		Conclusion:  Ptr("c"),
   394  		Number:      Ptr(int64(1)),
   395  		StartedAt:   &Timestamp{referenceTime},
   396  		CompletedAt: &Timestamp{referenceTime},
   397  	}
   398  
   399  	want := `{
   400  		"name": "n",
   401  		"status": "s",
   402  		"conclusion": "c",
   403  		"number": 1,
   404  		"started_at": ` + referenceTimeStr + `,
   405  		"completed_at": ` + referenceTimeStr + `
   406  	}`
   407  
   408  	testJSONMarshal(t, u, want)
   409  }
   410  
   411  func TestWorkflowJob_Marshal(t *testing.T) {
   412  	t.Parallel()
   413  	testJSONMarshal(t, &WorkflowJob{}, "{}")
   414  
   415  	u := &WorkflowJob{
   416  		ID:          Ptr(int64(1)),
   417  		RunID:       Ptr(int64(1)),
   418  		RunURL:      Ptr("r"),
   419  		NodeID:      Ptr("n"),
   420  		HeadBranch:  Ptr("b"),
   421  		HeadSHA:     Ptr("h"),
   422  		URL:         Ptr("u"),
   423  		HTMLURL:     Ptr("h"),
   424  		Status:      Ptr("s"),
   425  		Conclusion:  Ptr("c"),
   426  		CreatedAt:   &Timestamp{referenceTime},
   427  		StartedAt:   &Timestamp{referenceTime},
   428  		CompletedAt: &Timestamp{referenceTime},
   429  		Name:        Ptr("n"),
   430  		Steps: []*TaskStep{
   431  			{
   432  				Name:        Ptr("n"),
   433  				Status:      Ptr("s"),
   434  				Conclusion:  Ptr("c"),
   435  				Number:      Ptr(int64(1)),
   436  				StartedAt:   &Timestamp{referenceTime},
   437  				CompletedAt: &Timestamp{referenceTime},
   438  			},
   439  		},
   440  		CheckRunURL:  Ptr("c"),
   441  		WorkflowName: Ptr("w"),
   442  	}
   443  
   444  	want := `{
   445  		"id": 1,
   446  		"run_id": 1,
   447  		"run_url": "r",
   448  		"node_id": "n",
   449  		"head_branch": "b",
   450  		"head_sha": "h",
   451  		"url": "u",
   452  		"html_url": "h",
   453  		"status": "s",
   454  		"conclusion": "c",
   455  		"created_at": ` + referenceTimeStr + `,
   456  		"started_at": ` + referenceTimeStr + `,
   457  		"completed_at": ` + referenceTimeStr + `,
   458  		"name": "n",
   459  		"steps": [{
   460  			"name": "n",
   461  			"status": "s",
   462  			"conclusion": "c",
   463  			"number": 1,
   464  			"started_at": ` + referenceTimeStr + `,
   465  			"completed_at": ` + referenceTimeStr + `
   466  		}],
   467  		"check_run_url": "c",
   468  		"workflow_name": "w"
   469  	}`
   470  
   471  	testJSONMarshal(t, u, want)
   472  }
   473  
   474  func TestJobs_Marshal(t *testing.T) {
   475  	t.Parallel()
   476  	testJSONMarshal(t, &Jobs{}, "{}")
   477  
   478  	u := &Jobs{
   479  		TotalCount: Ptr(1),
   480  		Jobs: []*WorkflowJob{
   481  			{
   482  				ID:          Ptr(int64(1)),
   483  				RunID:       Ptr(int64(1)),
   484  				RunURL:      Ptr("r"),
   485  				NodeID:      Ptr("n"),
   486  				HeadBranch:  Ptr("b"),
   487  				HeadSHA:     Ptr("h"),
   488  				URL:         Ptr("u"),
   489  				HTMLURL:     Ptr("h"),
   490  				Status:      Ptr("s"),
   491  				Conclusion:  Ptr("c"),
   492  				CreatedAt:   &Timestamp{referenceTime},
   493  				StartedAt:   &Timestamp{referenceTime},
   494  				CompletedAt: &Timestamp{referenceTime},
   495  				Name:        Ptr("n"),
   496  				Steps: []*TaskStep{
   497  					{
   498  						Name:        Ptr("n"),
   499  						Status:      Ptr("s"),
   500  						Conclusion:  Ptr("c"),
   501  						Number:      Ptr(int64(1)),
   502  						StartedAt:   &Timestamp{referenceTime},
   503  						CompletedAt: &Timestamp{referenceTime},
   504  					},
   505  				},
   506  				CheckRunURL:  Ptr("c"),
   507  				RunAttempt:   Ptr(int64(2)),
   508  				WorkflowName: Ptr("w"),
   509  			},
   510  		},
   511  	}
   512  
   513  	want := `{
   514  		"total_count": 1,
   515  		"jobs": [{
   516  			"id": 1,
   517  			"run_id": 1,
   518  			"run_url": "r",
   519  			"node_id": "n",
   520  			"head_branch": "b",
   521  			"head_sha": "h",
   522  			"url": "u",
   523  			"html_url": "h",
   524  			"status": "s",
   525  			"conclusion": "c",
   526  			"created_at": ` + referenceTimeStr + `,
   527  			"started_at": ` + referenceTimeStr + `,
   528  			"completed_at": ` + referenceTimeStr + `,
   529  			"name": "n",
   530  			"steps": [{
   531  				"name": "n",
   532  				"status": "s",
   533  				"conclusion": "c",
   534  				"number": 1,
   535  				"started_at": ` + referenceTimeStr + `,
   536  				"completed_at": ` + referenceTimeStr + `
   537  			}],
   538  			"check_run_url": "c",
   539  			"run_attempt": 2,
   540  			"workflow_name": "w"
   541  		}]
   542  	}`
   543  
   544  	testJSONMarshal(t, u, want)
   545  }