1// Copyright 2014 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
6package github
7
8import (
9	"context"
10	"fmt"
11	"io/ioutil"
12	"net/http"
13	"reflect"
14	"testing"
15)
16
17func TestRepositoryContent_GetContent(t *testing.T) {
18	tests := []struct {
19		encoding, content *string // input encoding and content
20		want              string  // desired output
21		wantErr           bool    // whether an error is expected
22	}{
23		{
24			encoding: String(""),
25			content:  String("hello"),
26			want:     "hello",
27			wantErr:  false,
28		},
29		{
30			encoding: nil,
31			content:  String("hello"),
32			want:     "hello",
33			wantErr:  false,
34		},
35		{
36			encoding: nil,
37			content:  nil,
38			want:     "",
39			wantErr:  false,
40		},
41		{
42			encoding: String("base64"),
43			content:  String("aGVsbG8="),
44			want:     "hello",
45			wantErr:  false,
46		},
47		{
48			encoding: String("bad"),
49			content:  String("aGVsbG8="),
50			want:     "",
51			wantErr:  true,
52		},
53	}
54
55	for _, tt := range tests {
56		r := RepositoryContent{Encoding: tt.encoding, Content: tt.content}
57		got, err := r.GetContent()
58		if err != nil && !tt.wantErr {
59			t.Errorf("RepositoryContent(%s, %s) returned unexpected error: %v",
60				stringOrNil(tt.encoding), stringOrNil(tt.content), err)
61		}
62		if err == nil && tt.wantErr {
63			t.Errorf("RepositoryContent(%s, %s) did not return unexpected error",
64				stringOrNil(tt.encoding), stringOrNil(tt.content))
65		}
66		if want := tt.want; got != want {
67			t.Errorf("RepositoryContent.GetContent returned %+v, want %+v", got, want)
68		}
69	}
70}
71
72// stringOrNil converts a potentially null string pointer to string.
73// For non-nil input pointer, the returned string is enclosed in double-quotes.
74func stringOrNil(s *string) string {
75	if s == nil {
76		return "<nil>"
77	}
78	return fmt.Sprintf("%q", *s)
79}
80
81func TestRepositoriesService_GetReadme(t *testing.T) {
82	client, mux, _, teardown := setup()
83	defer teardown()
84	mux.HandleFunc("/repos/o/r/readme", func(w http.ResponseWriter, r *http.Request) {
85		testMethod(t, r, "GET")
86		fmt.Fprint(w, `{
87		  "type": "file",
88		  "encoding": "base64",
89		  "size": 5362,
90		  "name": "README.md",
91		  "path": "README.md"
92		}`)
93	})
94	readme, _, err := client.Repositories.GetReadme(context.Background(), "o", "r", &RepositoryContentGetOptions{})
95	if err != nil {
96		t.Errorf("Repositories.GetReadme returned error: %v", err)
97	}
98	want := &RepositoryContent{Type: String("file"), Name: String("README.md"), Size: Int(5362), Encoding: String("base64"), Path: String("README.md")}
99	if !reflect.DeepEqual(readme, want) {
100		t.Errorf("Repositories.GetReadme returned %+v, want %+v", readme, want)
101	}
102}
103
104func TestRepositoriesService_DownloadContents_Success(t *testing.T) {
105	client, mux, serverURL, teardown := setup()
106	defer teardown()
107	mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) {
108		testMethod(t, r, "GET")
109		fmt.Fprint(w, `[{
110		  "type": "file",
111		  "name": "f",
112		  "download_url": "`+serverURL+baseURLPath+`/download/f"
113		}]`)
114	})
115	mux.HandleFunc("/download/f", func(w http.ResponseWriter, r *http.Request) {
116		testMethod(t, r, "GET")
117		fmt.Fprint(w, "foo")
118	})
119
120	r, err := client.Repositories.DownloadContents(context.Background(), "o", "r", "d/f", nil)
121	if err != nil {
122		t.Errorf("Repositories.DownloadContents returned error: %v", err)
123	}
124
125	bytes, err := ioutil.ReadAll(r)
126	if err != nil {
127		t.Errorf("Error reading response body: %v", err)
128	}
129	r.Close()
130
131	if got, want := string(bytes), "foo"; got != want {
132		t.Errorf("Repositories.DownloadContents returned %v, want %v", got, want)
133	}
134}
135
136func TestRepositoriesService_DownloadContents_NoDownloadURL(t *testing.T) {
137	client, mux, _, teardown := setup()
138	defer teardown()
139	mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) {
140		testMethod(t, r, "GET")
141		fmt.Fprint(w, `[{
142		  "type": "file",
143		  "name": "f",
144		}]`)
145	})
146
147	_, err := client.Repositories.DownloadContents(context.Background(), "o", "r", "d/f", nil)
148	if err == nil {
149		t.Errorf("Repositories.DownloadContents did not return expected error")
150	}
151}
152
153func TestRepositoriesService_DownloadContents_NoFile(t *testing.T) {
154	client, mux, _, teardown := setup()
155	defer teardown()
156	mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) {
157		testMethod(t, r, "GET")
158		fmt.Fprint(w, `[]`)
159	})
160
161	_, err := client.Repositories.DownloadContents(context.Background(), "o", "r", "d/f", nil)
162	if err == nil {
163		t.Errorf("Repositories.DownloadContents did not return expected error")
164	}
165}
166
167func TestRepositoriesService_GetContents_File(t *testing.T) {
168	client, mux, _, teardown := setup()
169	defer teardown()
170	mux.HandleFunc("/repos/o/r/contents/p", func(w http.ResponseWriter, r *http.Request) {
171		testMethod(t, r, "GET")
172		fmt.Fprint(w, `{
173		  "type": "file",
174		  "encoding": "base64",
175		  "size": 20678,
176		  "name": "LICENSE",
177		  "path": "LICENSE"
178		}`)
179	})
180	fileContents, _, _, err := client.Repositories.GetContents(context.Background(), "o", "r", "p", &RepositoryContentGetOptions{})
181	if err != nil {
182		t.Errorf("Repositories.GetContents returned error: %v", err)
183	}
184	want := &RepositoryContent{Type: String("file"), Name: String("LICENSE"), Size: Int(20678), Encoding: String("base64"), Path: String("LICENSE")}
185	if !reflect.DeepEqual(fileContents, want) {
186		t.Errorf("Repositories.GetContents returned %+v, want %+v", fileContents, want)
187	}
188}
189
190func TestRepositoriesService_GetContents_FilenameNeedsEscape(t *testing.T) {
191	client, mux, _, teardown := setup()
192	defer teardown()
193	mux.HandleFunc("/repos/o/r/contents/p#?%/中.go", func(w http.ResponseWriter, r *http.Request) {
194		testMethod(t, r, "GET")
195		fmt.Fprint(w, `{}`)
196	})
197	_, _, _, err := client.Repositories.GetContents(context.Background(), "o", "r", "p#?%/中.go", &RepositoryContentGetOptions{})
198	if err != nil {
199		t.Fatalf("Repositories.GetContents returned error: %v", err)
200	}
201}
202
203func TestRepositoriesService_GetContents_DirectoryWithSpaces(t *testing.T) {
204	client, mux, _, teardown := setup()
205	defer teardown()
206	mux.HandleFunc("/repos/o/r/contents/some directory/file.go", func(w http.ResponseWriter, r *http.Request) {
207		testMethod(t, r, "GET")
208		fmt.Fprint(w, `{}`)
209	})
210	_, _, _, err := client.Repositories.GetContents(context.Background(), "o", "r", "some directory/file.go", &RepositoryContentGetOptions{})
211	if err != nil {
212		t.Fatalf("Repositories.GetContents returned error: %v", err)
213	}
214}
215
216func TestRepositoriesService_GetContents_DirectoryWithPlusChars(t *testing.T) {
217	client, mux, _, teardown := setup()
218	defer teardown()
219	mux.HandleFunc("/repos/o/r/contents/some directory+name/file.go", func(w http.ResponseWriter, r *http.Request) {
220		testMethod(t, r, "GET")
221		fmt.Fprint(w, `{}`)
222	})
223	_, _, _, err := client.Repositories.GetContents(context.Background(), "o", "r", "some directory+name/file.go", &RepositoryContentGetOptions{})
224	if err != nil {
225		t.Fatalf("Repositories.GetContents returned error: %v", err)
226	}
227}
228
229func TestRepositoriesService_GetContents_Directory(t *testing.T) {
230	client, mux, _, teardown := setup()
231	defer teardown()
232	mux.HandleFunc("/repos/o/r/contents/p", func(w http.ResponseWriter, r *http.Request) {
233		testMethod(t, r, "GET")
234		fmt.Fprint(w, `[{
235		  "type": "dir",
236		  "name": "lib",
237		  "path": "lib"
238		},
239		{
240		  "type": "file",
241		  "size": 20678,
242		  "name": "LICENSE",
243		  "path": "LICENSE"
244		}]`)
245	})
246	_, directoryContents, _, err := client.Repositories.GetContents(context.Background(), "o", "r", "p", &RepositoryContentGetOptions{})
247	if err != nil {
248		t.Errorf("Repositories.GetContents returned error: %v", err)
249	}
250	want := []*RepositoryContent{{Type: String("dir"), Name: String("lib"), Path: String("lib")},
251		{Type: String("file"), Name: String("LICENSE"), Size: Int(20678), Path: String("LICENSE")}}
252	if !reflect.DeepEqual(directoryContents, want) {
253		t.Errorf("Repositories.GetContents_Directory returned %+v, want %+v", directoryContents, want)
254	}
255}
256
257func TestRepositoriesService_CreateFile(t *testing.T) {
258	client, mux, _, teardown := setup()
259	defer teardown()
260	mux.HandleFunc("/repos/o/r/contents/p", func(w http.ResponseWriter, r *http.Request) {
261		testMethod(t, r, "PUT")
262		fmt.Fprint(w, `{
263			"content":{
264				"name":"p"
265			},
266			"commit":{
267				"message":"m",
268				"sha":"f5f369044773ff9c6383c087466d12adb6fa0828"
269			}
270		}`)
271	})
272	message := "m"
273	content := []byte("c")
274	repositoryContentsOptions := &RepositoryContentFileOptions{
275		Message:   &message,
276		Content:   content,
277		Committer: &CommitAuthor{Name: String("n"), Email: String("e")},
278	}
279	createResponse, _, err := client.Repositories.CreateFile(context.Background(), "o", "r", "p", repositoryContentsOptions)
280	if err != nil {
281		t.Errorf("Repositories.CreateFile returned error: %v", err)
282	}
283	want := &RepositoryContentResponse{
284		Content: &RepositoryContent{Name: String("p")},
285		Commit: Commit{
286			Message: String("m"),
287			SHA:     String("f5f369044773ff9c6383c087466d12adb6fa0828"),
288		},
289	}
290	if !reflect.DeepEqual(createResponse, want) {
291		t.Errorf("Repositories.CreateFile returned %+v, want %+v", createResponse, want)
292	}
293}
294
295func TestRepositoriesService_UpdateFile(t *testing.T) {
296	client, mux, _, teardown := setup()
297	defer teardown()
298	mux.HandleFunc("/repos/o/r/contents/p", func(w http.ResponseWriter, r *http.Request) {
299		testMethod(t, r, "PUT")
300		fmt.Fprint(w, `{
301			"content":{
302				"name":"p"
303			},
304			"commit":{
305				"message":"m",
306				"sha":"f5f369044773ff9c6383c087466d12adb6fa0828"
307			}
308		}`)
309	})
310	message := "m"
311	content := []byte("c")
312	sha := "f5f369044773ff9c6383c087466d12adb6fa0828"
313	repositoryContentsOptions := &RepositoryContentFileOptions{
314		Message:   &message,
315		Content:   content,
316		SHA:       &sha,
317		Committer: &CommitAuthor{Name: String("n"), Email: String("e")},
318	}
319	updateResponse, _, err := client.Repositories.UpdateFile(context.Background(), "o", "r", "p", repositoryContentsOptions)
320	if err != nil {
321		t.Errorf("Repositories.UpdateFile returned error: %v", err)
322	}
323	want := &RepositoryContentResponse{
324		Content: &RepositoryContent{Name: String("p")},
325		Commit: Commit{
326			Message: String("m"),
327			SHA:     String("f5f369044773ff9c6383c087466d12adb6fa0828"),
328		},
329	}
330	if !reflect.DeepEqual(updateResponse, want) {
331		t.Errorf("Repositories.UpdateFile returned %+v, want %+v", updateResponse, want)
332	}
333}
334
335func TestRepositoriesService_DeleteFile(t *testing.T) {
336	client, mux, _, teardown := setup()
337	defer teardown()
338	mux.HandleFunc("/repos/o/r/contents/p", func(w http.ResponseWriter, r *http.Request) {
339		testMethod(t, r, "DELETE")
340		fmt.Fprint(w, `{
341			"content": null,
342			"commit":{
343				"message":"m",
344				"sha":"f5f369044773ff9c6383c087466d12adb6fa0828"
345			}
346		}`)
347	})
348	message := "m"
349	sha := "f5f369044773ff9c6383c087466d12adb6fa0828"
350	repositoryContentsOptions := &RepositoryContentFileOptions{
351		Message:   &message,
352		SHA:       &sha,
353		Committer: &CommitAuthor{Name: String("n"), Email: String("e")},
354	}
355	deleteResponse, _, err := client.Repositories.DeleteFile(context.Background(), "o", "r", "p", repositoryContentsOptions)
356	if err != nil {
357		t.Errorf("Repositories.DeleteFile returned error: %v", err)
358	}
359	want := &RepositoryContentResponse{
360		Content: nil,
361		Commit: Commit{
362			Message: String("m"),
363			SHA:     String("f5f369044773ff9c6383c087466d12adb6fa0828"),
364		},
365	}
366	if !reflect.DeepEqual(deleteResponse, want) {
367		t.Errorf("Repositories.DeleteFile returned %+v, want %+v", deleteResponse, want)
368	}
369}
370
371func TestRepositoriesService_GetArchiveLink(t *testing.T) {
372	client, mux, _, teardown := setup()
373	defer teardown()
374	mux.HandleFunc("/repos/o/r/tarball", func(w http.ResponseWriter, r *http.Request) {
375		testMethod(t, r, "GET")
376		http.Redirect(w, r, "http://github.com/a", http.StatusFound)
377	})
378	url, resp, err := client.Repositories.GetArchiveLink(context.Background(), "o", "r", Tarball, &RepositoryContentGetOptions{})
379	if err != nil {
380		t.Errorf("Repositories.GetArchiveLink returned error: %v", err)
381	}
382	if resp.StatusCode != http.StatusFound {
383		t.Errorf("Repositories.GetArchiveLink returned status: %d, want %d", resp.StatusCode, http.StatusFound)
384	}
385	want := "http://github.com/a"
386	if url.String() != want {
387		t.Errorf("Repositories.GetArchiveLink returned %+v, want %+v", url.String(), want)
388	}
389}
390