Property definitions

github $ MockGithubAPI :: defaultinit
# GithubAPI testing
#
# To avoid test flakyness we test the GithubAPI against a mock of the real one.
# For each api request we return a cache file of the real API response body.
#
# Cache files can be automatically created and updated by setting
# `update_responses_cache` to `true` then running `nitunit`.
class MockGithubAPI
	super GithubAPI

	# Mock so it returns the response from a file
	#
	# See `update_responses_cache`.
	redef fun send(method, path, headers, body) do
		print path # for debugging

		assert has_response(path)

		if update_responses_cache then
			var file = response_file(path)
			save_actual_response(path, file)
		end

		var response = response_string(path)
		if response_is_error(path) then
			last_error = new GithubAPIError(
				response.parse_json.as(JsonObject)["message"].as(String),
				response_code(path).to_i,
				path
			)
			was_error = true
			return null
		end
		return response
	end

	var test_responses: Map[String, String] do
		var map = new HashMap[String, String]
		map["/user"] = "user_Morriar"
		map["/users/Morriar"] = "user_Morriar"
		map["/repos/nitlang/nit"] = "repo_nit"
		map["/repos/nitlang/nit/labels?page=1&per_page=3"] = "repo_labels_nit"
		map["/repos/nitlang/nit/labels/ok_will_merge"] = "repo_labels_ok_will_merge"
		map["/repos/nitlang/nit/milestones?page=1&per_page=3"] = "repo_milestones_nit"
		map["/repos/nitlang/nit/milestones/4"] = "repo_milestones_4"
		map["/repos/nitlang/nit/branches?page=1&per_page=2"] = "repo_branches_nit"
		map["/repos/nitlang/nit/branches/master"] = "repo_branches_master"
		map["/repos/nitlang/nit/issues?page=1&per_page=3"] = "repo_issues_nit"
		map["/repos/nitlang/nit/issues/1000"] = "repo_issues_1000"
		map["/repos/nitlang/nit/issues/1000/comments?page=1&per_page=3"] = "repo_issues_comments_nit"
		map["/repos/nitlang/nit/issues/comments/6020149"] = "repo_issues_comments_6020149"
		map["/repos/nitlang/nit/issues/1000/events?page=1&per_page=3"] = "repo_issues_events_nit"
		map["/repos/nitlang/nit/issues/events/199674194"] = "repo_issues_events_199674194"
		map["/repos/nitlang/nit/pulls?page=1&per_page=3"] = "repo_pulls_nit"
		map["/repos/nitlang/nit/pulls/1000"] = "repo_pulls_1000"
		map["/repos/nitlang/nit/pulls/945/comments?page=1&per_page=3"] = "repo_pulls_945_comments"
		map["/repos/nitlang/nit/pulls/comments/21010363"] = "repo_pulls_comment_21010363"
		map["/repos/nitlang/nit/commits/64ce1f"] = "repo_commits_64ce1f"
		map["/repos/nitlang/nit/commits/4e3c688d/status"] = "repo_commits_4e3c68_status"
		map["/repos/nitlang/nit/comments/8982707"] = "repo_comments_8982707"
		map["/search/issues?q=foo repo:nitlang/nit&page=1&per_page=3"] = "repo_search_issues_nit"
		map["/repos/nitlang/nit/stats/contributors"] = "repo_nit_contributors"
		# errors
		map["/users/not_found/not_found"] = "errors_404"
		return map
	end

	# Does `self` have a mock response for Github `path`?
	fun has_response(path: String): Bool do
		return test_responses.has_key(path)
	end

	# Root responses cache directory
	var responses_dir: String is lazy do
		var path = "NIT_TESTING_PATH".environ.dirname / "mock"
		path.mkdir
		return path
	end

	# Returns the response file path for a Github `path`
	fun response_file(path: String): String do
		assert has_response(path)
		return "{responses_dir / test_responses[path]}.res"
	end

	# Returns the response body string for a Github `path`
	fun response_string(path: String): String do
		var file = response_file(path)
		assert file.file_exists
		return file.to_path.read_all
	end

	# Is this response a simulated error?
	fun response_is_error(path: String): Bool do
		assert has_response(path)
		return test_responses[path].has_prefix("errors_")
	end

	# Status code of a simulated error
	#
	# See `response_is_error`.
	fun response_code(path: String): String do
		assert response_is_error(path)
		return test_responses[path].split("_").last
	end

	# Response caching

	# Activate caching
	#
	# Change this value to `true` then run nitunit to cache the responses
	# from the Github API.
	#
	# Default is `false`.
	var update_responses_cache = false

	# Save the actual Github API response body for `uri` to a `file`
	private fun save_actual_response(uri, file: String) do
		assert update_responses_cache

		var request = new CurlHTTPRequest("{api_url}{sanitize_uri(uri)}")
		request.user_agent = actual_api.user_agent
		request.headers = actual_api.new_headers
		var response = request.execute

		if response isa CurlResponseSuccess then
			response.body_str.write_to_file(file)
		else if response isa CurlResponseFailed then
			response.error_msg.write_to_file(file)
		else abort

		print "Response to `{uri}` saved at `{file}`"
	end

	# Actual GithubCurl instance used for caching
	private var actual_api = new GithubAPI(get_github_oauth, "nitunit")
end
lib/github/tests/test_api.nit:19,1--155,3