github.com/google/go-github/v71@v71.0.0/github/actions_artifacts_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  
    17  	"github.com/google/go-cmp/cmp"
    18  )
    19  
    20  func TestActionsService_ListArtifacts(t *testing.T) {
    21  	t.Parallel()
    22  	client, mux, _ := setup(t)
    23  
    24  	mux.HandleFunc("/repos/o/r/actions/artifacts", func(w http.ResponseWriter, r *http.Request) {
    25  		testMethod(t, r, "GET")
    26  		testFormValues(t, r, values{"page": "2", "name": "TheArtifact"})
    27  		fmt.Fprint(w,
    28  			`{
    29  				"total_count":1,
    30  				"artifacts":[{"id":1}]
    31  			}`,
    32  		)
    33  	})
    34  
    35  	opts := &ListArtifactsOptions{
    36  		Name:        Ptr("TheArtifact"),
    37  		ListOptions: ListOptions{Page: 2},
    38  	}
    39  	ctx := context.Background()
    40  	artifacts, _, err := client.Actions.ListArtifacts(ctx, "o", "r", opts)
    41  	if err != nil {
    42  		t.Errorf("Actions.ListArtifacts returned error: %v", err)
    43  	}
    44  
    45  	want := &ArtifactList{TotalCount: Ptr(int64(1)), Artifacts: []*Artifact{{ID: Ptr(int64(1))}}}
    46  	if !cmp.Equal(artifacts, want) {
    47  		t.Errorf("Actions.ListArtifacts returned %+v, want %+v", artifacts, want)
    48  	}
    49  
    50  	const methodName = "ListArtifacts"
    51  	testBadOptions(t, methodName, func() (err error) {
    52  		_, _, err = client.Actions.ListArtifacts(ctx, "\n", "\n", opts)
    53  		return err
    54  	})
    55  
    56  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
    57  		got, resp, err := client.Actions.ListArtifacts(ctx, "o", "r", opts)
    58  		if got != nil {
    59  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
    60  		}
    61  		return resp, err
    62  	})
    63  }
    64  
    65  func TestActionsService_ListArtifacts_invalidOwner(t *testing.T) {
    66  	t.Parallel()
    67  	client, _, _ := setup(t)
    68  
    69  	ctx := context.Background()
    70  	_, _, err := client.Actions.ListArtifacts(ctx, "%", "r", nil)
    71  	testURLParseError(t, err)
    72  }
    73  
    74  func TestActionsService_ListArtifacts_invalidRepo(t *testing.T) {
    75  	t.Parallel()
    76  	client, _, _ := setup(t)
    77  
    78  	ctx := context.Background()
    79  	_, _, err := client.Actions.ListArtifacts(ctx, "o", "%", nil)
    80  	testURLParseError(t, err)
    81  }
    82  
    83  func TestActionsService_ListArtifacts_notFound(t *testing.T) {
    84  	t.Parallel()
    85  	client, mux, _ := setup(t)
    86  
    87  	mux.HandleFunc("/repos/o/r/actions/artifacts", func(w http.ResponseWriter, r *http.Request) {
    88  		testMethod(t, r, "GET")
    89  		w.WriteHeader(http.StatusNotFound)
    90  	})
    91  
    92  	ctx := context.Background()
    93  	artifacts, resp, err := client.Actions.ListArtifacts(ctx, "o", "r", nil)
    94  	if err == nil {
    95  		t.Errorf("Expected HTTP 404 response")
    96  	}
    97  	if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want {
    98  		t.Errorf("Actions.ListArtifacts return status %d, want %d", got, want)
    99  	}
   100  	if artifacts != nil {
   101  		t.Errorf("Actions.ListArtifacts return %+v, want nil", artifacts)
   102  	}
   103  }
   104  
   105  func TestActionsService_ListWorkflowRunArtifacts(t *testing.T) {
   106  	t.Parallel()
   107  	client, mux, _ := setup(t)
   108  
   109  	mux.HandleFunc("/repos/o/r/actions/runs/1/artifacts", func(w http.ResponseWriter, r *http.Request) {
   110  		testMethod(t, r, "GET")
   111  		testFormValues(t, r, values{"page": "2"})
   112  		fmt.Fprint(w,
   113  			`{
   114  				"total_count":1,
   115  				"artifacts":[{"id":1}]
   116  			}`,
   117  		)
   118  	})
   119  
   120  	opts := &ListOptions{Page: 2}
   121  	ctx := context.Background()
   122  	artifacts, _, err := client.Actions.ListWorkflowRunArtifacts(ctx, "o", "r", 1, opts)
   123  	if err != nil {
   124  		t.Errorf("Actions.ListWorkflowRunArtifacts returned error: %v", err)
   125  	}
   126  
   127  	want := &ArtifactList{TotalCount: Ptr(int64(1)), Artifacts: []*Artifact{{ID: Ptr(int64(1))}}}
   128  	if !cmp.Equal(artifacts, want) {
   129  		t.Errorf("Actions.ListWorkflowRunArtifacts returned %+v, want %+v", artifacts, want)
   130  	}
   131  
   132  	const methodName = "ListWorkflowRunArtifacts"
   133  	testBadOptions(t, methodName, func() (err error) {
   134  		_, _, err = client.Actions.ListWorkflowRunArtifacts(ctx, "\n", "\n", -1, opts)
   135  		return err
   136  	})
   137  
   138  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   139  		got, resp, err := client.Actions.ListWorkflowRunArtifacts(ctx, "o", "r", 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_ListWorkflowRunArtifacts_invalidOwner(t *testing.T) {
   148  	t.Parallel()
   149  	client, _, _ := setup(t)
   150  
   151  	ctx := context.Background()
   152  	_, _, err := client.Actions.ListWorkflowRunArtifacts(ctx, "%", "r", 1, nil)
   153  	testURLParseError(t, err)
   154  }
   155  
   156  func TestActionsService_ListWorkflowRunArtifacts_invalidRepo(t *testing.T) {
   157  	t.Parallel()
   158  	client, _, _ := setup(t)
   159  
   160  	ctx := context.Background()
   161  	_, _, err := client.Actions.ListWorkflowRunArtifacts(ctx, "o", "%", 1, nil)
   162  	testURLParseError(t, err)
   163  }
   164  
   165  func TestActionsService_ListWorkflowRunArtifacts_notFound(t *testing.T) {
   166  	t.Parallel()
   167  	client, mux, _ := setup(t)
   168  
   169  	mux.HandleFunc("/repos/o/r/actions/runs/1/artifacts", func(w http.ResponseWriter, r *http.Request) {
   170  		testMethod(t, r, "GET")
   171  		w.WriteHeader(http.StatusNotFound)
   172  	})
   173  
   174  	ctx := context.Background()
   175  	artifacts, resp, err := client.Actions.ListWorkflowRunArtifacts(ctx, "o", "r", 1, nil)
   176  	if err == nil {
   177  		t.Errorf("Expected HTTP 404 response")
   178  	}
   179  	if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want {
   180  		t.Errorf("Actions.ListWorkflowRunArtifacts return status %d, want %d", got, want)
   181  	}
   182  	if artifacts != nil {
   183  		t.Errorf("Actions.ListWorkflowRunArtifacts return %+v, want nil", artifacts)
   184  	}
   185  }
   186  
   187  func TestActionsService_GetArtifact(t *testing.T) {
   188  	t.Parallel()
   189  	client, mux, _ := setup(t)
   190  
   191  	mux.HandleFunc("/repos/o/r/actions/artifacts/1", func(w http.ResponseWriter, r *http.Request) {
   192  		testMethod(t, r, "GET")
   193  		fmt.Fprint(w, `{
   194  			"id":1,
   195  			"node_id":"xyz",
   196  			"name":"a",
   197  			"size_in_bytes":5,
   198  			"archive_download_url":"u"
   199  		}`)
   200  	})
   201  
   202  	ctx := context.Background()
   203  	artifact, _, err := client.Actions.GetArtifact(ctx, "o", "r", 1)
   204  	if err != nil {
   205  		t.Errorf("Actions.GetArtifact returned error: %v", err)
   206  	}
   207  
   208  	want := &Artifact{
   209  		ID:                 Ptr(int64(1)),
   210  		NodeID:             Ptr("xyz"),
   211  		Name:               Ptr("a"),
   212  		SizeInBytes:        Ptr(int64(5)),
   213  		ArchiveDownloadURL: Ptr("u"),
   214  	}
   215  	if !cmp.Equal(artifact, want) {
   216  		t.Errorf("Actions.GetArtifact returned %+v, want %+v", artifact, want)
   217  	}
   218  
   219  	const methodName = "GetArtifact"
   220  	testBadOptions(t, methodName, func() (err error) {
   221  		_, _, err = client.Actions.GetArtifact(ctx, "\n", "\n", -1)
   222  		return err
   223  	})
   224  
   225  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   226  		got, resp, err := client.Actions.GetArtifact(ctx, "o", "r", 1)
   227  		if got != nil {
   228  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   229  		}
   230  		return resp, err
   231  	})
   232  }
   233  
   234  func TestActionsService_GetArtifact_invalidOwner(t *testing.T) {
   235  	t.Parallel()
   236  	client, _, _ := setup(t)
   237  
   238  	ctx := context.Background()
   239  	_, _, err := client.Actions.GetArtifact(ctx, "%", "r", 1)
   240  	testURLParseError(t, err)
   241  }
   242  
   243  func TestActionsService_GetArtifact_invalidRepo(t *testing.T) {
   244  	t.Parallel()
   245  	client, _, _ := setup(t)
   246  
   247  	ctx := context.Background()
   248  	_, _, err := client.Actions.GetArtifact(ctx, "o", "%", 1)
   249  	testURLParseError(t, err)
   250  }
   251  
   252  func TestActionsService_GetArtifact_notFound(t *testing.T) {
   253  	t.Parallel()
   254  	client, mux, _ := setup(t)
   255  
   256  	mux.HandleFunc("/repos/o/r/actions/artifacts/1", func(w http.ResponseWriter, r *http.Request) {
   257  		testMethod(t, r, "GET")
   258  		w.WriteHeader(http.StatusNotFound)
   259  	})
   260  
   261  	ctx := context.Background()
   262  	artifact, resp, err := client.Actions.GetArtifact(ctx, "o", "r", 1)
   263  	if err == nil {
   264  		t.Errorf("Expected HTTP 404 response")
   265  	}
   266  	if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want {
   267  		t.Errorf("Actions.GetArtifact return status %d, want %d", got, want)
   268  	}
   269  	if artifact != nil {
   270  		t.Errorf("Actions.GetArtifact return %+v, want nil", artifact)
   271  	}
   272  }
   273  
   274  func TestActionsService_DownloadArtifact(t *testing.T) {
   275  	t.Parallel()
   276  
   277  	tcs := []struct {
   278  		name              string
   279  		respectRateLimits bool
   280  	}{
   281  		{
   282  			name:              "withoutRateLimits",
   283  			respectRateLimits: false,
   284  		},
   285  		{
   286  			name:              "withRateLimits",
   287  			respectRateLimits: true,
   288  		},
   289  	}
   290  
   291  	for _, tc := range tcs {
   292  		t.Run(tc.name, func(t *testing.T) {
   293  			t.Parallel()
   294  			client, mux, _ := setup(t)
   295  			client.RateLimitRedirectionalEndpoints = tc.respectRateLimits
   296  
   297  			mux.HandleFunc("/repos/o/r/actions/artifacts/1/zip", func(w http.ResponseWriter, r *http.Request) {
   298  				testMethod(t, r, "GET")
   299  				http.Redirect(w, r, "https://github.com/artifact", http.StatusFound)
   300  			})
   301  
   302  			ctx := context.Background()
   303  			url, resp, err := client.Actions.DownloadArtifact(ctx, "o", "r", 1, 1)
   304  			if err != nil {
   305  				t.Errorf("Actions.DownloadArtifact returned error: %v", err)
   306  			}
   307  			if resp.StatusCode != http.StatusFound {
   308  				t.Errorf("Actions.DownloadArtifact returned status: %d, want %d", resp.StatusCode, http.StatusFound)
   309  			}
   310  
   311  			want := "https://github.com/artifact"
   312  			if url.String() != want {
   313  				t.Errorf("Actions.DownloadArtifact returned %+v, want %+v", url.String(), want)
   314  			}
   315  
   316  			const methodName = "DownloadArtifact"
   317  			testBadOptions(t, methodName, func() (err error) {
   318  				_, _, err = client.Actions.DownloadArtifact(ctx, "\n", "\n", -1, 1)
   319  				return err
   320  			})
   321  
   322  			// Add custom round tripper
   323  			client.client.Transport = roundTripperFunc(func(r *http.Request) (*http.Response, error) {
   324  				return nil, errors.New("failed to download artifact")
   325  			})
   326  			// propagate custom round tripper to client without CheckRedirect
   327  			client.initialize()
   328  			testBadOptions(t, methodName, func() (err error) {
   329  				_, _, err = client.Actions.DownloadArtifact(ctx, "o", "r", 1, 1)
   330  				return err
   331  			})
   332  		})
   333  	}
   334  }
   335  
   336  func TestActionsService_DownloadArtifact_invalidOwner(t *testing.T) {
   337  	t.Parallel()
   338  	tcs := []struct {
   339  		name              string
   340  		respectRateLimits bool
   341  	}{
   342  		{
   343  			name:              "withoutRateLimits",
   344  			respectRateLimits: false,
   345  		},
   346  		{
   347  			name:              "withRateLimits",
   348  			respectRateLimits: true,
   349  		},
   350  	}
   351  
   352  	for _, tc := range tcs {
   353  		t.Run(tc.name, func(t *testing.T) {
   354  			t.Parallel()
   355  			client, _, _ := setup(t)
   356  			client.RateLimitRedirectionalEndpoints = tc.respectRateLimits
   357  
   358  			ctx := context.Background()
   359  			_, _, err := client.Actions.DownloadArtifact(ctx, "%", "r", 1, 1)
   360  			testURLParseError(t, err)
   361  		})
   362  	}
   363  }
   364  
   365  func TestActionsService_DownloadArtifact_invalidRepo(t *testing.T) {
   366  	t.Parallel()
   367  	tcs := []struct {
   368  		name              string
   369  		respectRateLimits bool
   370  	}{
   371  		{
   372  			name:              "withoutRateLimits",
   373  			respectRateLimits: false,
   374  		},
   375  		{
   376  			name:              "withRateLimits",
   377  			respectRateLimits: true,
   378  		},
   379  	}
   380  
   381  	for _, tc := range tcs {
   382  		t.Run(tc.name, func(t *testing.T) {
   383  			t.Parallel()
   384  			client, _, _ := setup(t)
   385  			client.RateLimitRedirectionalEndpoints = tc.respectRateLimits
   386  
   387  			ctx := context.Background()
   388  			_, _, err := client.Actions.DownloadArtifact(ctx, "o", "%", 1, 1)
   389  			testURLParseError(t, err)
   390  		})
   391  	}
   392  }
   393  
   394  func TestActionsService_DownloadArtifact_StatusMovedPermanently_dontFollowRedirects(t *testing.T) {
   395  	t.Parallel()
   396  	tcs := []struct {
   397  		name              string
   398  		respectRateLimits bool
   399  	}{
   400  		{
   401  			name:              "withoutRateLimits",
   402  			respectRateLimits: false,
   403  		},
   404  		{
   405  			name:              "withRateLimits",
   406  			respectRateLimits: true,
   407  		},
   408  	}
   409  
   410  	for _, tc := range tcs {
   411  		t.Run(tc.name, func(t *testing.T) {
   412  			t.Parallel()
   413  			client, mux, _ := setup(t)
   414  			client.RateLimitRedirectionalEndpoints = tc.respectRateLimits
   415  
   416  			mux.HandleFunc("/repos/o/r/actions/artifacts/1/zip", func(w http.ResponseWriter, r *http.Request) {
   417  				testMethod(t, r, "GET")
   418  				http.Redirect(w, r, "https://github.com/artifact", http.StatusMovedPermanently)
   419  			})
   420  
   421  			ctx := context.Background()
   422  			_, resp, _ := client.Actions.DownloadArtifact(ctx, "o", "r", 1, 0)
   423  			if resp.StatusCode != http.StatusMovedPermanently {
   424  				t.Errorf("Actions.DownloadArtifact return status %d, want %d", resp.StatusCode, http.StatusMovedPermanently)
   425  			}
   426  		})
   427  	}
   428  }
   429  
   430  func TestActionsService_DownloadArtifact_StatusMovedPermanently_followRedirects(t *testing.T) {
   431  	t.Parallel()
   432  	tcs := []struct {
   433  		name              string
   434  		respectRateLimits bool
   435  	}{
   436  		{
   437  			name:              "withoutRateLimits",
   438  			respectRateLimits: false,
   439  		},
   440  		{
   441  			name:              "withRateLimits",
   442  			respectRateLimits: true,
   443  		},
   444  	}
   445  
   446  	for _, tc := range tcs {
   447  		t.Run(tc.name, func(t *testing.T) {
   448  			t.Parallel()
   449  			client, mux, serverURL := setup(t)
   450  			client.RateLimitRedirectionalEndpoints = tc.respectRateLimits
   451  
   452  			mux.HandleFunc("/repos/o/r/actions/artifacts/1/zip", func(w http.ResponseWriter, r *http.Request) {
   453  				testMethod(t, r, "GET")
   454  				redirectURL, _ := url.Parse(serverURL + baseURLPath + "/redirect")
   455  				http.Redirect(w, r, redirectURL.String(), http.StatusMovedPermanently)
   456  			})
   457  			mux.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) {
   458  				testMethod(t, r, "GET")
   459  				http.Redirect(w, r, "http://github.com/artifact", http.StatusFound)
   460  			})
   461  
   462  			ctx := context.Background()
   463  			url, resp, err := client.Actions.DownloadArtifact(ctx, "o", "r", 1, 1)
   464  			if err != nil {
   465  				t.Errorf("Actions.DownloadArtifact return error: %v", err)
   466  			}
   467  			if resp.StatusCode != http.StatusFound {
   468  				t.Errorf("Actions.DownloadArtifact return status %d, want %d", resp.StatusCode, http.StatusFound)
   469  			}
   470  			want := "http://github.com/artifact"
   471  			if url.String() != want {
   472  				t.Errorf("Actions.DownloadArtifact returned %+v, want %+v", url.String(), want)
   473  			}
   474  		})
   475  	}
   476  }
   477  
   478  func TestActionsService_DownloadArtifact_unexpectedCode(t *testing.T) {
   479  	t.Parallel()
   480  	tcs := []struct {
   481  		name              string
   482  		respectRateLimits bool
   483  	}{
   484  		{
   485  			name:              "withoutRateLimits",
   486  			respectRateLimits: false,
   487  		},
   488  		{
   489  			name:              "withRateLimits",
   490  			respectRateLimits: true,
   491  		},
   492  	}
   493  
   494  	for _, tc := range tcs {
   495  		t.Run(tc.name, func(t *testing.T) {
   496  			t.Parallel()
   497  			client, mux, serverURL := setup(t)
   498  			client.RateLimitRedirectionalEndpoints = tc.respectRateLimits
   499  
   500  			mux.HandleFunc("/repos/o/r/actions/artifacts/1/zip", func(w http.ResponseWriter, r *http.Request) {
   501  				testMethod(t, r, "GET")
   502  				redirectURL, _ := url.Parse(serverURL + baseURLPath + "/redirect")
   503  				http.Redirect(w, r, redirectURL.String(), http.StatusMovedPermanently)
   504  			})
   505  			mux.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) {
   506  				testMethod(t, r, "GET")
   507  				w.WriteHeader(http.StatusNoContent)
   508  			})
   509  
   510  			ctx := context.Background()
   511  			url, resp, err := client.Actions.DownloadArtifact(ctx, "o", "r", 1, 1)
   512  			if err == nil {
   513  				t.Fatalf("Actions.DownloadArtifact should return error on unexpected code")
   514  			}
   515  			if !strings.Contains(err.Error(), "unexpected status code") {
   516  				t.Error("Actions.DownloadArtifact should return unexpected status code")
   517  			}
   518  			if got, want := resp.Response.StatusCode, http.StatusNoContent; got != want {
   519  				t.Errorf("Actions.DownloadArtifact return status %d, want %d", got, want)
   520  			}
   521  			if url != nil {
   522  				t.Errorf("Actions.DownloadArtifact return %+v, want nil", url)
   523  			}
   524  		})
   525  	}
   526  }
   527  
   528  func TestActionsService_DeleteArtifact(t *testing.T) {
   529  	t.Parallel()
   530  	client, mux, _ := setup(t)
   531  
   532  	mux.HandleFunc("/repos/o/r/actions/artifacts/1", func(w http.ResponseWriter, r *http.Request) {
   533  		testMethod(t, r, "DELETE")
   534  	})
   535  
   536  	ctx := context.Background()
   537  	_, err := client.Actions.DeleteArtifact(ctx, "o", "r", 1)
   538  	if err != nil {
   539  		t.Errorf("Actions.DeleteArtifact return error: %v", err)
   540  	}
   541  
   542  	const methodName = "DeleteArtifact"
   543  	testBadOptions(t, methodName, func() (err error) {
   544  		_, err = client.Actions.DeleteArtifact(ctx, "\n", "\n", -1)
   545  		return err
   546  	})
   547  
   548  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   549  		return client.Actions.DeleteArtifact(ctx, "o", "r", 1)
   550  	})
   551  }
   552  
   553  func TestActionsService_DeleteArtifact_invalidOwner(t *testing.T) {
   554  	t.Parallel()
   555  	client, _, _ := setup(t)
   556  
   557  	ctx := context.Background()
   558  	_, err := client.Actions.DeleteArtifact(ctx, "%", "r", 1)
   559  	testURLParseError(t, err)
   560  }
   561  
   562  func TestActionsService_DeleteArtifact_invalidRepo(t *testing.T) {
   563  	t.Parallel()
   564  	client, _, _ := setup(t)
   565  
   566  	ctx := context.Background()
   567  	_, err := client.Actions.DeleteArtifact(ctx, "o", "%", 1)
   568  	testURLParseError(t, err)
   569  }
   570  
   571  func TestActionsService_DeleteArtifact_notFound(t *testing.T) {
   572  	t.Parallel()
   573  	client, mux, _ := setup(t)
   574  
   575  	mux.HandleFunc("/repos/o/r/actions/artifacts/1", func(w http.ResponseWriter, r *http.Request) {
   576  		testMethod(t, r, "DELETE")
   577  		w.WriteHeader(http.StatusNotFound)
   578  	})
   579  
   580  	ctx := context.Background()
   581  	resp, err := client.Actions.DeleteArtifact(ctx, "o", "r", 1)
   582  	if err == nil {
   583  		t.Errorf("Expected HTTP 404 response")
   584  	}
   585  	if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want {
   586  		t.Errorf("Actions.DeleteArtifact return status %d, want %d", got, want)
   587  	}
   588  }
   589  
   590  func TestArtifact_Marshal(t *testing.T) {
   591  	t.Parallel()
   592  	testJSONMarshal(t, &Artifact{}, "{}")
   593  
   594  	u := &Artifact{
   595  		ID:                 Ptr(int64(1)),
   596  		NodeID:             Ptr("nid"),
   597  		Name:               Ptr("n"),
   598  		SizeInBytes:        Ptr(int64(1)),
   599  		URL:                Ptr("u"),
   600  		ArchiveDownloadURL: Ptr("a"),
   601  		Expired:            Ptr(false),
   602  		CreatedAt:          &Timestamp{referenceTime},
   603  		UpdatedAt:          &Timestamp{referenceTime},
   604  		ExpiresAt:          &Timestamp{referenceTime},
   605  		WorkflowRun: &ArtifactWorkflowRun{
   606  			ID:               Ptr(int64(1)),
   607  			RepositoryID:     Ptr(int64(1)),
   608  			HeadRepositoryID: Ptr(int64(1)),
   609  			HeadBranch:       Ptr("b"),
   610  			HeadSHA:          Ptr("s"),
   611  		},
   612  	}
   613  
   614  	want := `{
   615  		"id": 1,
   616  		"node_id": "nid",
   617  		"name": "n",
   618  		"size_in_bytes": 1,
   619  		"url": "u",
   620  		"archive_download_url": "a",
   621  		"expired": false,
   622  		"created_at": ` + referenceTimeStr + `,
   623  		"updated_at": ` + referenceTimeStr + `,
   624  		"expires_at": ` + referenceTimeStr + `,
   625  		"workflow_run": {
   626  			"id": 1,
   627  			"repository_id": 1,
   628  			"head_repository_id": 1,
   629  			"head_branch": "b",
   630  			"head_sha": "s"
   631  		}
   632  	}`
   633  
   634  	testJSONMarshal(t, u, want)
   635  }
   636  
   637  func TestArtifactList_Marshal(t *testing.T) {
   638  	t.Parallel()
   639  	testJSONMarshal(t, &ArtifactList{}, "{}")
   640  
   641  	u := &ArtifactList{
   642  		TotalCount: Ptr(int64(1)),
   643  		Artifacts: []*Artifact{
   644  			{
   645  				ID:                 Ptr(int64(1)),
   646  				NodeID:             Ptr("nid"),
   647  				Name:               Ptr("n"),
   648  				SizeInBytes:        Ptr(int64(1)),
   649  				URL:                Ptr("u"),
   650  				ArchiveDownloadURL: Ptr("a"),
   651  				Expired:            Ptr(false),
   652  				CreatedAt:          &Timestamp{referenceTime},
   653  				UpdatedAt:          &Timestamp{referenceTime},
   654  				ExpiresAt:          &Timestamp{referenceTime},
   655  				WorkflowRun: &ArtifactWorkflowRun{
   656  					ID:               Ptr(int64(1)),
   657  					RepositoryID:     Ptr(int64(1)),
   658  					HeadRepositoryID: Ptr(int64(1)),
   659  					HeadBranch:       Ptr("b"),
   660  					HeadSHA:          Ptr("s"),
   661  				},
   662  			},
   663  		},
   664  	}
   665  
   666  	want := `{
   667  		"total_count": 1,
   668  		"artifacts": [{
   669  			"id": 1,
   670  			"node_id": "nid",
   671  			"name": "n",
   672  			"size_in_bytes": 1,
   673  			"url": "u",
   674  			"archive_download_url": "a",
   675  			"expired": false,
   676  			"created_at": ` + referenceTimeStr + `,
   677  			"updated_at": ` + referenceTimeStr + `,
   678  			"expires_at": ` + referenceTimeStr + `,
   679  			"workflow_run": {
   680  				"id": 1,
   681  				"repository_id": 1,
   682  				"head_repository_id": 1,
   683  				"head_branch": "b",
   684  				"head_sha": "s"
   685  			}
   686  		}]
   687  	}`
   688  
   689  	testJSONMarshal(t, u, want)
   690  }