Merge branch 'aw/all-merged' of https://github.com/olleolleolle/github-changelog-generator into olleolleolle-aw/all-merged

This commit is contained in:
Petr Korolev
2016-09-29 19:12:21 +03:00
41 changed files with 1133 additions and 501 deletions
+21
View File
@@ -19,6 +19,8 @@
require "codeclimate-test-reporter"
require "simplecov"
require "coveralls"
require "vcr"
require "webmock/rspec"
# This module is only used to check the environment is currently a testing env
module SpecHelper
@@ -36,6 +38,25 @@ end
require "github_changelog_generator"
require "github_changelog_generator/task"
VCR.configure do |c|
c.allow_http_connections_when_no_cassette = true
c.cassette_library_dir = "spec/vcr"
c.ignore_localhost = true
c.default_cassette_options = {
record: :new_episodes,
serialize_with: :json,
preserve_exact_body_bytes: true,
decode_compressed_response: true
}
c.filter_sensitive_data("<GITHUB_TOKEN>") do
"token #{ENV.fetch('CHANGELOG_GITHUB_TOKEN') { 'frobnitz' }}"
end
c.configure_rspec_metadata!
c.hook_into :webmock, :faraday
end
RSpec.configure do |config|
config.expect_with :rspec do |expectations|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
-60
View File
@@ -1,60 +0,0 @@
# frozen_string_literal: true
VALID_TOKEN = "0123456789abcdef"
INVALID_TOKEN = "0000000000000000"
DEFAULT_OPTIONS = { user: "skywinder",
project: "changelog_test" }
def options_with_invalid_token
options = DEFAULT_OPTIONS
options[:token] = INVALID_TOKEN
options
end
describe GitHubChangelogGenerator::Fetcher do
before(:all) do
@fetcher = GitHubChangelogGenerator::Fetcher.new
end
describe "#fetch_github_token" do
token = GitHubChangelogGenerator::Fetcher::CHANGELOG_GITHUB_TOKEN
context "when token in ENV exist" do
before { stub_const("ENV", ENV.to_hash.merge(token => VALID_TOKEN)) }
subject { @fetcher.fetch_github_token }
it { is_expected.to eq(VALID_TOKEN) }
end
context "when token in ENV is nil" do
before { stub_const("ENV", ENV.to_hash.merge(token => nil)) }
subject { @fetcher.fetch_github_token }
it { is_expected.to be_nil }
end
context "when token in options and ENV is nil" do
before do
stub_const("ENV", ENV.to_hash.merge(token => nil))
@fetcher = GitHubChangelogGenerator::Fetcher.new(token: VALID_TOKEN)
end
subject { @fetcher.fetch_github_token }
it { is_expected.to eq(VALID_TOKEN) }
end
context "when token in options and ENV specified" do
before do
stub_const("ENV", ENV.to_hash.merge(token => "no_matter_what"))
@fetcher = GitHubChangelogGenerator::Fetcher.new(token: VALID_TOKEN)
end
subject { @fetcher.fetch_github_token }
it { is_expected.to eq(VALID_TOKEN) }
end
end
describe "#github_fetch_tags" do
context "when wrong token provided" do
before do
options = options_with_invalid_token
@fetcher = GitHubChangelogGenerator::Fetcher.new(options)
end
it "should raise Unauthorized error" do
expect { @fetcher.github_fetch_tags }.to raise_error Github::Error::Unauthorized
end
end
end
end
@@ -2,10 +2,10 @@
module GitHubChangelogGenerator
describe Generator do
context "#exclude_issues_by_labels" do
let(:label) { double("the-bad-label", name: "BAD") }
let(:issue) { double("the-issue-to-be-excluded", labels: [label]) }
let(:good_label) { double("a-good-label", name: "GOOD") }
let(:good_issue) { double("an-issue-to-be-kept", labels: [good_label]) }
let(:label) { { "name" => "BAD" } }
let(:issue) { { "labels" => [label] } }
let(:good_label) { { "name" => "GOOD" } }
let(:good_issue) { { "labels" => [good_label] } }
let(:issues) { [issue, good_issue] }
subject(:generator) { described_class.new(exclude_labels: %w(BAD BOO)) }
+43 -40
View File
@@ -1,12 +1,14 @@
# frozen_string_literal: true
describe GitHubChangelogGenerator::Generator do
def tag_mash_with_name(tag)
Hashie::Mash.new.tap { |mash_tag| mash_tag.name = tag }
def tag_with_name(tag)
{
"name" => tag
}
end
def tags_mash_from_strings(tags_strings)
def tags_from_strings(tags_strings)
tags_strings.map do |tag|
tag_mash_with_name(tag)
tag_with_name(tag)
end
end
@@ -17,20 +19,20 @@ describe GitHubChangelogGenerator::Generator do
end
subject do
@generator.get_filtered_tags(tags_mash_from_strings(%w(1 2 3)))
@generator.get_filtered_tags(tags_from_strings(%w(1 2 3)))
end
it { is_expected.to be_a(Array) }
it { is_expected.to match_array(tags_mash_from_strings(%w(1 2 3))) }
it { is_expected.to match_array(tags_from_strings(%w(1 2 3))) }
end
context "when between_tags same as input array" do
before do
@generator = GitHubChangelogGenerator::Generator.new(between_tags: %w(1 2 3))
end
subject do
@generator.get_filtered_tags(tags_mash_from_strings(%w(1 2 3)))
@generator.get_filtered_tags(tags_from_strings(%w(1 2 3)))
end
it { is_expected.to be_a(Array) }
it { is_expected.to match_array(tags_mash_from_strings(%w(1 2 3))) }
it { is_expected.to match_array(tags_from_strings(%w(1 2 3))) }
end
context "when between_tags filled with correct values" do
@@ -38,10 +40,10 @@ describe GitHubChangelogGenerator::Generator do
@generator = GitHubChangelogGenerator::Generator.new(between_tags: %w(1 2))
end
subject do
@generator.get_filtered_tags(tags_mash_from_strings(%w(1 2 3)))
@generator.get_filtered_tags(tags_from_strings(%w(1 2 3)))
end
it { is_expected.to be_a(Array) }
it { is_expected.to match_array(tags_mash_from_strings(%w(1 2))) }
it { is_expected.to match_array(tags_from_strings(%w(1 2))) }
end
context "when between_tags filled with invalid values" do
@@ -50,133 +52,134 @@ describe GitHubChangelogGenerator::Generator do
end
subject do
@generator.get_filtered_tags(tags_mash_from_strings(%w(1 2 3)))
@generator.get_filtered_tags(tags_from_strings(%w(1 2 3)))
end
it { is_expected.to be_a(Array) }
it { is_expected.to match_array(tags_mash_from_strings(%w(1))) }
it { is_expected.to match_array(tags_from_strings(%w(1))) }
end
end
describe "#get_filtered_tags" do
subject do
generator.get_filtered_tags(tags_mash_from_strings(%w(1 2 3 4 5)))
generator.get_filtered_tags(tags_from_strings(%w(1 2 3 4 5)))
end
context "with excluded and between tags" do
let(:generator) { GitHubChangelogGenerator::Generator.new(between_tags: %w(1 2 3), exclude_tags: %w(2)) }
context "respects between tags" do
let(:generator) { GitHubChangelogGenerator::Generator.new(between_tags: %w(1 2 3)) }
it { is_expected.to be_a Array }
it { is_expected.to match_array(tags_mash_from_strings(%w(1 3))) }
it { is_expected.to match_array(tags_from_strings(%w(1 2 3))) }
end
end
describe "#filter_excluded_tags" do
subject { generator.filter_excluded_tags(tags_mash_from_strings(%w(1 2 3))) }
subject { generator.filter_excluded_tags(tags_from_strings(%w(1 2 3))) }
context "with matching string" do
let(:generator) { GitHubChangelogGenerator::Generator.new(exclude_tags: %w(3)) }
it { is_expected.to be_a Array }
it { is_expected.to match_array(tags_mash_from_strings(%w(1 2))) }
it { is_expected.to match_array(tags_from_strings(%w(1 2))) }
end
context "with non-matching string" do
let(:generator) { GitHubChangelogGenerator::Generator.new(exclude_tags: %w(invalid tags)) }
it { is_expected.to be_a Array }
it { is_expected.to match_array(tags_mash_from_strings(%w(1 2 3))) }
it { is_expected.to match_array(tags_from_strings(%w(1 2 3))) }
end
context "with matching regex" do
let(:generator) { GitHubChangelogGenerator::Generator.new(exclude_tags: /[23]/) }
it { is_expected.to be_a Array }
it { is_expected.to match_array(tags_mash_from_strings(%w(1))) }
it { is_expected.to match_array(tags_from_strings(%w(1))) }
end
context "with non-matching regex" do
let(:generator) { GitHubChangelogGenerator::Generator.new(exclude_tags: /[abc]/) }
it { is_expected.to be_a Array }
it { is_expected.to match_array(tags_mash_from_strings(%w(1 2 3))) }
it { is_expected.to match_array(tags_from_strings(%w(1 2 3))) }
end
end
describe "#filter_excluded_tags_regex" do
subject { generator.filter_excluded_tags(tags_mash_from_strings(%w(1 2 3))) }
subject { generator.filter_excluded_tags(tags_from_strings(%w(1 2 3))) }
context "with matching regex" do
let(:generator) { GitHubChangelogGenerator::Generator.new(exclude_tags_regex: "[23]") }
it { is_expected.to be_a Array }
it { is_expected.to match_array(tags_mash_from_strings(%w(1))) }
it { is_expected.to match_array(tags_from_strings(%w(1))) }
end
context "with non-matching regex" do
let(:generator) { GitHubChangelogGenerator::Generator.new(exclude_tags_regex: "[45]") }
it { is_expected.to be_a Array }
it { is_expected.to match_array(tags_mash_from_strings(%w(1 2 3))) }
it { is_expected.to match_array(tags_from_strings(%w(1 2 3))) }
end
end
describe "#filter_since_tag" do
context "with filled array" do
subject { generator.filter_since_tag(tags_mash_from_strings(%w(1 2 3))) }
subject { generator.filter_since_tag(tags_from_strings(%w(1 2 3))) }
context "with valid since tag" do
let(:generator) { GitHubChangelogGenerator::Generator.new(since_tag: "2") }
it { is_expected.to be_a Array }
it { is_expected.to match_array(tags_mash_from_strings(%w(1))) }
it { is_expected.to match_array(tags_from_strings(%w(1))) }
end
context "with invalid since tag" do
let(:generator) { GitHubChangelogGenerator::Generator.new(since_tag: "Invalid tag") }
it { is_expected.to be_a Array }
it { is_expected.to match_array(tags_mash_from_strings(%w(1 2 3))) }
it { is_expected.to match_array(tags_from_strings(%w(1 2 3))) }
end
end
context "with empty array" do
subject { generator.filter_since_tag(tags_mash_from_strings(%w())) }
subject { generator.filter_since_tag(tags_from_strings(%w())) }
context "with valid since tag" do
let(:generator) { GitHubChangelogGenerator::Generator.new(since_tag: "2") }
it { is_expected.to be_a Array }
it { is_expected.to match_array(tags_mash_from_strings(%w())) }
it { is_expected.to match_array(tags_from_strings(%w())) }
end
context "with invalid since tag" do
let(:generator) { GitHubChangelogGenerator::Generator.new(since_tag: "Invalid tag") }
it { is_expected.to be_a Array }
it { is_expected.to match_array(tags_mash_from_strings(%w())) }
it { is_expected.to match_array(tags_from_strings(%w())) }
end
end
end
describe "#filter_due_tag" do
context "with filled array" do
subject { generator.filter_due_tag(tags_mash_from_strings(%w(1 2 3))) }
subject { generator.filter_due_tag(tags_from_strings(%w(1 2 3))) }
context "with valid due tag" do
let(:generator) { GitHubChangelogGenerator::Generator.new(due_tag: "2") }
it { is_expected.to be_a Array }
it { is_expected.to match_array(tags_mash_from_strings(%w(3))) }
it { is_expected.to match_array(tags_from_strings(%w(3))) }
end
context "with invalid due tag" do
let(:generator) { GitHubChangelogGenerator::Generator.new(due_tag: "Invalid tag") }
it { is_expected.to be_a Array }
it { is_expected.to match_array(tags_mash_from_strings(%w(1 2 3))) }
it { is_expected.to match_array(tags_from_strings(%w(1 2 3))) }
end
end
context "with empty array" do
subject { generator.filter_due_tag(tags_mash_from_strings(%w())) }
subject { generator.filter_due_tag(tags_from_strings(%w())) }
context "with valid due tag" do
let(:generator) { GitHubChangelogGenerator::Generator.new(due_tag: "2") }
it { is_expected.to be_a Array }
it { is_expected.to match_array(tags_mash_from_strings(%w())) }
it { is_expected.to match_array(tags_from_strings(%w())) }
end
context "with invalid due tag" do
let(:generator) { GitHubChangelogGenerator::Generator.new(due_tag: "Invalid tag") }
it { is_expected.to be_a Array }
it { is_expected.to match_array(tags_mash_from_strings(%w())) }
it { is_expected.to match_array(tags_from_strings(%w())) }
end
end
end
@@ -191,7 +194,7 @@ describe GitHubChangelogGenerator::Generator do
end
context "fetch already filled tag" do
before { @generator.instance_variable_set :@tag_times_hash, "valid_tag" => current_time }
subject { @generator.get_time_of_tag tag_mash_with_name("valid_tag") }
subject { @generator.get_time_of_tag tag_with_name("valid_tag") }
it { is_expected.to be_a_kind_of(Time) }
it { is_expected.to eq(current_time) }
end
@@ -202,7 +205,7 @@ describe GitHubChangelogGenerator::Generator do
@generator.instance_variable_set :@fetcher, mock
end
subject do
of_tag = @generator.get_time_of_tag(tag_mash_with_name("valid_tag"))
of_tag = @generator.get_time_of_tag(tag_with_name("valid_tag"))
of_tag
end
it { is_expected.to be_a_kind_of(Time) }
@@ -229,13 +232,13 @@ describe GitHubChangelogGenerator::Generator do
@generator.sort_tags_by_date(tags)
end
context "sort unsorted tags" do
let(:tags) { tags_mash_from_strings %w(valid_tag1 valid_tag2 valid_tag3) }
let(:tags) { tags_from_strings %w(valid_tag1 valid_tag2 valid_tag3) }
it { is_expected.to be_a_kind_of(Array) }
it { is_expected.to match_array(tags.reverse!) }
end
context "sort sorted tags" do
let(:tags) { tags_mash_from_strings %w(valid_tag3 valid_tag2 valid_tag1) }
let(:tags) { tags_from_strings %w(valid_tag3 valid_tag2 valid_tag1) }
it { is_expected.to be_a_kind_of(Array) }
it { is_expected.to match_array(tags) }
+489
View File
@@ -0,0 +1,489 @@
# frozen_string_literal: true
VALID_TOKEN = "0123456789abcdef"
INVALID_TOKEN = "0000000000000000"
describe GitHubChangelogGenerator::OctoFetcher do
let(:options) do
{
user: "skywinder",
project: "changelog_test"
}
end
let(:fetcher) { GitHubChangelogGenerator::OctoFetcher.new(options) }
describe "#fetch_github_token" do
token = GitHubChangelogGenerator::OctoFetcher::CHANGELOG_GITHUB_TOKEN
context "when token in ENV exist" do
before { stub_const("ENV", ENV.to_hash.merge(token => VALID_TOKEN)) }
subject { fetcher.send(:fetch_github_token) }
it { is_expected.to eq(VALID_TOKEN) }
end
context "when token in ENV is nil" do
before { stub_const("ENV", ENV.to_hash.merge(token => nil)) }
subject { fetcher.send(:fetch_github_token) }
it { is_expected.to be_nil }
end
context "when token in options and ENV is nil" do
let(:options) { { token: VALID_TOKEN } }
before do
stub_const("ENV", ENV.to_hash.merge(token => nil))
end
subject { fetcher.send(:fetch_github_token) }
it { is_expected.to eq(VALID_TOKEN) }
end
context "when token in options and ENV specified" do
let(:options) { { token: VALID_TOKEN } }
before do
stub_const("ENV", ENV.to_hash.merge(token => "no_matter_what"))
end
subject { fetcher.send(:fetch_github_token) }
it { is_expected.to eq(VALID_TOKEN) }
end
end
describe "#get_all_tags" do
context "when github_fetch_tags returns tags" do
it "returns tags" do
mock_tags = ["tag"]
allow(fetcher).to receive(:github_fetch_tags).and_return(mock_tags)
expect(fetcher.get_all_tags).to eq(mock_tags)
end
end
end
describe "#github_fetch_tags" do
context "when wrong token provided", :vcr do
let(:options) do
{
user: "skywinder",
project: "changelog_test",
token: INVALID_TOKEN
}
end
it "should raise Unauthorized error" do
expect { fetcher.github_fetch_tags }.to raise_error SystemExit, "Error: wrong GitHub token"
end
end
context "when API call is valid", :vcr do
it "should return tags" do
expected_tags = [{ "name" => "v0.0.3",
"zipball_url" =>
"https://api.github.com/repos/skywinder/changelog_test/zipball/v0.0.3",
"tarball_url" =>
"https://api.github.com/repos/skywinder/changelog_test/tarball/v0.0.3",
"commit" =>
{ "sha" => "a0cba2b1a1ea9011ab07ee1ac140ba5a5eb8bd90",
"url" =>
"https://api.github.com/repos/skywinder/changelog_test/commits/a0cba2b1a1ea9011ab07ee1ac140ba5a5eb8bd90" } },
{ "name" => "v0.0.2",
"zipball_url" =>
"https://api.github.com/repos/skywinder/changelog_test/zipball/v0.0.2",
"tarball_url" =>
"https://api.github.com/repos/skywinder/changelog_test/tarball/v0.0.2",
"commit" =>
{ "sha" => "9b35bb13dcd15b68e7bcbf10cde5eb937a54f710",
"url" =>
"https://api.github.com/repos/skywinder/changelog_test/commits/9b35bb13dcd15b68e7bcbf10cde5eb937a54f710" } },
{ "name" => "v0.0.1",
"zipball_url" =>
"https://api.github.com/repos/skywinder/changelog_test/zipball/v0.0.1",
"tarball_url" =>
"https://api.github.com/repos/skywinder/changelog_test/tarball/v0.0.1",
"commit" =>
{ "sha" => "4c2d6d1ed58bdb24b870dcb5d9f2ceed0283d69d",
"url" =>
"https://api.github.com/repos/skywinder/changelog_test/commits/4c2d6d1ed58bdb24b870dcb5d9f2ceed0283d69d" } },
{ "name" => "0.0.4",
"zipball_url" =>
"https://api.github.com/repos/skywinder/changelog_test/zipball/0.0.4",
"tarball_url" =>
"https://api.github.com/repos/skywinder/changelog_test/tarball/0.0.4",
"commit" =>
{ "sha" => "ece0c3ab7142b21064b885061c55ede00ef6ce94",
"url" =>
"https://api.github.com/repos/skywinder/changelog_test/commits/ece0c3ab7142b21064b885061c55ede00ef6ce94" } }]
expect(fetcher.github_fetch_tags).to eq(expected_tags)
end
it "should return tags count" do
tags = fetcher.github_fetch_tags
expect(tags.size).to eq(4)
end
end
end
describe "#fetch_closed_issues_and_pr" do
context "when API call is valid", :vcr do
it "returns issues" do
issues, pull_requests = fetcher.fetch_closed_issues_and_pr
expect(issues.size).to eq(7)
expect(pull_requests.size).to eq(14)
end
it "returns issue with proper key/values" do
issues, _pull_requests = fetcher.fetch_closed_issues_and_pr
expected_issue = { "url" => "https://api.github.com/repos/skywinder/changelog_test/issues/14",
"repository_url" => "https://api.github.com/repos/skywinder/changelog_test",
"labels_url" =>
"https://api.github.com/repos/skywinder/changelog_test/issues/14/labels{/name}",
"comments_url" =>
"https://api.github.com/repos/skywinder/changelog_test/issues/14/comments",
"events_url" =>
"https://api.github.com/repos/skywinder/changelog_test/issues/14/events",
"html_url" => "https://github.com/skywinder/changelog_test/issues/14",
"id" => 95_419_412,
"number" => 14,
"title" => "Issue closed from commit from PR",
"user" =>
{ "login" => "skywinder",
"id" => 3_356_474,
"avatar_url" => "https://avatars.githubusercontent.com/u/3356474?v=3",
"gravatar_id" => "",
"url" => "https://api.github.com/users/skywinder",
"html_url" => "https://github.com/skywinder",
"followers_url" => "https://api.github.com/users/skywinder/followers",
"following_url" =>
"https://api.github.com/users/skywinder/following{/other_user}",
"gists_url" => "https://api.github.com/users/skywinder/gists{/gist_id}",
"starred_url" =>
"https://api.github.com/users/skywinder/starred{/owner}{/repo}",
"subscriptions_url" => "https://api.github.com/users/skywinder/subscriptions",
"organizations_url" => "https://api.github.com/users/skywinder/orgs",
"repos_url" => "https://api.github.com/users/skywinder/repos",
"events_url" => "https://api.github.com/users/skywinder/events{/privacy}",
"received_events_url" =>
"https://api.github.com/users/skywinder/received_events",
"type" => "User",
"site_admin" => false },
"labels" => [],
"state" => "closed",
"locked" => false,
"assignee" => nil,
"assignees" => [],
"milestone" => nil,
"comments" => 0,
"created_at" => "2015-07-16T12:06:08Z",
"updated_at" => "2015-07-16T12:21:42Z",
"closed_at" => "2015-07-16T12:21:42Z",
"body" => "" }
# Convert times to Time
expected_issue.each_pair do |k, v|
expected_issue[k] = Time.parse(v) if v =~ /^2015-/
end
expect(issues.first).to eq(expected_issue)
end
it "returns pull request with proper key/values" do
_issues, pull_requests = fetcher.fetch_closed_issues_and_pr
expected_pr = { "url" => "https://api.github.com/repos/skywinder/changelog_test/issues/21",
"repository_url" => "https://api.github.com/repos/skywinder/changelog_test",
"labels_url" =>
"https://api.github.com/repos/skywinder/changelog_test/issues/21/labels{/name}",
"comments_url" =>
"https://api.github.com/repos/skywinder/changelog_test/issues/21/comments",
"events_url" =>
"https://api.github.com/repos/skywinder/changelog_test/issues/21/events",
"html_url" => "https://github.com/skywinder/changelog_test/pull/21",
"id" => 124_925_759,
"number" => 21,
"title" => "Merged br (should appear in change log with #20)",
"user" =>
{ "login" => "skywinder",
"id" => 3_356_474,
"avatar_url" => "https://avatars.githubusercontent.com/u/3356474?v=3",
"gravatar_id" => "",
"url" => "https://api.github.com/users/skywinder",
"html_url" => "https://github.com/skywinder",
"followers_url" => "https://api.github.com/users/skywinder/followers",
"following_url" =>
"https://api.github.com/users/skywinder/following{/other_user}",
"gists_url" => "https://api.github.com/users/skywinder/gists{/gist_id}",
"starred_url" =>
"https://api.github.com/users/skywinder/starred{/owner}{/repo}",
"subscriptions_url" => "https://api.github.com/users/skywinder/subscriptions",
"organizations_url" => "https://api.github.com/users/skywinder/orgs",
"repos_url" => "https://api.github.com/users/skywinder/repos",
"events_url" => "https://api.github.com/users/skywinder/events{/privacy}",
"received_events_url" =>
"https://api.github.com/users/skywinder/received_events",
"type" => "User",
"site_admin" => false },
"labels" => [],
"state" => "closed",
"locked" => false,
"assignee" => nil,
"assignees" => [],
"milestone" => nil,
"comments" => 0,
"created_at" => "2016-01-05T09:24:08Z",
"updated_at" => "2016-01-05T09:26:53Z",
"closed_at" => "2016-01-05T09:24:27Z",
"pull_request" =>
{ "url" => "https://api.github.com/repos/skywinder/changelog_test/pulls/21",
"html_url" => "https://github.com/skywinder/changelog_test/pull/21",
"diff_url" => "https://github.com/skywinder/changelog_test/pull/21.diff",
"patch_url" => "https://github.com/skywinder/changelog_test/pull/21.patch" },
"body" =>
"to test https://github.com/skywinder/github-changelog-generator/pull/305\r\nshould appear in change log with #20" }
# Convert times to Time
expected_pr.each_pair do |k, v|
expected_pr[k] = Time.parse(v) if v =~ /^2016-01/
end
expect(pull_requests.first).to eq(expected_pr)
end
it "returns issues with labels" do
issues, _pull_requests = fetcher.fetch_closed_issues_and_pr
expected = [[], [], ["Bug"], [], ["enhancement"], ["some label"], []]
expect(issues.map { |i| i["labels"].map { |l| l["name"] } }).to eq(expected)
end
it "returns pull_requests with labels" do
_issues, pull_requests = fetcher.fetch_closed_issues_and_pr
expected = [[], [], [], [], [], ["enhancement"], [], [], ["invalid"], [], [], [], [], ["invalid"]]
expect(pull_requests.map { |i| i["labels"].map { |l| l["name"] } }).to eq(expected)
end
end
end
describe "#fetch_closed_pull_requests" do
context "when API call is valid", :vcr do
it "returns pull requests" do
pull_requests = fetcher.fetch_closed_pull_requests
expect(pull_requests.size).to eq(14)
end
it "returns correct pull request keys" do
pull_requests = fetcher.fetch_closed_pull_requests
pr = pull_requests.first
expect(pr.keys).to eq(%w(url id html_url diff_url patch_url issue_url number state locked title user body created_at updated_at closed_at merged_at merge_commit_sha assignee assignees milestone commits_url review_comments_url review_comment_url comments_url statuses_url head base _links))
end
end
end
describe "#fetch_events_async" do
context "when API call is valid", :vcr do
it "populates issues" do
issues = [{ "url" => "https://api.github.com/repos/skywinder/changelog_test/issues/14",
"repository_url" => "https://api.github.com/repos/skywinder/changelog_test",
"labels_url" =>
"https://api.github.com/repos/skywinder/changelog_test/issues/14/labels{/name}",
"comments_url" =>
"https://api.github.com/repos/skywinder/changelog_test/issues/14/comments",
"events_url" =>
"https://api.github.com/repos/skywinder/changelog_test/issues/14/events",
"html_url" => "https://github.com/skywinder/changelog_test/issues/14",
"id" => 95_419_412,
"number" => 14,
"title" => "Issue closed from commit from PR",
"user" =>
{ "login" => "skywinder",
"id" => 3_356_474,
"avatar_url" => "https://avatars.githubusercontent.com/u/3356474?v=3",
"gravatar_id" => "",
"url" => "https://api.github.com/users/skywinder",
"html_url" => "https://github.com/skywinder",
"followers_url" => "https://api.github.com/users/skywinder/followers",
"following_url" =>
"https://api.github.com/users/skywinder/following{/other_user}",
"gists_url" => "https://api.github.com/users/skywinder/gists{/gist_id}",
"starred_url" =>
"https://api.github.com/users/skywinder/starred{/owner}{/repo}",
"subscriptions_url" =>
"https://api.github.com/users/skywinder/subscriptions",
"organizations_url" => "https://api.github.com/users/skywinder/orgs",
"repos_url" => "https://api.github.com/users/skywinder/repos",
"events_url" => "https://api.github.com/users/skywinder/events{/privacy}",
"received_events_url" =>
"https://api.github.com/users/skywinder/received_events",
"type" => "User",
"site_admin" => false },
"labels" => [],
"state" => "closed",
"locked" => false,
"assignee" => nil,
"assignees" => [],
"milestone" => nil,
"comments" => 0,
"created_at" => "2015-07-16T12:06:08Z",
"updated_at" => "2015-07-16T12:21:42Z",
"closed_at" => "2015-07-16T12:21:42Z",
"body" => "" }]
# Check that they are blank to begin with
expect(issues.first["events"]).to be_nil
fetcher.fetch_events_async(issues)
issue_events = issues.first["events"]
expected_events = [{ "id" => 357_462_189,
"url" =>
"https://api.github.com/repos/skywinder/changelog_test/issues/events/357462189",
"actor" =>
{ "login" => "skywinder",
"id" => 3_356_474,
"avatar_url" => "https://avatars.githubusercontent.com/u/3356474?v=3",
"gravatar_id" => "",
"url" => "https://api.github.com/users/skywinder",
"html_url" => "https://github.com/skywinder",
"followers_url" => "https://api.github.com/users/skywinder/followers",
"following_url" =>
"https://api.github.com/users/skywinder/following{/other_user}",
"gists_url" => "https://api.github.com/users/skywinder/gists{/gist_id}",
"starred_url" =>
"https://api.github.com/users/skywinder/starred{/owner}{/repo}",
"subscriptions_url" =>
"https://api.github.com/users/skywinder/subscriptions",
"organizations_url" => "https://api.github.com/users/skywinder/orgs",
"repos_url" => "https://api.github.com/users/skywinder/repos",
"events_url" => "https://api.github.com/users/skywinder/events{/privacy}",
"received_events_url" =>
"https://api.github.com/users/skywinder/received_events",
"type" => "User",
"site_admin" => false },
"event" => "referenced",
"commit_id" => "decfe840d1a1b86e0c28700de5362d3365a29555",
"commit_url" =>
"https://api.github.com/repos/skywinder/changelog_test/commits/decfe840d1a1b86e0c28700de5362d3365a29555",
"created_at" => "2015-07-16T12:21:16Z" },
{ "id" => 357_462_542,
"url" =>
"https://api.github.com/repos/skywinder/changelog_test/issues/events/357462542",
"actor" =>
{ "login" => "skywinder",
"id" => 3_356_474,
"avatar_url" => "https://avatars.githubusercontent.com/u/3356474?v=3",
"gravatar_id" => "",
"url" => "https://api.github.com/users/skywinder",
"html_url" => "https://github.com/skywinder",
"followers_url" => "https://api.github.com/users/skywinder/followers",
"following_url" =>
"https://api.github.com/users/skywinder/following{/other_user}",
"gists_url" => "https://api.github.com/users/skywinder/gists{/gist_id}",
"starred_url" =>
"https://api.github.com/users/skywinder/starred{/owner}{/repo}",
"subscriptions_url" =>
"https://api.github.com/users/skywinder/subscriptions",
"organizations_url" => "https://api.github.com/users/skywinder/orgs",
"repos_url" => "https://api.github.com/users/skywinder/repos",
"events_url" => "https://api.github.com/users/skywinder/events{/privacy}",
"received_events_url" =>
"https://api.github.com/users/skywinder/received_events",
"type" => "User",
"site_admin" => false },
"event" => "closed",
"commit_id" => "decfe840d1a1b86e0c28700de5362d3365a29555",
"commit_url" =>
"https://api.github.com/repos/skywinder/changelog_test/commits/decfe840d1a1b86e0c28700de5362d3365a29555",
"created_at" => "2015-07-16T12:21:42Z" }]
# Convert times to Time
expected_events.map! do |event|
event.each_pair do |k, v|
event[k] = Time.parse(v) if v =~ /^201[56]-/
end
end
expect(issue_events).to eq(expected_events)
end
end
end
describe "#fetch_date_of_tag" do
context "when API call is valid", :vcr do
it "returns date" do
tag = { "name" => "v0.0.3",
"zipball_url" =>
"https://api.github.com/repos/skywinder/changelog_test/zipball/v0.0.3",
"tarball_url" =>
"https://api.github.com/repos/skywinder/changelog_test/tarball/v0.0.3",
"commit" =>
{ "sha" => "a0cba2b1a1ea9011ab07ee1ac140ba5a5eb8bd90",
"url" =>
"https://api.github.com/repos/skywinder/changelog_test/commits/a0cba2b1a1ea9011ab07ee1ac140ba5a5eb8bd90" } }
dt = fetcher.fetch_date_of_tag(tag)
expect(dt).to eq(Time.parse("2015-03-04 19:01:48 UTC"))
end
end
end
describe "#fetch_commit" do
context "when API call is valid", :vcr do
it "returns commit" do
event = { "id" => 357_462_189,
"url" =>
"https://api.github.com/repos/skywinder/changelog_test/issues/events/357462189",
"actor" =>
{ "login" => "skywinder",
"id" => 3_356_474,
"avatar_url" => "https://avatars.githubusercontent.com/u/3356474?v=3",
"gravatar_id" => "",
"url" => "https://api.github.com/users/skywinder",
"html_url" => "https://github.com/skywinder",
"followers_url" => "https://api.github.com/users/skywinder/followers",
"following_url" =>
"https://api.github.com/users/skywinder/following{/other_user}",
"gists_url" => "https://api.github.com/users/skywinder/gists{/gist_id}",
"starred_url" =>
"https://api.github.com/users/skywinder/starred{/owner}{/repo}",
"subscriptions_url" => "https://api.github.com/users/skywinder/subscriptions",
"organizations_url" => "https://api.github.com/users/skywinder/orgs",
"repos_url" => "https://api.github.com/users/skywinder/repos",
"events_url" => "https://api.github.com/users/skywinder/events{/privacy}",
"received_events_url" =>
"https://api.github.com/users/skywinder/received_events",
"type" => "User",
"site_admin" => false },
"event" => "referenced",
"commit_id" => "decfe840d1a1b86e0c28700de5362d3365a29555",
"commit_url" =>
"https://api.github.com/repos/skywinder/changelog_test/commits/decfe840d1a1b86e0c28700de5362d3365a29555",
"created_at" => "2015-07-16T12:21:16Z" }
commit = fetcher.fetch_commit(event)
expectations = [
%w(sha decfe840d1a1b86e0c28700de5362d3365a29555),
["url",
"https://api.github.com/repos/skywinder/changelog_test/commits/decfe840d1a1b86e0c28700de5362d3365a29555"],
# OLD API: "https://api.github.com/repos/skywinder/changelog_test/git/commits/decfe840d1a1b86e0c28700de5362d3365a29555"],
["html_url",
"https://github.com/skywinder/changelog_test/commit/decfe840d1a1b86e0c28700de5362d3365a29555"],
["author",
{ "login" => "skywinder", "id" => 3_356_474, "avatar_url" => "https://avatars.githubusercontent.com/u/3356474?v=3", "gravatar_id" => "", "url" => "https://api.github.com/users/skywinder", "html_url" => "https://github.com/skywinder", "followers_url" => "https://api.github.com/users/skywinder/followers", "following_url" => "https://api.github.com/users/skywinder/following{/other_user}", "gists_url" => "https://api.github.com/users/skywinder/gists{/gist_id}", "starred_url" => "https://api.github.com/users/skywinder/starred{/owner}{/repo}", "subscriptions_url" => "https://api.github.com/users/skywinder/subscriptions", "organizations_url" => "https://api.github.com/users/skywinder/orgs", "repos_url" => "https://api.github.com/users/skywinder/repos", "events_url" => "https://api.github.com/users/skywinder/events{/privacy}", "received_events_url" => "https://api.github.com/users/skywinder/received_events", "type" => "User", "site_admin" => false }],
["committer",
{ "login" => "skywinder", "id" => 3_356_474, "avatar_url" => "https://avatars.githubusercontent.com/u/3356474?v=3", "gravatar_id" => "", "url" => "https://api.github.com/users/skywinder", "html_url" => "https://github.com/skywinder", "followers_url" => "https://api.github.com/users/skywinder/followers", "following_url" => "https://api.github.com/users/skywinder/following{/other_user}", "gists_url" => "https://api.github.com/users/skywinder/gists{/gist_id}", "starred_url" => "https://api.github.com/users/skywinder/starred{/owner}{/repo}", "subscriptions_url" => "https://api.github.com/users/skywinder/subscriptions", "organizations_url" => "https://api.github.com/users/skywinder/orgs", "repos_url" => "https://api.github.com/users/skywinder/repos", "events_url" => "https://api.github.com/users/skywinder/events{/privacy}", "received_events_url" => "https://api.github.com/users/skywinder/received_events", "type" => "User", "site_admin" => false }],
["parents",
[{ "sha" => "7ec095e5e3caceacedabf44d0b9b10da17c92e51",
"url" =>
"https://api.github.com/repos/skywinder/changelog_test/commits/7ec095e5e3caceacedabf44d0b9b10da17c92e51",
# OLD API: "https://api.github.com/repos/skywinder/changelog_test/git/commits/7ec095e5e3caceacedabf44d0b9b10da17c92e51",
"html_url" =>
"https://github.com/skywinder/changelog_test/commit/7ec095e5e3caceacedabf44d0b9b10da17c92e51" }]]
]
expectations.each do |property, val|
expect(commit[property]).to eq(val)
end
end
end
end
end
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
{"http_interactions":[{"request":{"method":"get","uri":"https://api.github.com/repos/skywinder/changelog_test/issues/14/events?per_page=100","body":{"encoding":"US-ASCII","base64_string":""},"headers":{"Accept":["application/vnd.github.v3+json"],"User-Agent":["Octokit Ruby Gem 4.3.0"],"Content-Type":["application/json"],"Accept-Encoding":["gzip;q=1.0,deflate;q=0.6,identity;q=0.3"]}},"response":{"status":{"code":200,"message":"OK"},"headers":{"Server":["GitHub.com"],"Date":["Fri, 20 May 2016 06:13:06 GMT"],"Content-Type":["application/json; charset=utf-8"],"Transfer-Encoding":["chunked"],"Status":["200 OK"],"X-Ratelimit-Limit":["60"],"X-Ratelimit-Remaining":["37"],"X-Ratelimit-Reset":["1463724861"],"Cache-Control":["public, max-age=60, s-maxage=60"],"Vary":["Accept","Accept-Encoding"],"Etag":["W/\"4d408c4e8053c706d7255563141ccb80\""],"X-Github-Media-Type":["github.v3; format=json"],"Access-Control-Expose-Headers":["ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval"],"Access-Control-Allow-Origin":["*"],"Content-Security-Policy":["default-src 'none'"],"Strict-Transport-Security":["max-age=31536000; includeSubdomains; preload"],"X-Content-Type-Options":["nosniff"],"X-Frame-Options":["deny"],"X-Xss-Protection":["1; mode=block"],"X-Served-By":["2d7a5e35115884240089368322196939"],"X-Github-Request-Id":["6C2F0F69:7F8B:2084B01:573EAAF2"]},"body":{"encoding":"ASCII-8BIT","base64_string":"W3siaWQiOjM1NzQ2MjE4OSwidXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNv\nbS9yZXBvcy9za3l3aW5kZXIvY2hhbmdlbG9nX3Rlc3QvaXNzdWVzL2V2ZW50\ncy8zNTc0NjIxODkiLCJhY3RvciI6eyJsb2dpbiI6InNreXdpbmRlciIsImlk\nIjozMzU2NDc0LCJhdmF0YXJfdXJsIjoiaHR0cHM6Ly9hdmF0YXJzLmdpdGh1\nYnVzZXJjb250ZW50LmNvbS91LzMzNTY0NzQ/dj0zIiwiZ3JhdmF0YXJfaWQi\nOiIiLCJ1cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL3VzZXJzL3NreXdp\nbmRlciIsImh0bWxfdXJsIjoiaHR0cHM6Ly9naXRodWIuY29tL3NreXdpbmRl\nciIsImZvbGxvd2Vyc191cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL3Vz\nZXJzL3NreXdpbmRlci9mb2xsb3dlcnMiLCJmb2xsb3dpbmdfdXJsIjoiaHR0\ncHM6Ly9hcGkuZ2l0aHViLmNvbS91c2Vycy9za3l3aW5kZXIvZm9sbG93aW5n\ney9vdGhlcl91c2VyfSIsImdpc3RzX3VybCI6Imh0dHBzOi8vYXBpLmdpdGh1\nYi5jb20vdXNlcnMvc2t5d2luZGVyL2dpc3Rzey9naXN0X2lkfSIsInN0YXJy\nZWRfdXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS91c2Vycy9za3l3aW5k\nZXIvc3RhcnJlZHsvb3duZXJ9ey9yZXBvfSIsInN1YnNjcmlwdGlvbnNfdXJs\nIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS91c2Vycy9za3l3aW5kZXIvc3Vi\nc2NyaXB0aW9ucyIsIm9yZ2FuaXphdGlvbnNfdXJsIjoiaHR0cHM6Ly9hcGku\nZ2l0aHViLmNvbS91c2Vycy9za3l3aW5kZXIvb3JncyIsInJlcG9zX3VybCI6\nImh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vdXNlcnMvc2t5d2luZGVyL3JlcG9z\nIiwiZXZlbnRzX3VybCI6Imh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vdXNlcnMv\nc2t5d2luZGVyL2V2ZW50c3svcHJpdmFjeX0iLCJyZWNlaXZlZF9ldmVudHNf\ndXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS91c2Vycy9za3l3aW5kZXIv\ncmVjZWl2ZWRfZXZlbnRzIiwidHlwZSI6IlVzZXIiLCJzaXRlX2FkbWluIjpm\nYWxzZX0sImV2ZW50IjoicmVmZXJlbmNlZCIsImNvbW1pdF9pZCI6ImRlY2Zl\nODQwZDFhMWI4NmUwYzI4NzAwZGU1MzYyZDMzNjVhMjk1NTUiLCJjb21taXRf\ndXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9yZXBvcy9za3l3aW5kZXIv\nY2hhbmdlbG9nX3Rlc3QvY29tbWl0cy9kZWNmZTg0MGQxYTFiODZlMGMyODcw\nMGRlNTM2MmQzMzY1YTI5NTU1IiwiY3JlYXRlZF9hdCI6IjIwMTUtMDctMTZU\nMTI6MjE6MTZaIn0seyJpZCI6MzU3NDYyNTQyLCJ1cmwiOiJodHRwczovL2Fw\naS5naXRodWIuY29tL3JlcG9zL3NreXdpbmRlci9jaGFuZ2Vsb2dfdGVzdC9p\nc3N1ZXMvZXZlbnRzLzM1NzQ2MjU0MiIsImFjdG9yIjp7ImxvZ2luIjoic2t5\nd2luZGVyIiwiaWQiOjMzNTY0NzQsImF2YXRhcl91cmwiOiJodHRwczovL2F2\nYXRhcnMuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3UvMzM1NjQ3ND92PTMiLCJn\ncmF2YXRhcl9pZCI6IiIsInVybCI6Imh0dHBzOi8vYXBpLmdpdGh1Yi5jb20v\ndXNlcnMvc2t5d2luZGVyIiwiaHRtbF91cmwiOiJodHRwczovL2dpdGh1Yi5j\nb20vc2t5d2luZGVyIiwiZm9sbG93ZXJzX3VybCI6Imh0dHBzOi8vYXBpLmdp\ndGh1Yi5jb20vdXNlcnMvc2t5d2luZGVyL2ZvbGxvd2VycyIsImZvbGxvd2lu\nZ191cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL3VzZXJzL3NreXdpbmRl\nci9mb2xsb3dpbmd7L290aGVyX3VzZXJ9IiwiZ2lzdHNfdXJsIjoiaHR0cHM6\nLy9hcGkuZ2l0aHViLmNvbS91c2Vycy9za3l3aW5kZXIvZ2lzdHN7L2dpc3Rf\naWR9Iiwic3RhcnJlZF91cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL3Vz\nZXJzL3NreXdpbmRlci9zdGFycmVkey9vd25lcn17L3JlcG99Iiwic3Vic2Ny\naXB0aW9uc191cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL3VzZXJzL3Nr\neXdpbmRlci9zdWJzY3JpcHRpb25zIiwib3JnYW5pemF0aW9uc191cmwiOiJo\ndHRwczovL2FwaS5naXRodWIuY29tL3VzZXJzL3NreXdpbmRlci9vcmdzIiwi\ncmVwb3NfdXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS91c2Vycy9za3l3\naW5kZXIvcmVwb3MiLCJldmVudHNfdXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHVi\nLmNvbS91c2Vycy9za3l3aW5kZXIvZXZlbnRzey9wcml2YWN5fSIsInJlY2Vp\ndmVkX2V2ZW50c191cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL3VzZXJz\nL3NreXdpbmRlci9yZWNlaXZlZF9ldmVudHMiLCJ0eXBlIjoiVXNlciIsInNp\ndGVfYWRtaW4iOmZhbHNlfSwiZXZlbnQiOiJjbG9zZWQiLCJjb21taXRfaWQi\nOiJkZWNmZTg0MGQxYTFiODZlMGMyODcwMGRlNTM2MmQzMzY1YTI5NTU1Iiwi\nY29tbWl0X3VybCI6Imh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vcmVwb3Mvc2t5\nd2luZGVyL2NoYW5nZWxvZ190ZXN0L2NvbW1pdHMvZGVjZmU4NDBkMWExYjg2\nZTBjMjg3MDBkZTUzNjJkMzM2NWEyOTU1NSIsImNyZWF0ZWRfYXQiOiIyMDE1\nLTA3LTE2VDEyOjIxOjQyWiJ9XQ==\n"},"http_version":null},"recorded_at":"Fri, 20 May 2016 06:13:10 GMT"}],"recorded_with":"VCR 3.0.1"}
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
{"http_interactions":[{"request":{"method":"get","uri":"https://api.github.com/repos/skywinder/changelog_test/tags?per_page=100","body":{"encoding":"US-ASCII","base64_string":""},"headers":{"Accept":["application/vnd.github.v3+json"],"User-Agent":["Octokit Ruby Gem 4.3.0"],"Content-Type":["application/json"],"Authorization":["token 0000000000000000"],"Accept-Encoding":["gzip;q=1.0,deflate;q=0.6,identity;q=0.3"]}},"response":{"status":{"code":401,"message":"Unauthorized"},"headers":{"Server":["GitHub.com"],"Date":["Fri, 20 May 2016 05:47:03 GMT"],"Content-Type":["application/json; charset=utf-8"],"Content-Length":["83"],"Status":["401 Unauthorized"],"X-Github-Media-Type":["github.v3; format=json"],"X-Ratelimit-Limit":["60"],"X-Ratelimit-Remaining":["42"],"X-Ratelimit-Reset":["1463724861"],"Access-Control-Expose-Headers":["ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval"],"Access-Control-Allow-Origin":["*"],"Content-Security-Policy":["default-src 'none'"],"Strict-Transport-Security":["max-age=31536000; includeSubdomains; preload"],"X-Content-Type-Options":["nosniff"],"X-Frame-Options":["deny"],"X-Xss-Protection":["1; mode=block"],"X-Github-Request-Id":["6C2F0F69:1271A:B8980B7:573EA4D6"]},"body":{"encoding":"UTF-8","base64_string":"eyJtZXNzYWdlIjoiQmFkIGNyZWRlbnRpYWxzIiwiZG9jdW1lbnRhdGlvbl91\ncmwiOiJodHRwczovL2RldmVsb3Blci5naXRodWIuY29tL3YzIn0=\n"},"http_version":null},"recorded_at":"Fri, 20 May 2016 05:47:07 GMT"}],"recorded_with":"VCR 3.0.1"}
@@ -0,0 +1 @@
{"http_interactions":[{"request":{"method":"get","uri":"https://api.github.com/repos/skywinder/changelog_test/tags?per_page=100","body":{"encoding":"US-ASCII","base64_string":""},"headers":{"Accept":["application/vnd.github.v3+json"],"User-Agent":["Octokit Ruby Gem 4.3.0"],"Content-Type":["application/json"],"Authorization":["token 0000000000000000"],"Accept-Encoding":["gzip;q=1.0,deflate;q=0.6,identity;q=0.3"]}},"response":{"status":{"code":401,"message":"Unauthorized"},"headers":{"Server":["GitHub.com"],"Date":["Sat, 02 Jul 2016 14:13:57 GMT"],"Content-Type":["application/json; charset=utf-8"],"Content-Length":["83"],"Status":["401 Unauthorized"],"X-Github-Media-Type":["github.v3; format=json"],"X-Ratelimit-Limit":["60"],"X-Ratelimit-Remaining":["59"],"X-Ratelimit-Reset":["1467472437"],"Access-Control-Expose-Headers":["ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval"],"Access-Control-Allow-Origin":["*"],"Content-Security-Policy":["default-src 'none'"],"Strict-Transport-Security":["max-age=31536000; includeSubdomains; preload"],"X-Content-Type-Options":["nosniff"],"X-Frame-Options":["deny"],"X-Xss-Protection":["1; mode=block"],"X-Github-Request-Id":["54DBAB83:300E:76DBC85:5777CC25"]},"body":{"encoding":"UTF-8","base64_string":"eyJtZXNzYWdlIjoiQmFkIGNyZWRlbnRpYWxzIiwiZG9jdW1lbnRhdGlvbl91\ncmwiOiJodHRwczovL2RldmVsb3Blci5naXRodWIuY29tL3YzIn0=\n"},"http_version":null},"recorded_at":"Sat, 02 Jul 2016 14:13:58 GMT"}],"recorded_with":"VCR 3.0.1"}