rickub/clipublic Fork 0
main
Commits
Clone
git clone https://git.rickub.com/rickub/cli.git
git clone ssh://git@rickub.com/rickub/cli.git
client_test.go · 221 lines · 6.1 KBGo Blame HistoryRaw
Initial import of the rickub CLI as a standalone public project 1a1d430 Olivier Girardot 11h ago1package api
2
3import (
4 "context"
5 "encoding/json"
6 "errors"
7 "net/http"
8 "net/http/httptest"
9 "testing"
10)
11
12func TestAuthHeaderAndPath(t *testing.T) {
13 var gotAuth, gotPath, gotAccept string
14 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
15 gotAuth = r.Header.Get("Authorization")
16 gotPath = r.URL.Path
17 gotAccept = r.Header.Get("Accept")
18 json.NewEncoder(w).Encode(User{Handle: "ricktester"})
19 }))
20 defer srv.Close()
21
22 c := New(srv.URL, "rickub_pat_secret")
23 u, err := c.GetUser(context.Background())
24 if err != nil {
25 t.Fatalf("GetUser: %v", err)
26 }
27 if u.Handle != "ricktester" {
28 t.Errorf("handle = %q", u.Handle)
29 }
30 if gotAuth != "Bearer rickub_pat_secret" {
31 t.Errorf("auth header = %q", gotAuth)
32 }
33 if gotPath != "/api/v1/user" {
34 t.Errorf("path = %q", gotPath)
35 }
36 if gotAccept != "application/json" {
37 t.Errorf("accept = %q", gotAccept)
38 }
39}
40
41func TestErrorEnvelopeParsed(t *testing.T) {
42 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
43 w.WriteHeader(http.StatusNotFound)
44 w.Write([]byte(`{"error":{"code":"not_found","message":"repository not found"}}`))
45 }))
46 defer srv.Close()
47
48 c := New(srv.URL, "t")
49 _, err := c.GetRepo(context.Background(), "who", "what")
50 if err == nil {
51 t.Fatal("expected error")
52 }
53 var apiErr *APIError
54 if !errors.As(err, &apiErr) {
55 t.Fatalf("expected *APIError, got %T", err)
56 }
57 if apiErr.Status != http.StatusNotFound {
58 t.Errorf("status = %d", apiErr.Status)
59 }
60 if apiErr.Code != "not_found" {
61 t.Errorf("code = %q", apiErr.Code)
62 }
63 if apiErr.Message != "repository not found" {
64 t.Errorf("message = %q", apiErr.Message)
65 }
66 if apiErr.Error() != "repository not found (not_found)" {
67 t.Errorf("Error() = %q", apiErr.Error())
68 }
69}
70
71func TestNonJSONErrorFallback(t *testing.T) {
72 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
73 w.WriteHeader(http.StatusBadGateway)
74 w.Write([]byte("upstream boom"))
75 }))
76 defer srv.Close()
77
78 c := New(srv.URL, "t")
79 _, err := c.GetUser(context.Background())
80 var apiErr *APIError
81 if !errors.As(err, &apiErr) {
82 t.Fatalf("expected *APIError, got %v", err)
83 }
84 if apiErr.Message != "upstream boom" {
85 t.Errorf("message = %q", apiErr.Message)
86 }
87}
88
89func TestPaginationQueryAndDecode(t *testing.T) {
90 var gotQuery string
91 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
92 gotQuery = r.URL.RawQuery
93 json.NewEncoder(w).Encode(map[string]any{
94 "page": 2,
95 "per_page": 5,
96 "has_next": true,
97 "items": []map[string]any{
98 {"owner": "ricktester", "name": "demo", "full_name": "ricktester/demo", "visibility": "public"},
99 },
100 })
101 }))
102 defer srv.Close()
103
104 c := New(srv.URL, "t")
105 page, err := c.ListUserRepos(context.Background(), "ricktester", 2, 5)
106 if err != nil {
107 t.Fatalf("ListUserRepos: %v", err)
108 }
109 if gotQuery != "page=2&per_page=5" {
110 t.Errorf("query = %q", gotQuery)
111 }
112 if page.Page.Page != 2 || page.PerPage != 5 || !page.HasNext {
113 t.Errorf("page envelope = %+v", page.Page)
114 }
115 if len(page.Items) != 1 || page.Items[0].FullName != "ricktester/demo" {
116 t.Errorf("items = %+v", page.Items)
117 }
118}
119
120func TestSearchQueryEncoding(t *testing.T) {
121 var gotQuery string
122 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
123 gotQuery = r.URL.RawQuery
124 json.NewEncoder(w).Encode(RepoPage{})
125 }))
126 defer srv.Close()
127
128 c := New(srv.URL, "t")
129 if _, err := c.SearchRepos(context.Background(), "hello world", 0, 0); err != nil {
130 t.Fatalf("SearchRepos: %v", err)
131 }
132 if gotQuery != "q=hello+world" {
133 t.Errorf("query = %q", gotQuery)
134 }
135}
136
137func TestCreateRepoSendsBody(t *testing.T) {
138 var gotMethod string
139 var body RepoCreate
140 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
141 gotMethod = r.Method
142 json.NewDecoder(r.Body).Decode(&body)
143 if ct := r.Header.Get("Content-Type"); ct != "application/json" {
144 t.Errorf("content-type = %q", ct)
145 }
146 w.WriteHeader(http.StatusCreated)
147 json.NewEncoder(w).Encode(Repo{FullName: "ricktester/demo", Visibility: "public"})
148 }))
149 defer srv.Close()
150
151 c := New(srv.URL, "t")
152 r, err := c.CreateRepo(context.Background(), RepoCreate{Name: "demo", Visibility: "public"})
153 if err != nil {
154 t.Fatalf("CreateRepo: %v", err)
155 }
156 if gotMethod != http.MethodPost {
157 t.Errorf("method = %s", gotMethod)
158 }
159 if body.Name != "demo" || body.Visibility != "public" {
160 t.Errorf("body = %+v", body)
161 }
162 if r.FullName != "ricktester/demo" {
163 t.Errorf("full_name = %q", r.FullName)
164 }
165}
166
167func TestDeleteRepoNoContent(t *testing.T) {
168 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
169 if r.Method != http.MethodDelete {
170 t.Errorf("method = %s", r.Method)
171 }
172 w.WriteHeader(http.StatusNoContent)
173 }))
174 defer srv.Close()
175
176 c := New(srv.URL, "t")
177 if err := c.DeleteRepo(context.Background(), "o", "r"); err != nil {
178 t.Fatalf("DeleteRepo: %v", err)
179 }
180}
181
182func TestRawTextAcceptHeader(t *testing.T) {
183 var gotAccept string
184 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
185 gotAccept = r.Header.Get("Accept")
186 w.Header().Set("Content-Type", "text/plain")
187 w.Write([]byte("hello\n"))
188 }))
189 defer srv.Close()
190
191 c := New(srv.URL, "t")
192 data, err := c.GetRaw(context.Background(), "o", "r", "main", "README.md")
193 if err != nil {
194 t.Fatalf("GetRaw: %v", err)
195 }
196 if gotAccept != "text/plain" {
197 t.Errorf("accept = %q", gotAccept)
198 }
199 if string(data) != "hello\n" {
200 t.Errorf("data = %q", data)
201 }
202}
203
204func TestContentsPathEscaping(t *testing.T) {
205 var gotPath string
206 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
207 gotPath = r.URL.EscapedPath()
208 json.NewEncoder(w).Encode(Contents{Type: "file", File: &Blob{Path: "a/b.txt", Content: "hi"}})
209 }))
210 defer srv.Close()
211
212 c := New(srv.URL, "t")
213 if _, err := c.GetContents(context.Background(), "o", "r", "main", "dir/sub/file name.txt"); err != nil {
214 t.Fatalf("GetContents: %v", err)
215 }
216 // Each segment escaped, slashes preserved.
217 want := "/api/v1/repos/o/r/contents/main/dir/sub/file%20name.txt"
218 if gotPath != want {
219 t.Errorf("path = %q, want %q", gotPath, want)
220 }
221}