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