github.com/google/go-github/v57@v57.0.0/github/repos_releases_test.go (about)

     1  // Copyright 2013 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  	"bytes"
    10  	"context"
    11  	"encoding/json"
    12  	"fmt"
    13  	"io"
    14  	"net/http"
    15  	"os"
    16  	"strings"
    17  	"testing"
    18  
    19  	"github.com/google/go-cmp/cmp"
    20  )
    21  
    22  func TestRepositoriesService_ListReleases(t *testing.T) {
    23  	client, mux, _, teardown := setup()
    24  	defer teardown()
    25  
    26  	mux.HandleFunc("/repos/o/r/releases", func(w http.ResponseWriter, r *http.Request) {
    27  		testMethod(t, r, "GET")
    28  		testFormValues(t, r, values{"page": "2"})
    29  		fmt.Fprint(w, `[{"id":1}]`)
    30  	})
    31  
    32  	opt := &ListOptions{Page: 2}
    33  	ctx := context.Background()
    34  	releases, _, err := client.Repositories.ListReleases(ctx, "o", "r", opt)
    35  	if err != nil {
    36  		t.Errorf("Repositories.ListReleases returned error: %v", err)
    37  	}
    38  	want := []*RepositoryRelease{{ID: Int64(1)}}
    39  	if !cmp.Equal(releases, want) {
    40  		t.Errorf("Repositories.ListReleases returned %+v, want %+v", releases, want)
    41  	}
    42  
    43  	const methodName = "ListReleases"
    44  	testBadOptions(t, methodName, func() (err error) {
    45  		_, _, err = client.Repositories.ListReleases(ctx, "\n", "\n", opt)
    46  		return err
    47  	})
    48  
    49  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
    50  		got, resp, err := client.Repositories.ListReleases(ctx, "o", "r", opt)
    51  		if got != nil {
    52  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
    53  		}
    54  		return resp, err
    55  	})
    56  }
    57  
    58  func TestRepositoriesService_GenerateReleaseNotes(t *testing.T) {
    59  	client, mux, _, teardown := setup()
    60  	defer teardown()
    61  
    62  	mux.HandleFunc("/repos/o/r/releases/generate-notes", func(w http.ResponseWriter, r *http.Request) {
    63  		testMethod(t, r, "POST")
    64  		testBody(t, r, `{"tag_name":"v1.0.0"}`+"\n")
    65  		fmt.Fprint(w, `{"name":"v1.0.0","body":"**Full Changelog**: https://github.com/o/r/compare/v0.9.0...v1.0.0"}`)
    66  	})
    67  
    68  	opt := &GenerateNotesOptions{
    69  		TagName: "v1.0.0",
    70  	}
    71  	ctx := context.Background()
    72  	releases, _, err := client.Repositories.GenerateReleaseNotes(ctx, "o", "r", opt)
    73  	if err != nil {
    74  		t.Errorf("Repositories.GenerateReleaseNotes returned error: %v", err)
    75  	}
    76  	want := &RepositoryReleaseNotes{
    77  		Name: "v1.0.0",
    78  		Body: "**Full Changelog**: https://github.com/o/r/compare/v0.9.0...v1.0.0",
    79  	}
    80  	if !cmp.Equal(releases, want) {
    81  		t.Errorf("Repositories.GenerateReleaseNotes returned %+v, want %+v", releases, want)
    82  	}
    83  
    84  	const methodName = "GenerateReleaseNotes"
    85  	testBadOptions(t, methodName, func() (err error) {
    86  		_, _, err = client.Repositories.GenerateReleaseNotes(ctx, "\n", "\n", opt)
    87  		return err
    88  	})
    89  
    90  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
    91  		got, resp, err := client.Repositories.GenerateReleaseNotes(ctx, "o", "r", opt)
    92  		if got != nil {
    93  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
    94  		}
    95  		return resp, err
    96  	})
    97  }
    98  
    99  func TestRepositoriesService_GetRelease(t *testing.T) {
   100  	client, mux, _, teardown := setup()
   101  	defer teardown()
   102  
   103  	mux.HandleFunc("/repos/o/r/releases/1", func(w http.ResponseWriter, r *http.Request) {
   104  		testMethod(t, r, "GET")
   105  		fmt.Fprint(w, `{"id":1,"author":{"login":"l"}}`)
   106  	})
   107  
   108  	ctx := context.Background()
   109  	release, resp, err := client.Repositories.GetRelease(ctx, "o", "r", 1)
   110  	if err != nil {
   111  		t.Errorf("Repositories.GetRelease returned error: %v\n%v", err, resp.Body)
   112  	}
   113  
   114  	want := &RepositoryRelease{ID: Int64(1), Author: &User{Login: String("l")}}
   115  	if !cmp.Equal(release, want) {
   116  		t.Errorf("Repositories.GetRelease returned %+v, want %+v", release, want)
   117  	}
   118  
   119  	const methodName = "GetRelease"
   120  	testBadOptions(t, methodName, func() (err error) {
   121  		_, _, err = client.Repositories.GetRelease(ctx, "\n", "\n", 1)
   122  		return err
   123  	})
   124  
   125  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   126  		got, resp, err := client.Repositories.GetRelease(ctx, "o", "r", 1)
   127  		if got != nil {
   128  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   129  		}
   130  		return resp, err
   131  	})
   132  }
   133  
   134  func TestRepositoriesService_GetLatestRelease(t *testing.T) {
   135  	client, mux, _, teardown := setup()
   136  	defer teardown()
   137  
   138  	mux.HandleFunc("/repos/o/r/releases/latest", func(w http.ResponseWriter, r *http.Request) {
   139  		testMethod(t, r, "GET")
   140  		fmt.Fprint(w, `{"id":3}`)
   141  	})
   142  
   143  	ctx := context.Background()
   144  	release, resp, err := client.Repositories.GetLatestRelease(ctx, "o", "r")
   145  	if err != nil {
   146  		t.Errorf("Repositories.GetLatestRelease returned error: %v\n%v", err, resp.Body)
   147  	}
   148  
   149  	want := &RepositoryRelease{ID: Int64(3)}
   150  	if !cmp.Equal(release, want) {
   151  		t.Errorf("Repositories.GetLatestRelease returned %+v, want %+v", release, want)
   152  	}
   153  
   154  	const methodName = "GetLatestRelease"
   155  	testBadOptions(t, methodName, func() (err error) {
   156  		_, _, err = client.Repositories.GetLatestRelease(ctx, "\n", "\n")
   157  		return err
   158  	})
   159  
   160  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   161  		got, resp, err := client.Repositories.GetLatestRelease(ctx, "o", "r")
   162  		if got != nil {
   163  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   164  		}
   165  		return resp, err
   166  	})
   167  }
   168  
   169  func TestRepositoriesService_GetReleaseByTag(t *testing.T) {
   170  	client, mux, _, teardown := setup()
   171  	defer teardown()
   172  
   173  	mux.HandleFunc("/repos/o/r/releases/tags/foo", func(w http.ResponseWriter, r *http.Request) {
   174  		testMethod(t, r, "GET")
   175  		fmt.Fprint(w, `{"id":13}`)
   176  	})
   177  
   178  	ctx := context.Background()
   179  	release, resp, err := client.Repositories.GetReleaseByTag(ctx, "o", "r", "foo")
   180  	if err != nil {
   181  		t.Errorf("Repositories.GetReleaseByTag returned error: %v\n%v", err, resp.Body)
   182  	}
   183  
   184  	want := &RepositoryRelease{ID: Int64(13)}
   185  	if !cmp.Equal(release, want) {
   186  		t.Errorf("Repositories.GetReleaseByTag returned %+v, want %+v", release, want)
   187  	}
   188  
   189  	const methodName = "GetReleaseByTag"
   190  	testBadOptions(t, methodName, func() (err error) {
   191  		_, _, err = client.Repositories.GetReleaseByTag(ctx, "\n", "\n", "foo")
   192  		return err
   193  	})
   194  
   195  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   196  		got, resp, err := client.Repositories.GetReleaseByTag(ctx, "o", "r", "foo")
   197  		if got != nil {
   198  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   199  		}
   200  		return resp, err
   201  	})
   202  }
   203  
   204  func TestRepositoriesService_CreateRelease(t *testing.T) {
   205  	client, mux, _, teardown := setup()
   206  	defer teardown()
   207  
   208  	input := &RepositoryRelease{
   209  		Name:                   String("v1.0"),
   210  		DiscussionCategoryName: String("General"),
   211  		GenerateReleaseNotes:   Bool(true),
   212  		// Fields to be removed:
   213  		ID:          Int64(2),
   214  		CreatedAt:   &Timestamp{referenceTime},
   215  		PublishedAt: &Timestamp{referenceTime},
   216  		URL:         String("http://url/"),
   217  		HTMLURL:     String("http://htmlurl/"),
   218  		AssetsURL:   String("http://assetsurl/"),
   219  		Assets:      []*ReleaseAsset{{ID: Int64(5)}},
   220  		UploadURL:   String("http://uploadurl/"),
   221  		ZipballURL:  String("http://zipballurl/"),
   222  		TarballURL:  String("http://tarballurl/"),
   223  		Author:      &User{Name: String("octocat")},
   224  		NodeID:      String("nodeid"),
   225  	}
   226  
   227  	mux.HandleFunc("/repos/o/r/releases", func(w http.ResponseWriter, r *http.Request) {
   228  		v := new(repositoryReleaseRequest)
   229  		assertNilError(t, json.NewDecoder(r.Body).Decode(v))
   230  
   231  		testMethod(t, r, "POST")
   232  		want := &repositoryReleaseRequest{
   233  			Name:                   String("v1.0"),
   234  			DiscussionCategoryName: String("General"),
   235  			GenerateReleaseNotes:   Bool(true),
   236  		}
   237  		if !cmp.Equal(v, want) {
   238  			t.Errorf("Request body = %+v, want %+v", v, want)
   239  		}
   240  		fmt.Fprint(w, `{"id":1}`)
   241  	})
   242  
   243  	ctx := context.Background()
   244  	release, _, err := client.Repositories.CreateRelease(ctx, "o", "r", input)
   245  	if err != nil {
   246  		t.Errorf("Repositories.CreateRelease returned error: %v", err)
   247  	}
   248  
   249  	want := &RepositoryRelease{ID: Int64(1)}
   250  	if !cmp.Equal(release, want) {
   251  		t.Errorf("Repositories.CreateRelease returned %+v, want %+v", release, want)
   252  	}
   253  
   254  	const methodName = "CreateRelease"
   255  	testBadOptions(t, methodName, func() (err error) {
   256  		_, _, err = client.Repositories.CreateRelease(ctx, "\n", "\n", input)
   257  		return err
   258  	})
   259  
   260  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   261  		got, resp, err := client.Repositories.CreateRelease(ctx, "o", "r", input)
   262  		if got != nil {
   263  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   264  		}
   265  		return resp, err
   266  	})
   267  }
   268  
   269  func TestRepositoriesService_EditRelease(t *testing.T) {
   270  	client, mux, _, teardown := setup()
   271  	defer teardown()
   272  
   273  	input := &RepositoryRelease{
   274  		Name:                   String("n"),
   275  		DiscussionCategoryName: String("General"),
   276  		// Fields to be removed:
   277  		GenerateReleaseNotes: Bool(true),
   278  		ID:                   Int64(2),
   279  		CreatedAt:            &Timestamp{referenceTime},
   280  		PublishedAt:          &Timestamp{referenceTime},
   281  		URL:                  String("http://url/"),
   282  		HTMLURL:              String("http://htmlurl/"),
   283  		AssetsURL:            String("http://assetsurl/"),
   284  		Assets:               []*ReleaseAsset{{ID: Int64(5)}},
   285  		UploadURL:            String("http://uploadurl/"),
   286  		ZipballURL:           String("http://zipballurl/"),
   287  		TarballURL:           String("http://tarballurl/"),
   288  		Author:               &User{Name: String("octocat")},
   289  		NodeID:               String("nodeid"),
   290  	}
   291  
   292  	mux.HandleFunc("/repos/o/r/releases/1", func(w http.ResponseWriter, r *http.Request) {
   293  		v := new(repositoryReleaseRequest)
   294  		assertNilError(t, json.NewDecoder(r.Body).Decode(v))
   295  
   296  		testMethod(t, r, "PATCH")
   297  		want := &repositoryReleaseRequest{
   298  			Name:                   String("n"),
   299  			DiscussionCategoryName: String("General"),
   300  		}
   301  		if !cmp.Equal(v, want) {
   302  			t.Errorf("Request body = %+v, want %+v", v, want)
   303  		}
   304  		fmt.Fprint(w, `{"id":1}`)
   305  	})
   306  
   307  	ctx := context.Background()
   308  	release, _, err := client.Repositories.EditRelease(ctx, "o", "r", 1, input)
   309  	if err != nil {
   310  		t.Errorf("Repositories.EditRelease returned error: %v", err)
   311  	}
   312  	want := &RepositoryRelease{ID: Int64(1)}
   313  	if !cmp.Equal(release, want) {
   314  		t.Errorf("Repositories.EditRelease returned = %+v, want %+v", release, want)
   315  	}
   316  
   317  	const methodName = "EditRelease"
   318  	testBadOptions(t, methodName, func() (err error) {
   319  		_, _, err = client.Repositories.EditRelease(ctx, "\n", "\n", 1, input)
   320  		return err
   321  	})
   322  
   323  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   324  		got, resp, err := client.Repositories.EditRelease(ctx, "o", "r", 1, input)
   325  		if got != nil {
   326  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   327  		}
   328  		return resp, err
   329  	})
   330  }
   331  
   332  func TestRepositoriesService_DeleteRelease(t *testing.T) {
   333  	client, mux, _, teardown := setup()
   334  	defer teardown()
   335  
   336  	mux.HandleFunc("/repos/o/r/releases/1", func(w http.ResponseWriter, r *http.Request) {
   337  		testMethod(t, r, "DELETE")
   338  	})
   339  
   340  	ctx := context.Background()
   341  	_, err := client.Repositories.DeleteRelease(ctx, "o", "r", 1)
   342  	if err != nil {
   343  		t.Errorf("Repositories.DeleteRelease returned error: %v", err)
   344  	}
   345  
   346  	const methodName = "DeleteRelease"
   347  	testBadOptions(t, methodName, func() (err error) {
   348  		_, err = client.Repositories.DeleteRelease(ctx, "\n", "\n", 1)
   349  		return err
   350  	})
   351  
   352  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   353  		return client.Repositories.DeleteRelease(ctx, "o", "r", 1)
   354  	})
   355  }
   356  
   357  func TestRepositoriesService_ListReleaseAssets(t *testing.T) {
   358  	client, mux, _, teardown := setup()
   359  	defer teardown()
   360  
   361  	mux.HandleFunc("/repos/o/r/releases/1/assets", func(w http.ResponseWriter, r *http.Request) {
   362  		testMethod(t, r, "GET")
   363  		testFormValues(t, r, values{"page": "2"})
   364  		fmt.Fprint(w, `[{"id":1}]`)
   365  	})
   366  
   367  	opt := &ListOptions{Page: 2}
   368  	ctx := context.Background()
   369  	assets, _, err := client.Repositories.ListReleaseAssets(ctx, "o", "r", 1, opt)
   370  	if err != nil {
   371  		t.Errorf("Repositories.ListReleaseAssets returned error: %v", err)
   372  	}
   373  	want := []*ReleaseAsset{{ID: Int64(1)}}
   374  	if !cmp.Equal(assets, want) {
   375  		t.Errorf("Repositories.ListReleaseAssets returned %+v, want %+v", assets, want)
   376  	}
   377  
   378  	const methodName = "ListReleaseAssets"
   379  	testBadOptions(t, methodName, func() (err error) {
   380  		_, _, err = client.Repositories.ListReleaseAssets(ctx, "\n", "\n", 1, opt)
   381  		return err
   382  	})
   383  
   384  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   385  		got, resp, err := client.Repositories.ListReleaseAssets(ctx, "o", "r", 1, opt)
   386  		if got != nil {
   387  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   388  		}
   389  		return resp, err
   390  	})
   391  }
   392  
   393  func TestRepositoriesService_GetReleaseAsset(t *testing.T) {
   394  	client, mux, _, teardown := setup()
   395  	defer teardown()
   396  
   397  	mux.HandleFunc("/repos/o/r/releases/assets/1", func(w http.ResponseWriter, r *http.Request) {
   398  		testMethod(t, r, "GET")
   399  		fmt.Fprint(w, `{"id":1}`)
   400  	})
   401  
   402  	ctx := context.Background()
   403  	asset, _, err := client.Repositories.GetReleaseAsset(ctx, "o", "r", 1)
   404  	if err != nil {
   405  		t.Errorf("Repositories.GetReleaseAsset returned error: %v", err)
   406  	}
   407  	want := &ReleaseAsset{ID: Int64(1)}
   408  	if !cmp.Equal(asset, want) {
   409  		t.Errorf("Repositories.GetReleaseAsset returned %+v, want %+v", asset, want)
   410  	}
   411  
   412  	const methodName = "GetReleaseAsset"
   413  	testBadOptions(t, methodName, func() (err error) {
   414  		_, _, err = client.Repositories.GetReleaseAsset(ctx, "\n", "\n", 1)
   415  		return err
   416  	})
   417  
   418  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   419  		got, resp, err := client.Repositories.GetReleaseAsset(ctx, "o", "r", 1)
   420  		if got != nil {
   421  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   422  		}
   423  		return resp, err
   424  	})
   425  }
   426  
   427  func TestRepositoriesService_DownloadReleaseAsset_Stream(t *testing.T) {
   428  	client, mux, _, teardown := setup()
   429  	defer teardown()
   430  
   431  	mux.HandleFunc("/repos/o/r/releases/assets/1", func(w http.ResponseWriter, r *http.Request) {
   432  		testMethod(t, r, "GET")
   433  		testHeader(t, r, "Accept", defaultMediaType)
   434  		w.Header().Set("Content-Type", "application/octet-stream")
   435  		w.Header().Set("Content-Disposition", "attachment; filename=hello-world.txt")
   436  		fmt.Fprint(w, "Hello World")
   437  	})
   438  
   439  	ctx := context.Background()
   440  	reader, _, err := client.Repositories.DownloadReleaseAsset(ctx, "o", "r", 1, nil)
   441  	if err != nil {
   442  		t.Errorf("Repositories.DownloadReleaseAsset returned error: %v", err)
   443  	}
   444  	want := []byte("Hello World")
   445  	content, err := io.ReadAll(reader)
   446  	if err != nil {
   447  		t.Errorf("Repositories.DownloadReleaseAsset returned bad reader: %v", err)
   448  	}
   449  	if !bytes.Equal(want, content) {
   450  		t.Errorf("Repositories.DownloadReleaseAsset returned %+v, want %+v", content, want)
   451  	}
   452  
   453  	const methodName = "DownloadReleaseAsset"
   454  	testBadOptions(t, methodName, func() (err error) {
   455  		_, _, err = client.Repositories.DownloadReleaseAsset(ctx, "\n", "\n", -1, nil)
   456  		return err
   457  	})
   458  }
   459  
   460  func TestRepositoriesService_DownloadReleaseAsset_Redirect(t *testing.T) {
   461  	client, mux, _, teardown := setup()
   462  	defer teardown()
   463  
   464  	mux.HandleFunc("/repos/o/r/releases/assets/1", func(w http.ResponseWriter, r *http.Request) {
   465  		testMethod(t, r, "GET")
   466  		testHeader(t, r, "Accept", defaultMediaType)
   467  		http.Redirect(w, r, "/yo", http.StatusFound)
   468  	})
   469  
   470  	ctx := context.Background()
   471  	_, got, err := client.Repositories.DownloadReleaseAsset(ctx, "o", "r", 1, nil)
   472  	if err != nil {
   473  		t.Errorf("Repositories.DownloadReleaseAsset returned error: %v", err)
   474  	}
   475  	want := "/yo"
   476  	if !strings.HasSuffix(got, want) {
   477  		t.Errorf("Repositories.DownloadReleaseAsset returned %+v, want %+v", got, want)
   478  	}
   479  }
   480  
   481  func TestRepositoriesService_DownloadReleaseAsset_FollowRedirect(t *testing.T) {
   482  	client, mux, _, teardown := setup()
   483  	defer teardown()
   484  
   485  	mux.HandleFunc("/repos/o/r/releases/assets/1", func(w http.ResponseWriter, r *http.Request) {
   486  		testMethod(t, r, "GET")
   487  		testHeader(t, r, "Accept", defaultMediaType)
   488  		// /yo, below will be served as baseURLPath/yo
   489  		http.Redirect(w, r, baseURLPath+"/yo", http.StatusFound)
   490  	})
   491  	mux.HandleFunc("/yo", func(w http.ResponseWriter, r *http.Request) {
   492  		testMethod(t, r, "GET")
   493  		testHeader(t, r, "Accept", "*/*")
   494  		w.Header().Set("Content-Type", "application/octet-stream")
   495  		w.Header().Set("Content-Disposition", "attachment; filename=hello-world.txt")
   496  		fmt.Fprint(w, "Hello World")
   497  	})
   498  
   499  	ctx := context.Background()
   500  	reader, _, err := client.Repositories.DownloadReleaseAsset(ctx, "o", "r", 1, http.DefaultClient)
   501  	if err != nil {
   502  		t.Errorf("Repositories.DownloadReleaseAsset returned error: %v", err)
   503  	}
   504  	content, err := io.ReadAll(reader)
   505  	if err != nil {
   506  		t.Errorf("Reading Repositories.DownloadReleaseAsset returned error: %v", err)
   507  	}
   508  	reader.Close()
   509  	want := []byte("Hello World")
   510  	if !bytes.Equal(want, content) {
   511  		t.Errorf("Repositories.DownloadReleaseAsset returned %+v, want %+v", content, want)
   512  	}
   513  }
   514  
   515  func TestRepositoriesService_DownloadReleaseAsset_FollowRedirectToError(t *testing.T) {
   516  	client, mux, _, teardown := setup()
   517  	defer teardown()
   518  
   519  	mux.HandleFunc("/repos/o/r/releases/assets/1", func(w http.ResponseWriter, r *http.Request) {
   520  		testMethod(t, r, "GET")
   521  		testHeader(t, r, "Accept", defaultMediaType)
   522  		// /yo, below will be served as baseURLPath/yo
   523  		http.Redirect(w, r, baseURLPath+"/yo", http.StatusFound)
   524  	})
   525  	mux.HandleFunc("/yo", func(w http.ResponseWriter, r *http.Request) {
   526  		testMethod(t, r, "GET")
   527  		testHeader(t, r, "Accept", "*/*")
   528  		w.WriteHeader(http.StatusNotFound)
   529  	})
   530  
   531  	ctx := context.Background()
   532  	resp, loc, err := client.Repositories.DownloadReleaseAsset(ctx, "o", "r", 1, http.DefaultClient)
   533  	if err == nil {
   534  		t.Error("Repositories.DownloadReleaseAsset did not return an error")
   535  	}
   536  	if resp != nil {
   537  		resp.Close()
   538  		t.Error("Repositories.DownloadReleaseAsset returned stream, want nil")
   539  	}
   540  	if loc != "" {
   541  		t.Errorf(`Repositories.DownloadReleaseAsset returned "%s", want empty ""`, loc)
   542  	}
   543  }
   544  
   545  func TestRepositoriesService_DownloadReleaseAsset_APIError(t *testing.T) {
   546  	client, mux, _, teardown := setup()
   547  	defer teardown()
   548  
   549  	mux.HandleFunc("/repos/o/r/releases/assets/1", func(w http.ResponseWriter, r *http.Request) {
   550  		testMethod(t, r, "GET")
   551  		testHeader(t, r, "Accept", defaultMediaType)
   552  		w.WriteHeader(http.StatusNotFound)
   553  		fmt.Fprint(w, `{"message":"Not Found","documentation_url":"https://developer.github.com/v3"}`)
   554  	})
   555  
   556  	ctx := context.Background()
   557  	resp, loc, err := client.Repositories.DownloadReleaseAsset(ctx, "o", "r", 1, nil)
   558  	if err == nil {
   559  		t.Error("Repositories.DownloadReleaseAsset did not return an error")
   560  	}
   561  
   562  	if resp != nil {
   563  		resp.Close()
   564  		t.Error("Repositories.DownloadReleaseAsset returned stream, want nil")
   565  	}
   566  
   567  	if loc != "" {
   568  		t.Errorf(`Repositories.DownloadReleaseAsset returned "%s", want empty ""`, loc)
   569  	}
   570  }
   571  
   572  func TestRepositoriesService_EditReleaseAsset(t *testing.T) {
   573  	client, mux, _, teardown := setup()
   574  	defer teardown()
   575  
   576  	input := &ReleaseAsset{Name: String("n")}
   577  
   578  	mux.HandleFunc("/repos/o/r/releases/assets/1", func(w http.ResponseWriter, r *http.Request) {
   579  		v := new(ReleaseAsset)
   580  		assertNilError(t, json.NewDecoder(r.Body).Decode(v))
   581  
   582  		testMethod(t, r, "PATCH")
   583  		if !cmp.Equal(v, input) {
   584  			t.Errorf("Request body = %+v, want %+v", v, input)
   585  		}
   586  		fmt.Fprint(w, `{"id":1}`)
   587  	})
   588  
   589  	ctx := context.Background()
   590  	asset, _, err := client.Repositories.EditReleaseAsset(ctx, "o", "r", 1, input)
   591  	if err != nil {
   592  		t.Errorf("Repositories.EditReleaseAsset returned error: %v", err)
   593  	}
   594  	want := &ReleaseAsset{ID: Int64(1)}
   595  	if !cmp.Equal(asset, want) {
   596  		t.Errorf("Repositories.EditReleaseAsset returned = %+v, want %+v", asset, want)
   597  	}
   598  
   599  	const methodName = "EditReleaseAsset"
   600  	testBadOptions(t, methodName, func() (err error) {
   601  		_, _, err = client.Repositories.EditReleaseAsset(ctx, "\n", "\n", 1, input)
   602  		return err
   603  	})
   604  
   605  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   606  		got, resp, err := client.Repositories.EditReleaseAsset(ctx, "o", "r", 1, input)
   607  		if got != nil {
   608  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   609  		}
   610  		return resp, err
   611  	})
   612  }
   613  
   614  func TestRepositoriesService_DeleteReleaseAsset(t *testing.T) {
   615  	client, mux, _, teardown := setup()
   616  	defer teardown()
   617  
   618  	mux.HandleFunc("/repos/o/r/releases/assets/1", func(w http.ResponseWriter, r *http.Request) {
   619  		testMethod(t, r, "DELETE")
   620  	})
   621  
   622  	ctx := context.Background()
   623  	_, err := client.Repositories.DeleteReleaseAsset(ctx, "o", "r", 1)
   624  	if err != nil {
   625  		t.Errorf("Repositories.DeleteReleaseAsset returned error: %v", err)
   626  	}
   627  
   628  	const methodName = "DeleteReleaseAsset"
   629  	testBadOptions(t, methodName, func() (err error) {
   630  		_, err = client.Repositories.DeleteReleaseAsset(ctx, "\n", "\n", 1)
   631  		return err
   632  	})
   633  
   634  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   635  		return client.Repositories.DeleteReleaseAsset(ctx, "o", "r", 1)
   636  	})
   637  }
   638  
   639  func TestRepositoriesService_UploadReleaseAsset(t *testing.T) {
   640  	var (
   641  		defaultUploadOptions     = &UploadOptions{Name: "n"}
   642  		defaultExpectedFormValue = values{"name": "n"}
   643  		mediaTypeTextPlain       = "text/plain; charset=utf-8"
   644  	)
   645  	uploadTests := []struct {
   646  		uploadOpts         *UploadOptions
   647  		fileName           string
   648  		expectedFormValues values
   649  		expectedMediaType  string
   650  	}{
   651  		// No file extension and no explicit media type.
   652  		{
   653  			defaultUploadOptions,
   654  			"upload",
   655  			defaultExpectedFormValue,
   656  			defaultMediaType,
   657  		},
   658  		// File extension and no explicit media type.
   659  		{
   660  			defaultUploadOptions,
   661  			"upload.txt",
   662  			defaultExpectedFormValue,
   663  			mediaTypeTextPlain,
   664  		},
   665  		// No file extension and explicit media type.
   666  		{
   667  			&UploadOptions{Name: "n", MediaType: "image/png"},
   668  			"upload",
   669  			defaultExpectedFormValue,
   670  			"image/png",
   671  		},
   672  		// File extension and explicit media type.
   673  		{
   674  			&UploadOptions{Name: "n", MediaType: "image/png"},
   675  			"upload.png",
   676  			defaultExpectedFormValue,
   677  			"image/png",
   678  		},
   679  		// Label provided.
   680  		{
   681  			&UploadOptions{Name: "n", Label: "l"},
   682  			"upload.txt",
   683  			values{"name": "n", "label": "l"},
   684  			mediaTypeTextPlain,
   685  		},
   686  		// No label provided.
   687  		{
   688  			defaultUploadOptions,
   689  			"upload.txt",
   690  			defaultExpectedFormValue,
   691  			mediaTypeTextPlain,
   692  		},
   693  	}
   694  
   695  	client, mux, _, teardown := setup()
   696  	defer teardown()
   697  
   698  	for key, test := range uploadTests {
   699  		releaseEndpoint := fmt.Sprintf("/repos/o/r/releases/%d/assets", key)
   700  		mux.HandleFunc(releaseEndpoint, func(w http.ResponseWriter, r *http.Request) {
   701  			testMethod(t, r, "POST")
   702  			testHeader(t, r, "Content-Type", test.expectedMediaType)
   703  			testHeader(t, r, "Content-Length", "12")
   704  			testFormValues(t, r, test.expectedFormValues)
   705  			testBody(t, r, "Upload me !\n")
   706  
   707  			fmt.Fprintf(w, `{"id":1}`)
   708  		})
   709  
   710  		file, dir, err := openTestFile(test.fileName, "Upload me !\n")
   711  		if err != nil {
   712  			t.Fatalf("Unable to create temp file: %v", err)
   713  		}
   714  		defer os.RemoveAll(dir)
   715  
   716  		ctx := context.Background()
   717  		asset, _, err := client.Repositories.UploadReleaseAsset(ctx, "o", "r", int64(key), test.uploadOpts, file)
   718  		if err != nil {
   719  			t.Errorf("Repositories.UploadReleaseAssert returned error: %v", err)
   720  		}
   721  		want := &ReleaseAsset{ID: Int64(1)}
   722  		if !cmp.Equal(asset, want) {
   723  			t.Errorf("Repositories.UploadReleaseAssert returned %+v, want %+v", asset, want)
   724  		}
   725  
   726  		const methodName = "UploadReleaseAsset"
   727  		testBadOptions(t, methodName, func() (err error) {
   728  			_, _, err = client.Repositories.UploadReleaseAsset(ctx, "\n", "\n", int64(key), test.uploadOpts, file)
   729  			return err
   730  		})
   731  	}
   732  }
   733  
   734  func TestRepositoryReleaseRequest_Marshal(t *testing.T) {
   735  	testJSONMarshal(t, &repositoryReleaseRequest{}, "{}")
   736  
   737  	u := &repositoryReleaseRequest{
   738  		TagName:                String("tn"),
   739  		TargetCommitish:        String("tc"),
   740  		Name:                   String("name"),
   741  		Body:                   String("body"),
   742  		Draft:                  Bool(false),
   743  		Prerelease:             Bool(false),
   744  		MakeLatest:             String("legacy"),
   745  		DiscussionCategoryName: String("dcn"),
   746  	}
   747  
   748  	want := `{
   749  		"tag_name": "tn",
   750  		"target_commitish": "tc",
   751  		"name": "name",
   752  		"body": "body",
   753  		"draft": false,
   754  		"prerelease": false,
   755  		"make_latest": "legacy",
   756  		"discussion_category_name": "dcn"
   757  	}`
   758  
   759  	testJSONMarshal(t, u, want)
   760  }
   761  
   762  func TestReleaseAsset_Marshal(t *testing.T) {
   763  	testJSONMarshal(t, &ReleaseAsset{}, "{}")
   764  
   765  	u := &ReleaseAsset{
   766  		ID:                 Int64(1),
   767  		URL:                String("url"),
   768  		Name:               String("name"),
   769  		Label:              String("label"),
   770  		State:              String("state"),
   771  		ContentType:        String("ct"),
   772  		Size:               Int(1),
   773  		DownloadCount:      Int(1),
   774  		CreatedAt:          &Timestamp{referenceTime},
   775  		UpdatedAt:          &Timestamp{referenceTime},
   776  		BrowserDownloadURL: String("bdu"),
   777  		Uploader:           &User{ID: Int64(1)},
   778  		NodeID:             String("nid"),
   779  	}
   780  
   781  	want := `{
   782  		"id": 1,
   783  		"url": "url",
   784  		"name": "name",
   785  		"label": "label",
   786  		"state": "state",
   787  		"content_type": "ct",
   788  		"size": 1,
   789  		"download_count": 1,
   790  		"created_at": ` + referenceTimeStr + `,
   791  		"updated_at": ` + referenceTimeStr + `,
   792  		"browser_download_url": "bdu",
   793  		"uploader": {
   794  			"id": 1
   795  		},
   796  		"node_id": "nid"
   797  	}`
   798  
   799  	testJSONMarshal(t, u, want)
   800  }
   801  
   802  func TestRepositoryRelease_Marshal(t *testing.T) {
   803  	testJSONMarshal(t, &RepositoryRelease{}, "{}")
   804  
   805  	u := &RepositoryRelease{
   806  		TagName:                String("tn"),
   807  		TargetCommitish:        String("tc"),
   808  		Name:                   String("name"),
   809  		Body:                   String("body"),
   810  		Draft:                  Bool(false),
   811  		Prerelease:             Bool(false),
   812  		MakeLatest:             String("legacy"),
   813  		DiscussionCategoryName: String("dcn"),
   814  		ID:                     Int64(1),
   815  		CreatedAt:              &Timestamp{referenceTime},
   816  		PublishedAt:            &Timestamp{referenceTime},
   817  		URL:                    String("url"),
   818  		HTMLURL:                String("hurl"),
   819  		AssetsURL:              String("aurl"),
   820  		Assets:                 []*ReleaseAsset{{ID: Int64(1)}},
   821  		UploadURL:              String("uurl"),
   822  		ZipballURL:             String("zurl"),
   823  		TarballURL:             String("turl"),
   824  		Author:                 &User{ID: Int64(1)},
   825  		NodeID:                 String("nid"),
   826  	}
   827  
   828  	want := `{
   829  		"tag_name": "tn",
   830  		"target_commitish": "tc",
   831  		"name": "name",
   832  		"body": "body",
   833  		"draft": false,
   834  		"prerelease": false,
   835  		"make_latest": "legacy",
   836  		"discussion_category_name": "dcn",
   837  		"id": 1,
   838  		"created_at": ` + referenceTimeStr + `,
   839  		"published_at": ` + referenceTimeStr + `,
   840  		"url": "url",
   841  		"html_url": "hurl",
   842  		"assets_url": "aurl",
   843  		"assets": [
   844  			{
   845  				"id": 1
   846  			}
   847  		],
   848  		"upload_url": "uurl",
   849  		"zipball_url": "zurl",
   850  		"tarball_url": "turl",
   851  		"author": {
   852  			"id": 1
   853  		},
   854  		"node_id": "nid"
   855  	}`
   856  
   857  	testJSONMarshal(t, u, want)
   858  }
   859  
   860  func TestGenerateNotesOptions_Marshal(t *testing.T) {
   861  	testJSONMarshal(t, &GenerateNotesOptions{}, "{}")
   862  
   863  	u := &GenerateNotesOptions{
   864  		TagName:         "tag_name",
   865  		PreviousTagName: String("previous_tag_name"),
   866  		TargetCommitish: String("target_commitish"),
   867  	}
   868  
   869  	want := `{
   870  		"tag_name":          "tag_name",
   871  		"previous_tag_name": "previous_tag_name",
   872  		"target_commitish":  "target_commitish"
   873  	}`
   874  
   875  	testJSONMarshal(t, u, want)
   876  }