Merge: fix ci nitunit some
[nit.git] / lib / github / tests / test_api.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 module test_api is test
16
17 intrude import api
18
19 # GithubAPI testing
20 #
21 # To avoid test flakyness we test the GithubAPI against a mock of the real one.
22 # For each api request we return a cache file of the real API response body.
23 #
24 # Cache files can be automatically created and updated by setting
25 # `update_responses_cache` to `true` then running `nitunit`.
26 class MockGithubAPI
27 super GithubAPI
28
29 # Mock so it returns the response from a file
30 #
31 # See `update_responses_cache`.
32 redef fun send(method, path, headers, body) do
33 print path # for debugging
34
35 assert has_response(path)
36
37 if update_responses_cache then
38 var file = response_file(path)
39 save_actual_response(path, file)
40 end
41
42 var response = response_string(path)
43 if response_is_error(path) then
44 last_error = new GithubAPIError(
45 response.parse_json.as(JsonObject)["message"].as(String),
46 response_code(path).to_i,
47 path
48 )
49 was_error = true
50 return null
51 end
52 return response
53 end
54
55 var test_responses: Map[String, String] do
56 var map = new HashMap[String, String]
57 map["/user"] = "user_Morriar"
58 map["/users/Morriar"] = "user_Morriar"
59 map["/repos/nitlang/nit"] = "repo_nit"
60 map["/repos/nitlang/nit/labels?page=1&per_page=3"] = "repo_labels_nit"
61 map["/repos/nitlang/nit/labels/ok_will_merge"] = "repo_labels_ok_will_merge"
62 map["/repos/nitlang/nit/milestones?page=1&per_page=3"] = "repo_milestones_nit"
63 map["/repos/nitlang/nit/milestones/4"] = "repo_milestones_4"
64 map["/repos/nitlang/nit/branches?page=1&per_page=2"] = "repo_branches_nit"
65 map["/repos/nitlang/nit/branches/master"] = "repo_branches_master"
66 map["/repos/nitlang/nit/issues?page=1&per_page=3"] = "repo_issues_nit"
67 map["/repos/nitlang/nit/issues/1000"] = "repo_issues_1000"
68 map["/repos/nitlang/nit/issues/1000/comments?page=1&per_page=3"] = "repo_issues_comments_nit"
69 map["/repos/nitlang/nit/issues/comments/6020149"] = "repo_issues_comments_6020149"
70 map["/repos/nitlang/nit/issues/1000/events?page=1&per_page=3"] = "repo_issues_events_nit"
71 map["/repos/nitlang/nit/issues/events/199674194"] = "repo_issues_events_199674194"
72 map["/repos/nitlang/nit/pulls?page=1&per_page=3"] = "repo_pulls_nit"
73 map["/repos/nitlang/nit/pulls/1000"] = "repo_pulls_1000"
74 map["/repos/nitlang/nit/pulls/945/comments?page=1&per_page=3"] = "repo_pulls_945_comments"
75 map["/repos/nitlang/nit/pulls/comments/21010363"] = "repo_pulls_comment_21010363"
76 map["/repos/nitlang/nit/commits/64ce1f"] = "repo_commits_64ce1f"
77 map["/repos/nitlang/nit/commits/4e3c688d/status"] = "repo_commits_4e3c68_status"
78 map["/repos/nitlang/nit/comments/8982707"] = "repo_comments_8982707"
79 map["/search/issues?q=foo repo:nitlang/nit&page=1&per_page=3"] = "repo_search_issues_nit"
80 map["/repos/nitlang/nit/stats/contributors"] = "repo_nit_contributors"
81 # errors
82 map["/users/not_found/not_found"] = "errors_404"
83 return map
84 end
85
86 # Does `self` have a mock response for Github `path`?
87 fun has_response(path: String): Bool do
88 return test_responses.has_key(path)
89 end
90
91 # Root responses cache directory
92 var responses_dir: String is lazy do
93 var path = "NIT_TESTING_PATH".environ.dirname / "mock"
94 path.mkdir
95 return path
96 end
97
98 # Returns the response file path for a Github `path`
99 fun response_file(path: String): String do
100 assert has_response(path)
101 return "{responses_dir / test_responses[path]}.res"
102 end
103
104 # Returns the response body string for a Github `path`
105 fun response_string(path: String): String do
106 var file = response_file(path)
107 assert file.file_exists
108 return file.to_path.read_all
109 end
110
111 # Is this response a simulated error?
112 fun response_is_error(path: String): Bool do
113 assert has_response(path)
114 return test_responses[path].has_prefix("errors_")
115 end
116
117 # Status code of a simulated error
118 #
119 # See `response_is_error`.
120 fun response_code(path: String): String do
121 assert response_is_error(path)
122 return test_responses[path].split("_").last
123 end
124
125 # Response caching
126
127 # Activate caching
128 #
129 # Change this value to `true` then run nitunit to cache the responses
130 # from the Github API.
131 #
132 # Default is `false`.
133 var update_responses_cache = false
134
135 # Save the actual Github API response body for `uri` to a `file`
136 private fun save_actual_response(uri, file: String) do
137 assert update_responses_cache
138
139 var request = new CurlHTTPRequest("{api_url}{sanitize_uri(uri)}")
140 request.user_agent = actual_api.user_agent
141 request.headers = actual_api.new_headers
142 var response = request.execute
143
144 if response isa CurlResponseSuccess then
145 response.body_str.write_to_file(file)
146 else if response isa CurlResponseFailed then
147 response.error_msg.write_to_file(file)
148 else abort
149
150 print "Response to `{uri}` saved at `{file}`"
151 end
152
153 # Actual GithubCurl instance used for caching
154 private var actual_api = new GithubAPI(get_github_oauth, "nitunit")
155 end
156
157 class TestGithubAPI
158 test
159
160 fun api: MockGithubAPI do return new MockGithubAPI("test", "test")
161
162 fun test_deserialize is test do
163 var response = api.response_string("/users/Morriar")
164 var obj = api.deserialize(response)
165 assert obj isa User
166 assert obj.login == "Morriar"
167 end
168
169 fun test_sanitize_url is test do
170 # TODO better tests
171 assert api.sanitize_uri("/repos/Nit with spaces/") == "/repos/Nit%20with%20spaces/"
172 end
173
174 fun test_get is test do
175 var api = self.api
176 var obj = api.get("/users/Morriar")
177 assert not api.was_error
178 assert api.last_error == null
179 assert obj isa User
180 assert obj.login == "Morriar"
181 end
182
183 fun test_get_404 is test do
184 var api = self.api
185 var res = api.get("/users/not_found/not_found")
186 assert res == null
187 assert api.was_error
188 var err = api.last_error
189 assert err isa GithubAPIError
190 assert err.status_code == 404
191 assert err.message == "Not Found"
192 end
193
194 # TODO test more error cases
195
196 fun test_get_auth_user is test do
197 var user = api.get_auth_user
198 assert user isa User
199 assert user.login == "Morriar"
200 assert user.avatar_url == "https://avatars2.githubusercontent.com/u/583144?v=4"
201 assert user.name == "Alexandre Terrasa"
202 assert user.email == "alexandre@moz-code.org"
203 assert user.blog == "moz-code.org"
204 end
205
206 fun test_get_user is test do
207 var user = api.get_user("Morriar")
208 assert user isa User
209 assert user.login == "Morriar"
210 assert user.avatar_url == "https://avatars2.githubusercontent.com/u/583144?v=4"
211 assert user.name == "Alexandre Terrasa"
212 assert user.email == "alexandre@moz-code.org"
213 assert user.blog == "moz-code.org"
214 end
215
216 fun test_get_repo is test do
217 var repo = api.get_repo("nitlang/nit")
218 assert repo isa Repo
219 assert repo.full_name == "nitlang/nit"
220 assert repo.name == "nit"
221 assert repo.owner.login == "nitlang"
222 assert repo.default_branch == "master"
223 end
224
225 fun test_get_branches is test do
226 var branches = api.get_repo_branches("nitlang/nit", 1, 2)
227 assert branches.length == 2
228 assert branches.first.name == "master"
229 assert branches.last.name == "next"
230 end
231
232 fun test_get_issues is test do
233 var issues = api.get_repo_issues("nitlang/nit", 1, 3)
234 assert issues.length == 3
235 assert issues.first.title == "nitrpg: Move `nitrpg` to its own repository"
236 assert issues.last.title == "Mock Github API tests"
237 end
238
239 fun test_search_issues is test do
240 var results = api.search_repo_issues("nitlang/nit", "foo", 1, 3)
241 assert results isa SearchResults
242 assert results.items.length == 3
243 assert results.items.first.as(Issue).title == "Introduction of contracts in Nit"
244 assert results.items.last.as(Issue).title == "Appel de méthodes abstraites non redéfinies"
245 end
246
247 fun test_get_labels is test do
248 var labels = api.get_repo_labels("nitlang/nit", 1, 3)
249 assert labels.length == 3
250 assert labels.first.name == "API"
251 assert labels.last.name == "NEP"
252 end
253
254 fun test_get_milestones is test do
255 var milestones = api.get_repo_milestones("nitlang/nit", 1, 3)
256 assert milestones.length == 3
257 assert milestones.first.title == "v1.0prealpha"
258 assert milestones.last.title == "nitdoc - Abstraction levels"
259 end
260
261 fun test_get_pulls is test do
262 var pulls = api.get_repo_pulls("nitlang/nit", 1, 3)
263 assert pulls.length == 3
264 assert pulls.first.title == "nitrpg: Move `nitrpg` to its own repository"
265 assert pulls.last.title == "Mock Github API tests"
266 end
267
268 fun test_get_branch is test do
269 var branch = api.get_branch("nitlang/nit", "master")
270 assert branch isa Branch
271 assert branch.name == "master"
272 end
273
274 fun test_get_commit is test do
275 var commit = api.get_commit("nitlang/nit", "64ce1f")
276 assert commit isa Commit
277 assert commit.sha == "64ce1f587209024f5de46d06c70526a569ff537f"
278 # TODO other fields
279 end
280
281 fun test_get_commit_status is test do
282 var status = api.get_commit_status("nitlang/nit", "4e3c688d")
283 assert status isa CommitStatus
284 assert status.state == "failure"
285 assert status.sha == "4e3c688d2c4b875c00f206eb4c4b6f2c4f34c096"
286 assert status.total_count == 1
287
288 var sub = status.statuses.first
289 assert sub.state == "failure"
290 assert sub.description == "Merged pipeline on gitlab: failed"
291 assert sub.context == "gitlab-ci"
292
293 var repo = status.repository
294 assert repo isa Repo
295 assert repo.full_name == "nitlang/nit"
296 end
297
298 fun test_get_issue is test do
299 var issue = api.get_issue("nitlang/nit", 1000)
300 assert issue isa Issue
301 assert issue.number == 1000
302 assert issue.title == "Raise nitc from the dead"
303 assert issue.user.as(User).login == "privat"
304 assert issue.comments == 7
305 assert issue.created_at == "2014-12-11T02:55:09Z"
306 assert issue.closed_at == "2014-12-13T15:38:09Z"
307 assert issue.closed_by.as(User).login == "privat"
308 assert issue.body == "Raise dead on `nitc`.\nIt's super effective...\n"
309 assert issue.is_pull_request
310 end
311
312 fun test_get_issue_comments is test do
313 var comments = api.get_issue_comments("nitlang/nit", 1000, 1, 3)
314 assert comments.length == 3
315 assert comments.first.user.login == "R4PaSs"
316 assert comments.last.user.login == "xymus"
317 end
318
319 fun test_get_issue_events is test do
320 var events = api.get_issue_events("nitlang/nit", 1000, 1, 3)
321 assert events.length == 3
322 assert events.first.actor.login == "privat"
323 assert events.last.actor.login == "xymus"
324 end
325
326 fun test_get_pull is test do
327 var pull = api.get_pull("nitlang/nit", 1000)
328 assert pull isa PullRequest
329 assert pull.number == 1000
330 assert pull.title == "Raise nitc from the dead"
331 assert pull.user.as(User).login == "privat"
332 assert pull.comments == 7
333 assert pull.created_at == "2014-12-11T02:55:09Z"
334 assert pull.closed_at == "2014-12-13T15:38:09Z"
335 assert pull.merged_by.as(User).login == "privat"
336 assert pull.body == "Raise dead on `nitc`.\nIt's super effective...\n"
337 end
338
339 fun test_get_pull_comments is test do
340 var comments = api.get_pull_comments("nitlang/nit", 945, 1, 3)
341 assert comments.length == 2
342 assert comments.first.path == "src/modelize/modelize_property.nit"
343 end
344
345 fun test_get_pull_comment is test do
346 var comment = api.get_pull_comment("nitlang/nit", 21010363)
347 assert comment isa PullComment
348 assert comment.path == "src/modelize/modelize_property.nit"
349 assert comment.original_position == 26
350 assert comment.pull_number == 945
351 # TODO other fields
352 end
353
354 fun test_get_label is test do
355 var labl = api.get_label("nitlang/nit", "ok_will_merge")
356 assert labl isa Label
357 assert labl.name == "ok_will_merge"
358 end
359
360 fun test_get_milestone is test do
361 var milestone = api.get_milestone("nitlang/nit", 4)
362 assert milestone isa Milestone
363 assert milestone.title == "v1.0prealpha"
364 # TODO other fields
365 end
366
367 fun test_get_issue_event is test do
368 var event = api.get_issue_event("nitlang/nit", 199674194)
369 assert event isa IssueEvent
370 assert event.actor.login == "privat"
371 assert event.event == "labeled"
372 assert event.labl.as(Label).name == "need_review"
373 end
374
375 fun test_get_issue_comment is test do
376 var comment = api.get_issue_comment("nitlang/nit", 6020149)
377 assert comment isa IssueComment
378 assert comment.user.login == "privat"
379 assert comment.created_at.to_s == "2012-05-30T20:16:54Z"
380 assert comment.issue_number == 10
381 end
382
383 fun test_get_comment is test do
384 var comment = api.get_commit_comment("nitlang/nit", 8982707)
385 assert comment isa CommitComment
386 assert comment.user.login == "Morriar"
387 assert comment.body == "For testing purposes...\n"
388 assert comment.commit_id == "7eacb86d1e24b7e72bc9ac869bf7182c0300ceca"
389 end
390
391 fun test_contributor_stats is test do
392 var stats = api.get_repo_contrib_stats("nitlang/nit")
393 assert stats.last.author.login == "privat"
394 assert stats.last.total == 4536
395 assert stats.last.weeks.length == 575
396 end
397 end