github_changelog_generator 1.13.2 → 1.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/github_changelog_generator.rb +6 -1
  4. data/lib/github_changelog_generator/generator/generator.rb +28 -28
  5. data/lib/github_changelog_generator/generator/generator_fetcher.rb +23 -20
  6. data/lib/github_changelog_generator/generator/generator_generation.rb +40 -53
  7. data/lib/github_changelog_generator/generator/generator_processor.rb +32 -22
  8. data/lib/github_changelog_generator/generator/generator_tags.rb +77 -46
  9. data/lib/github_changelog_generator/octo_fetcher.rb +363 -0
  10. data/lib/github_changelog_generator/options.rb +92 -0
  11. data/lib/github_changelog_generator/parser.rb +21 -5
  12. data/lib/github_changelog_generator/parser_file.rb +2 -2
  13. data/lib/github_changelog_generator/version.rb +1 -1
  14. data/man/git-generate-changelog.1 +44 -2
  15. data/man/git-generate-changelog.1.html +290 -0
  16. data/man/git-generate-changelog.md +29 -1
  17. data/spec/spec_helper.rb +21 -0
  18. data/spec/unit/generator/generator_processor_spec.rb +4 -4
  19. data/spec/unit/generator/generator_tags_spec.rb +43 -40
  20. data/spec/unit/octo_fetcher_spec.rb +528 -0
  21. data/spec/unit/options_spec.rb +42 -0
  22. data/spec/unit/reader_spec.rb +0 -4
  23. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_closed_issues_and_pr/when_API_call_is_valid.json +1 -0
  24. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_closed_issues_and_pr/when_API_call_is_valid/returns_issue_with_proper_key/values.json +1 -0
  25. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_closed_issues_and_pr/when_API_call_is_valid/returns_issues.json +1 -0
  26. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_closed_issues_and_pr/when_API_call_is_valid/returns_issues_with_labels.json +1 -0
  27. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_closed_issues_and_pr/when_API_call_is_valid/returns_pull_request_with_proper_key/values.json +1 -0
  28. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_closed_issues_and_pr/when_API_call_is_valid/returns_pull_requests_with_labels.json +1 -0
  29. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_closed_pull_requests/when_API_call_is_valid.json +1 -0
  30. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_closed_pull_requests/when_API_call_is_valid/returns_correct_pull_request_keys.json +1 -0
  31. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_closed_pull_requests/when_API_call_is_valid/returns_pull_requests.json +1 -0
  32. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_commit/when_API_call_is_valid.json +1 -0
  33. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_commit/when_API_call_is_valid/returns_commit.json +1 -0
  34. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_date_of_tag/when_API_call_is_valid.json +1 -0
  35. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_date_of_tag/when_API_call_is_valid/returns_date.json +1 -0
  36. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_events_async/when_API_call_is_valid.json +1 -0
  37. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_events_async/when_API_call_is_valid/populates_issues.json +1 -0
  38. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_github_fetch_tags/when_API_call_is_valid.json +1 -0
  39. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_github_fetch_tags/when_API_call_is_valid/should_return_tags.json +1 -0
  40. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_github_fetch_tags/when_API_call_is_valid/should_return_tags_count.json +1 -0
  41. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_github_fetch_tags/when_wrong_token_provided.json +1 -0
  42. data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_github_fetch_tags/when_wrong_token_provided/should_raise_Unauthorized_error.json +1 -0
  43. metadata +97 -12
  44. data/lib/CHANGELOG.md +0 -58
  45. data/lib/github_changelog_generator/fetcher.rb +0 -226
  46. data/spec/unit/fetcher_spec.rb +0 -60
@@ -51,6 +51,10 @@ Automatically generate change log from your tags, issues, labels and pull reques
51
51
 
52
52
  Setup custom header label. Default is "# Change Log"
53
53
 
54
+ --front-matter [JSON]
55
+
56
+ Add YAML front matter. Formatted as JSON because it's easier to add on the command line
57
+
54
58
  --pr-label [LABEL]
55
59
 
56
60
  Setup custom label for pull requests section. Default is "**Merged pull requests:**"
@@ -79,6 +83,10 @@ Automatically generate change log from your tags, issues, labels and pull reques
79
83
 
80
84
  Add author of pull-request in the end. Default is true
81
85
 
86
+ --usernames-as-github-logins
87
+
88
+ Use GitHub tags instead of Markdown links for the author of an issue or pull-request.
89
+
82
90
  --unreleased-only
83
91
 
84
92
  Generate log from unreleased closed issues only.
@@ -89,7 +97,7 @@ Automatically generate change log from your tags, issues, labels and pull reques
89
97
 
90
98
  --unreleased-label [label]
91
99
 
92
- Add to log unreleased closed issues. Default is true
100
+ Setup custom label for unreleased closed issues section. Default is "**Unreleased:**"
93
101
 
94
102
  --[no-]compare-link
95
103
 
@@ -119,6 +127,10 @@ Automatically generate change log from your tags, issues, labels and pull reques
119
127
 
120
128
  Change log will exclude specified tags
121
129
 
130
+ --exclude-tags-regex [REGEX]
131
+
132
+ Apply a regular expression on tag names so that they can be excluded, for example: --exclude-tags-regex ".*\+\d{1,}"
133
+
122
134
  --since-tag x
123
135
 
124
136
  Change log will start after specified tag
@@ -151,6 +163,22 @@ Automatically generate change log from your tags, issues, labels and pull reques
151
163
 
152
164
  Put the unreleased changes in the specified release number.
153
165
 
166
+ --release-branch [RELEASE-BRANCH]
167
+
168
+ Limit pull requests to the release branch, such as master or release
169
+
170
+ --http-cache
171
+
172
+ Use HTTP Cache to cache Github API requests (useful for large repos) Default is true.
173
+
174
+ --[no-]cache-file [CACHE-FILE]
175
+
176
+ Filename to use for cache. Default is /tmp/github-changelog-http-cache
177
+
178
+ --cache-log [CACHE-LOG]
179
+
180
+ Filename to use for cache log. Default is /tmp/github-changelog-logger.log
181
+
154
182
  --[no-]verbose
155
183
 
156
184
  Run verbosely. Default is true
@@ -19,6 +19,8 @@
19
19
  require "codeclimate-test-reporter"
20
20
  require "simplecov"
21
21
  require "coveralls"
22
+ require "vcr"
23
+ require "webmock/rspec"
22
24
 
23
25
  # This module is only used to check the environment is currently a testing env
24
26
  module SpecHelper
@@ -36,6 +38,25 @@ end
36
38
  require "github_changelog_generator"
37
39
  require "github_changelog_generator/task"
38
40
 
41
+ VCR.configure do |c|
42
+ c.allow_http_connections_when_no_cassette = true
43
+ c.cassette_library_dir = "spec/vcr"
44
+ c.ignore_localhost = true
45
+ c.default_cassette_options = {
46
+ record: :new_episodes,
47
+ serialize_with: :json,
48
+ preserve_exact_body_bytes: true,
49
+ decode_compressed_response: true
50
+ }
51
+ c.filter_sensitive_data("<GITHUB_TOKEN>") do
52
+ "token #{ENV.fetch('CHANGELOG_GITHUB_TOKEN') { 'frobnitz' }}"
53
+ end
54
+
55
+ c.configure_rspec_metadata!
56
+
57
+ c.hook_into :webmock, :faraday
58
+ end
59
+
39
60
  RSpec.configure do |config|
40
61
  config.expect_with :rspec do |expectations|
41
62
  expectations.include_chain_clauses_in_custom_matcher_descriptions = true
@@ -2,10 +2,10 @@
2
2
  module GitHubChangelogGenerator
3
3
  describe Generator do
4
4
  context "#exclude_issues_by_labels" do
5
- let(:label) { double("the-bad-label", name: "BAD") }
6
- let(:issue) { double("the-issue-to-be-excluded", labels: [label]) }
7
- let(:good_label) { double("a-good-label", name: "GOOD") }
8
- let(:good_issue) { double("an-issue-to-be-kept", labels: [good_label]) }
5
+ let(:label) { { "name" => "BAD" } }
6
+ let(:issue) { { "labels" => [label] } }
7
+ let(:good_label) { { "name" => "GOOD" } }
8
+ let(:good_issue) { { "labels" => [good_label] } }
9
9
  let(:issues) { [issue, good_issue] }
10
10
  subject(:generator) { described_class.new(exclude_labels: %w(BAD BOO)) }
11
11
 
@@ -1,12 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
  describe GitHubChangelogGenerator::Generator do
3
- def tag_mash_with_name(tag)
4
- Hashie::Mash.new.tap { |mash_tag| mash_tag.name = tag }
3
+ def tag_with_name(tag)
4
+ {
5
+ "name" => tag
6
+ }
5
7
  end
6
8
 
7
- def tags_mash_from_strings(tags_strings)
9
+ def tags_from_strings(tags_strings)
8
10
  tags_strings.map do |tag|
9
- tag_mash_with_name(tag)
11
+ tag_with_name(tag)
10
12
  end
11
13
  end
12
14
 
@@ -17,20 +19,20 @@ describe GitHubChangelogGenerator::Generator do
17
19
  end
18
20
 
19
21
  subject do
20
- @generator.get_filtered_tags(tags_mash_from_strings(%w(1 2 3)))
22
+ @generator.get_filtered_tags(tags_from_strings(%w(1 2 3)))
21
23
  end
22
24
  it { is_expected.to be_a(Array) }
23
- it { is_expected.to match_array(tags_mash_from_strings(%w(1 2 3))) }
25
+ it { is_expected.to match_array(tags_from_strings(%w(1 2 3))) }
24
26
  end
25
27
  context "when between_tags same as input array" do
26
28
  before do
27
29
  @generator = GitHubChangelogGenerator::Generator.new(between_tags: %w(1 2 3))
28
30
  end
29
31
  subject do
30
- @generator.get_filtered_tags(tags_mash_from_strings(%w(1 2 3)))
32
+ @generator.get_filtered_tags(tags_from_strings(%w(1 2 3)))
31
33
  end
32
34
  it { is_expected.to be_a(Array) }
33
- it { is_expected.to match_array(tags_mash_from_strings(%w(1 2 3))) }
35
+ it { is_expected.to match_array(tags_from_strings(%w(1 2 3))) }
34
36
  end
35
37
 
36
38
  context "when between_tags filled with correct values" do
@@ -38,10 +40,10 @@ describe GitHubChangelogGenerator::Generator do
38
40
  @generator = GitHubChangelogGenerator::Generator.new(between_tags: %w(1 2))
39
41
  end
40
42
  subject do
41
- @generator.get_filtered_tags(tags_mash_from_strings(%w(1 2 3)))
43
+ @generator.get_filtered_tags(tags_from_strings(%w(1 2 3)))
42
44
  end
43
45
  it { is_expected.to be_a(Array) }
44
- it { is_expected.to match_array(tags_mash_from_strings(%w(1 2))) }
46
+ it { is_expected.to match_array(tags_from_strings(%w(1 2))) }
45
47
  end
46
48
 
47
49
  context "when between_tags filled with invalid values" do
@@ -50,133 +52,134 @@ describe GitHubChangelogGenerator::Generator do
50
52
  end
51
53
 
52
54
  subject do
53
- @generator.get_filtered_tags(tags_mash_from_strings(%w(1 2 3)))
55
+ @generator.get_filtered_tags(tags_from_strings(%w(1 2 3)))
54
56
  end
55
57
  it { is_expected.to be_a(Array) }
56
- it { is_expected.to match_array(tags_mash_from_strings(%w(1))) }
58
+ it { is_expected.to match_array(tags_from_strings(%w(1))) }
57
59
  end
58
60
  end
59
61
 
60
62
  describe "#get_filtered_tags" do
61
63
  subject do
62
- generator.get_filtered_tags(tags_mash_from_strings(%w(1 2 3 4 5)))
64
+ generator.get_filtered_tags(tags_from_strings(%w(1 2 3 4 5)))
63
65
  end
64
66
 
65
- context "with excluded and between tags" do
66
- let(:generator) { GitHubChangelogGenerator::Generator.new(between_tags: %w(1 2 3), exclude_tags: %w(2)) }
67
+ context "respects between tags" do
68
+ let(:generator) { GitHubChangelogGenerator::Generator.new(between_tags: %w(1 2 3)) }
69
+
67
70
  it { is_expected.to be_a Array }
68
- it { is_expected.to match_array(tags_mash_from_strings(%w(1 3))) }
71
+ it { is_expected.to match_array(tags_from_strings(%w(1 2 3))) }
69
72
  end
70
73
  end
71
74
 
72
75
  describe "#filter_excluded_tags" do
73
- subject { generator.filter_excluded_tags(tags_mash_from_strings(%w(1 2 3))) }
76
+ subject { generator.filter_excluded_tags(tags_from_strings(%w(1 2 3))) }
74
77
 
75
78
  context "with matching string" do
76
79
  let(:generator) { GitHubChangelogGenerator::Generator.new(exclude_tags: %w(3)) }
77
80
  it { is_expected.to be_a Array }
78
- it { is_expected.to match_array(tags_mash_from_strings(%w(1 2))) }
81
+ it { is_expected.to match_array(tags_from_strings(%w(1 2))) }
79
82
  end
80
83
 
81
84
  context "with non-matching string" do
82
85
  let(:generator) { GitHubChangelogGenerator::Generator.new(exclude_tags: %w(invalid tags)) }
83
86
  it { is_expected.to be_a Array }
84
- it { is_expected.to match_array(tags_mash_from_strings(%w(1 2 3))) }
87
+ it { is_expected.to match_array(tags_from_strings(%w(1 2 3))) }
85
88
  end
86
89
 
87
90
  context "with matching regex" do
88
91
  let(:generator) { GitHubChangelogGenerator::Generator.new(exclude_tags: /[23]/) }
89
92
  it { is_expected.to be_a Array }
90
- it { is_expected.to match_array(tags_mash_from_strings(%w(1))) }
93
+ it { is_expected.to match_array(tags_from_strings(%w(1))) }
91
94
  end
92
95
 
93
96
  context "with non-matching regex" do
94
97
  let(:generator) { GitHubChangelogGenerator::Generator.new(exclude_tags: /[abc]/) }
95
98
  it { is_expected.to be_a Array }
96
- it { is_expected.to match_array(tags_mash_from_strings(%w(1 2 3))) }
99
+ it { is_expected.to match_array(tags_from_strings(%w(1 2 3))) }
97
100
  end
98
101
  end
99
102
 
100
103
  describe "#filter_excluded_tags_regex" do
101
- subject { generator.filter_excluded_tags(tags_mash_from_strings(%w(1 2 3))) }
104
+ subject { generator.filter_excluded_tags(tags_from_strings(%w(1 2 3))) }
102
105
 
103
106
  context "with matching regex" do
104
107
  let(:generator) { GitHubChangelogGenerator::Generator.new(exclude_tags_regex: "[23]") }
105
108
  it { is_expected.to be_a Array }
106
- it { is_expected.to match_array(tags_mash_from_strings(%w(1))) }
109
+ it { is_expected.to match_array(tags_from_strings(%w(1))) }
107
110
  end
108
111
 
109
112
  context "with non-matching regex" do
110
113
  let(:generator) { GitHubChangelogGenerator::Generator.new(exclude_tags_regex: "[45]") }
111
114
  it { is_expected.to be_a Array }
112
- it { is_expected.to match_array(tags_mash_from_strings(%w(1 2 3))) }
115
+ it { is_expected.to match_array(tags_from_strings(%w(1 2 3))) }
113
116
  end
114
117
  end
115
118
 
116
119
  describe "#filter_since_tag" do
117
120
  context "with filled array" do
118
- subject { generator.filter_since_tag(tags_mash_from_strings(%w(1 2 3))) }
121
+ subject { generator.filter_since_tag(tags_from_strings(%w(1 2 3))) }
119
122
 
120
123
  context "with valid since tag" do
121
124
  let(:generator) { GitHubChangelogGenerator::Generator.new(since_tag: "2") }
122
125
  it { is_expected.to be_a Array }
123
- it { is_expected.to match_array(tags_mash_from_strings(%w(1))) }
126
+ it { is_expected.to match_array(tags_from_strings(%w(1))) }
124
127
  end
125
128
 
126
129
  context "with invalid since tag" do
127
130
  let(:generator) { GitHubChangelogGenerator::Generator.new(since_tag: "Invalid tag") }
128
131
  it { is_expected.to be_a Array }
129
- it { is_expected.to match_array(tags_mash_from_strings(%w(1 2 3))) }
132
+ it { is_expected.to match_array(tags_from_strings(%w(1 2 3))) }
130
133
  end
131
134
  end
132
135
 
133
136
  context "with empty array" do
134
- subject { generator.filter_since_tag(tags_mash_from_strings(%w())) }
137
+ subject { generator.filter_since_tag(tags_from_strings(%w())) }
135
138
 
136
139
  context "with valid since tag" do
137
140
  let(:generator) { GitHubChangelogGenerator::Generator.new(since_tag: "2") }
138
141
  it { is_expected.to be_a Array }
139
- it { is_expected.to match_array(tags_mash_from_strings(%w())) }
142
+ it { is_expected.to match_array(tags_from_strings(%w())) }
140
143
  end
141
144
 
142
145
  context "with invalid since tag" do
143
146
  let(:generator) { GitHubChangelogGenerator::Generator.new(since_tag: "Invalid tag") }
144
147
  it { is_expected.to be_a Array }
145
- it { is_expected.to match_array(tags_mash_from_strings(%w())) }
148
+ it { is_expected.to match_array(tags_from_strings(%w())) }
146
149
  end
147
150
  end
148
151
  end
149
152
 
150
153
  describe "#filter_due_tag" do
151
154
  context "with filled array" do
152
- subject { generator.filter_due_tag(tags_mash_from_strings(%w(1 2 3))) }
155
+ subject { generator.filter_due_tag(tags_from_strings(%w(1 2 3))) }
153
156
 
154
157
  context "with valid due tag" do
155
158
  let(:generator) { GitHubChangelogGenerator::Generator.new(due_tag: "2") }
156
159
  it { is_expected.to be_a Array }
157
- it { is_expected.to match_array(tags_mash_from_strings(%w(3))) }
160
+ it { is_expected.to match_array(tags_from_strings(%w(3))) }
158
161
  end
159
162
 
160
163
  context "with invalid due tag" do
161
164
  let(:generator) { GitHubChangelogGenerator::Generator.new(due_tag: "Invalid tag") }
162
165
  it { is_expected.to be_a Array }
163
- it { is_expected.to match_array(tags_mash_from_strings(%w(1 2 3))) }
166
+ it { is_expected.to match_array(tags_from_strings(%w(1 2 3))) }
164
167
  end
165
168
  end
166
169
 
167
170
  context "with empty array" do
168
- subject { generator.filter_due_tag(tags_mash_from_strings(%w())) }
171
+ subject { generator.filter_due_tag(tags_from_strings(%w())) }
169
172
 
170
173
  context "with valid due tag" do
171
174
  let(:generator) { GitHubChangelogGenerator::Generator.new(due_tag: "2") }
172
175
  it { is_expected.to be_a Array }
173
- it { is_expected.to match_array(tags_mash_from_strings(%w())) }
176
+ it { is_expected.to match_array(tags_from_strings(%w())) }
174
177
  end
175
178
 
176
179
  context "with invalid due tag" do
177
180
  let(:generator) { GitHubChangelogGenerator::Generator.new(due_tag: "Invalid tag") }
178
181
  it { is_expected.to be_a Array }
179
- it { is_expected.to match_array(tags_mash_from_strings(%w())) }
182
+ it { is_expected.to match_array(tags_from_strings(%w())) }
180
183
  end
181
184
  end
182
185
  end
@@ -191,7 +194,7 @@ describe GitHubChangelogGenerator::Generator do
191
194
  end
192
195
  context "fetch already filled tag" do
193
196
  before { @generator.instance_variable_set :@tag_times_hash, "valid_tag" => current_time }
194
- subject { @generator.get_time_of_tag tag_mash_with_name("valid_tag") }
197
+ subject { @generator.get_time_of_tag tag_with_name("valid_tag") }
195
198
  it { is_expected.to be_a_kind_of(Time) }
196
199
  it { is_expected.to eq(current_time) }
197
200
  end
@@ -202,7 +205,7 @@ describe GitHubChangelogGenerator::Generator do
202
205
  @generator.instance_variable_set :@fetcher, mock
203
206
  end
204
207
  subject do
205
- of_tag = @generator.get_time_of_tag(tag_mash_with_name("valid_tag"))
208
+ of_tag = @generator.get_time_of_tag(tag_with_name("valid_tag"))
206
209
  of_tag
207
210
  end
208
211
  it { is_expected.to be_a_kind_of(Time) }
@@ -229,13 +232,13 @@ describe GitHubChangelogGenerator::Generator do
229
232
  @generator.sort_tags_by_date(tags)
230
233
  end
231
234
  context "sort unsorted tags" do
232
- let(:tags) { tags_mash_from_strings %w(valid_tag1 valid_tag2 valid_tag3) }
235
+ let(:tags) { tags_from_strings %w(valid_tag1 valid_tag2 valid_tag3) }
233
236
 
234
237
  it { is_expected.to be_a_kind_of(Array) }
235
238
  it { is_expected.to match_array(tags.reverse!) }
236
239
  end
237
240
  context "sort sorted tags" do
238
- let(:tags) { tags_mash_from_strings %w(valid_tag3 valid_tag2 valid_tag1) }
241
+ let(:tags) { tags_from_strings %w(valid_tag3 valid_tag2 valid_tag1) }
239
242
 
240
243
  it { is_expected.to be_a_kind_of(Array) }
241
244
  it { is_expected.to match_array(tags) }
@@ -0,0 +1,528 @@
1
+ # frozen_string_literal: true
2
+ VALID_TOKEN = "0123456789abcdef"
3
+ INVALID_TOKEN = "0000000000000000"
4
+
5
+ describe GitHubChangelogGenerator::OctoFetcher do
6
+ let(:options) do
7
+ {
8
+ user: "skywinder",
9
+ project: "changelog_test"
10
+ }
11
+ end
12
+
13
+ let(:fetcher) { GitHubChangelogGenerator::OctoFetcher.new(options) }
14
+
15
+ describe "#check_github_response" do
16
+ context "when returns successfully" do
17
+ it "returns block value" do
18
+ expect(fetcher.send(:check_github_response) { 1 + 1 }).to eq(2)
19
+ end
20
+ end
21
+
22
+ context "when raises Octokit::Unauthorized" do
23
+ it "aborts" do
24
+ expect(fetcher).to receive(:sys_abort).with("Error: wrong GitHub token")
25
+ fetcher.send(:check_github_response) { raise(Octokit::Unauthorized) }
26
+ end
27
+ end
28
+
29
+ context "when raises Octokit::Forbidden" do
30
+ it "sleeps and retries and then aborts" do
31
+ retry_limit = GitHubChangelogGenerator::OctoFetcher::MAX_FORBIDDEN_RETRIES - 1
32
+ allow(fetcher).to receive(:sleep_base_interval).exactly(retry_limit).times.and_return(0)
33
+
34
+ expect(fetcher).to receive(:sys_abort).with("Exceeded retry limit")
35
+ fetcher.send(:check_github_response) { raise(Octokit::Forbidden) }
36
+ end
37
+ end
38
+ end
39
+
40
+ describe "#fetch_github_token" do
41
+ token = GitHubChangelogGenerator::OctoFetcher::CHANGELOG_GITHUB_TOKEN
42
+ context "when token in ENV exist" do
43
+ before { stub_const("ENV", ENV.to_hash.merge(token => VALID_TOKEN)) }
44
+ subject { fetcher.send(:fetch_github_token) }
45
+ it { is_expected.to eq(VALID_TOKEN) }
46
+ end
47
+
48
+ context "when token in ENV is nil" do
49
+ before { stub_const("ENV", ENV.to_hash.merge(token => nil)) }
50
+ subject { fetcher.send(:fetch_github_token) }
51
+ it { is_expected.to be_nil }
52
+ end
53
+
54
+ context "when token in options and ENV is nil" do
55
+ let(:options) { { token: VALID_TOKEN } }
56
+
57
+ before do
58
+ stub_const("ENV", ENV.to_hash.merge(token => nil))
59
+ end
60
+
61
+ subject { fetcher.send(:fetch_github_token) }
62
+ it { is_expected.to eq(VALID_TOKEN) }
63
+ end
64
+
65
+ context "when token in options and ENV specified" do
66
+ let(:options) { { token: VALID_TOKEN } }
67
+
68
+ before do
69
+ stub_const("ENV", ENV.to_hash.merge(token => "no_matter_what"))
70
+ end
71
+
72
+ subject { fetcher.send(:fetch_github_token) }
73
+ it { is_expected.to eq(VALID_TOKEN) }
74
+ end
75
+ end
76
+
77
+ describe "#get_all_tags" do
78
+ context "when github_fetch_tags returns tags" do
79
+ it "returns tags" do
80
+ mock_tags = ["tag"]
81
+ allow(fetcher).to receive(:github_fetch_tags).and_return(mock_tags)
82
+ expect(fetcher.get_all_tags).to eq(mock_tags)
83
+ end
84
+ end
85
+ end
86
+
87
+ describe "#github_fetch_tags" do
88
+ context "when wrong token provided", :vcr do
89
+ let(:options) do
90
+ {
91
+ user: "skywinder",
92
+ project: "changelog_test",
93
+ token: INVALID_TOKEN
94
+ }
95
+ end
96
+
97
+ it "should raise Unauthorized error" do
98
+ expect { fetcher.github_fetch_tags }.to raise_error SystemExit, "Error: wrong GitHub token"
99
+ end
100
+ end
101
+
102
+ context "when API call is valid", :vcr do
103
+ it "should return tags" do
104
+ expected_tags = [{ "name" => "v0.0.3",
105
+ "zipball_url" =>
106
+ "https://api.github.com/repos/skywinder/changelog_test/zipball/v0.0.3",
107
+ "tarball_url" =>
108
+ "https://api.github.com/repos/skywinder/changelog_test/tarball/v0.0.3",
109
+ "commit" =>
110
+ { "sha" => "a0cba2b1a1ea9011ab07ee1ac140ba5a5eb8bd90",
111
+ "url" =>
112
+ "https://api.github.com/repos/skywinder/changelog_test/commits/a0cba2b1a1ea9011ab07ee1ac140ba5a5eb8bd90" } },
113
+ { "name" => "v0.0.2",
114
+ "zipball_url" =>
115
+ "https://api.github.com/repos/skywinder/changelog_test/zipball/v0.0.2",
116
+ "tarball_url" =>
117
+ "https://api.github.com/repos/skywinder/changelog_test/tarball/v0.0.2",
118
+ "commit" =>
119
+ { "sha" => "9b35bb13dcd15b68e7bcbf10cde5eb937a54f710",
120
+ "url" =>
121
+ "https://api.github.com/repos/skywinder/changelog_test/commits/9b35bb13dcd15b68e7bcbf10cde5eb937a54f710" } },
122
+ { "name" => "v0.0.1",
123
+ "zipball_url" =>
124
+ "https://api.github.com/repos/skywinder/changelog_test/zipball/v0.0.1",
125
+ "tarball_url" =>
126
+ "https://api.github.com/repos/skywinder/changelog_test/tarball/v0.0.1",
127
+ "commit" =>
128
+ { "sha" => "4c2d6d1ed58bdb24b870dcb5d9f2ceed0283d69d",
129
+ "url" =>
130
+ "https://api.github.com/repos/skywinder/changelog_test/commits/4c2d6d1ed58bdb24b870dcb5d9f2ceed0283d69d" } },
131
+ { "name" => "0.0.4",
132
+ "zipball_url" =>
133
+ "https://api.github.com/repos/skywinder/changelog_test/zipball/0.0.4",
134
+ "tarball_url" =>
135
+ "https://api.github.com/repos/skywinder/changelog_test/tarball/0.0.4",
136
+ "commit" =>
137
+ { "sha" => "ece0c3ab7142b21064b885061c55ede00ef6ce94",
138
+ "url" =>
139
+ "https://api.github.com/repos/skywinder/changelog_test/commits/ece0c3ab7142b21064b885061c55ede00ef6ce94" } }]
140
+
141
+ expect(fetcher.github_fetch_tags).to eq(expected_tags)
142
+ end
143
+
144
+ it "should return tags count" do
145
+ tags = fetcher.github_fetch_tags
146
+ expect(tags.size).to eq(4)
147
+ end
148
+ end
149
+ end
150
+
151
+ describe "#fetch_closed_issues_and_pr" do
152
+ context "when API call is valid", :vcr do
153
+ it "returns issues" do
154
+ issues, pull_requests = fetcher.fetch_closed_issues_and_pr
155
+ expect(issues.size).to eq(7)
156
+ expect(pull_requests.size).to eq(14)
157
+ end
158
+
159
+ it "returns issue with proper key/values" do
160
+ issues, _pull_requests = fetcher.fetch_closed_issues_and_pr
161
+
162
+ expected_issue = { "url" => "https://api.github.com/repos/skywinder/changelog_test/issues/14",
163
+ "repository_url" => "https://api.github.com/repos/skywinder/changelog_test",
164
+ "labels_url" =>
165
+ "https://api.github.com/repos/skywinder/changelog_test/issues/14/labels{/name}",
166
+ "comments_url" =>
167
+ "https://api.github.com/repos/skywinder/changelog_test/issues/14/comments",
168
+ "events_url" =>
169
+ "https://api.github.com/repos/skywinder/changelog_test/issues/14/events",
170
+ "html_url" => "https://github.com/skywinder/changelog_test/issues/14",
171
+ "id" => 95_419_412,
172
+ "number" => 14,
173
+ "title" => "Issue closed from commit from PR",
174
+ "user" =>
175
+ { "login" => "skywinder",
176
+ "id" => 3_356_474,
177
+ "avatar_url" => "https://avatars.githubusercontent.com/u/3356474?v=3",
178
+ "gravatar_id" => "",
179
+ "url" => "https://api.github.com/users/skywinder",
180
+ "html_url" => "https://github.com/skywinder",
181
+ "followers_url" => "https://api.github.com/users/skywinder/followers",
182
+ "following_url" =>
183
+ "https://api.github.com/users/skywinder/following{/other_user}",
184
+ "gists_url" => "https://api.github.com/users/skywinder/gists{/gist_id}",
185
+ "starred_url" =>
186
+ "https://api.github.com/users/skywinder/starred{/owner}{/repo}",
187
+ "subscriptions_url" => "https://api.github.com/users/skywinder/subscriptions",
188
+ "organizations_url" => "https://api.github.com/users/skywinder/orgs",
189
+ "repos_url" => "https://api.github.com/users/skywinder/repos",
190
+ "events_url" => "https://api.github.com/users/skywinder/events{/privacy}",
191
+ "received_events_url" =>
192
+ "https://api.github.com/users/skywinder/received_events",
193
+ "type" => "User",
194
+ "site_admin" => false },
195
+ "labels" => [],
196
+ "state" => "closed",
197
+ "locked" => false,
198
+ "assignee" => nil,
199
+ "assignees" => [],
200
+ "milestone" => nil,
201
+ "comments" => 0,
202
+ "created_at" => "2015-07-16T12:06:08Z",
203
+ "updated_at" => "2015-07-16T12:21:42Z",
204
+ "closed_at" => "2015-07-16T12:21:42Z",
205
+ "body" => "" }
206
+
207
+ # Convert times to Time
208
+ expected_issue.each_pair do |k, v|
209
+ expected_issue[k] = Time.parse(v) if v =~ /^2015-/
210
+ end
211
+
212
+ expect(issues.first).to eq(expected_issue)
213
+ end
214
+
215
+ it "returns pull request with proper key/values" do
216
+ _issues, pull_requests = fetcher.fetch_closed_issues_and_pr
217
+
218
+ expected_pr = { "url" => "https://api.github.com/repos/skywinder/changelog_test/issues/21",
219
+ "repository_url" => "https://api.github.com/repos/skywinder/changelog_test",
220
+ "labels_url" =>
221
+ "https://api.github.com/repos/skywinder/changelog_test/issues/21/labels{/name}",
222
+ "comments_url" =>
223
+ "https://api.github.com/repos/skywinder/changelog_test/issues/21/comments",
224
+ "events_url" =>
225
+ "https://api.github.com/repos/skywinder/changelog_test/issues/21/events",
226
+ "html_url" => "https://github.com/skywinder/changelog_test/pull/21",
227
+ "id" => 124_925_759,
228
+ "number" => 21,
229
+ "title" => "Merged br (should appear in change log with #20)",
230
+ "user" =>
231
+ { "login" => "skywinder",
232
+ "id" => 3_356_474,
233
+ "avatar_url" => "https://avatars.githubusercontent.com/u/3356474?v=3",
234
+ "gravatar_id" => "",
235
+ "url" => "https://api.github.com/users/skywinder",
236
+ "html_url" => "https://github.com/skywinder",
237
+ "followers_url" => "https://api.github.com/users/skywinder/followers",
238
+ "following_url" =>
239
+ "https://api.github.com/users/skywinder/following{/other_user}",
240
+ "gists_url" => "https://api.github.com/users/skywinder/gists{/gist_id}",
241
+ "starred_url" =>
242
+ "https://api.github.com/users/skywinder/starred{/owner}{/repo}",
243
+ "subscriptions_url" => "https://api.github.com/users/skywinder/subscriptions",
244
+ "organizations_url" => "https://api.github.com/users/skywinder/orgs",
245
+ "repos_url" => "https://api.github.com/users/skywinder/repos",
246
+ "events_url" => "https://api.github.com/users/skywinder/events{/privacy}",
247
+ "received_events_url" =>
248
+ "https://api.github.com/users/skywinder/received_events",
249
+ "type" => "User",
250
+ "site_admin" => false },
251
+ "labels" => [],
252
+ "state" => "closed",
253
+ "locked" => false,
254
+ "assignee" => nil,
255
+ "assignees" => [],
256
+ "milestone" => nil,
257
+ "comments" => 0,
258
+ "created_at" => "2016-01-05T09:24:08Z",
259
+ "updated_at" => "2016-01-05T09:26:53Z",
260
+ "closed_at" => "2016-01-05T09:24:27Z",
261
+ "pull_request" =>
262
+ { "url" => "https://api.github.com/repos/skywinder/changelog_test/pulls/21",
263
+ "html_url" => "https://github.com/skywinder/changelog_test/pull/21",
264
+ "diff_url" => "https://github.com/skywinder/changelog_test/pull/21.diff",
265
+ "patch_url" => "https://github.com/skywinder/changelog_test/pull/21.patch" },
266
+ "body" =>
267
+ "to test https://github.com/skywinder/github-changelog-generator/pull/305\r\nshould appear in change log with #20" }
268
+
269
+ # Convert times to Time
270
+ expected_pr.each_pair do |k, v|
271
+ expected_pr[k] = Time.parse(v) if v =~ /^2016-01/
272
+ end
273
+
274
+ expect(pull_requests.first).to eq(expected_pr)
275
+ end
276
+
277
+ it "returns issues with labels" do
278
+ issues, _pull_requests = fetcher.fetch_closed_issues_and_pr
279
+ expected = [[], [], ["Bug"], [], ["enhancement"], ["some label"], []]
280
+ expect(issues.map { |i| i["labels"].map { |l| l["name"] } }).to eq(expected)
281
+ end
282
+
283
+ it "returns pull_requests with labels" do
284
+ _issues, pull_requests = fetcher.fetch_closed_issues_and_pr
285
+ expected = [[], [], [], [], [], ["enhancement"], [], [], ["invalid"], [], [], [], [], ["invalid"]]
286
+ expect(pull_requests.map { |i| i["labels"].map { |l| l["name"] } }).to eq(expected)
287
+ end
288
+ end
289
+ end
290
+
291
+ describe "#fetch_closed_pull_requests" do
292
+ context "when API call is valid", :vcr do
293
+ it "returns pull requests" do
294
+ pull_requests = fetcher.fetch_closed_pull_requests
295
+ expect(pull_requests.size).to eq(14)
296
+ end
297
+
298
+ it "returns correct pull request keys" do
299
+ pull_requests = fetcher.fetch_closed_pull_requests
300
+
301
+ pr = pull_requests.first
302
+ 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))
303
+ end
304
+ end
305
+ end
306
+
307
+ describe "#fetch_events_async" do
308
+ context "when API call is valid", :vcr do
309
+ it "populates issues" do
310
+ issues = [{ "url" => "https://api.github.com/repos/skywinder/changelog_test/issues/14",
311
+ "repository_url" => "https://api.github.com/repos/skywinder/changelog_test",
312
+ "labels_url" =>
313
+ "https://api.github.com/repos/skywinder/changelog_test/issues/14/labels{/name}",
314
+ "comments_url" =>
315
+ "https://api.github.com/repos/skywinder/changelog_test/issues/14/comments",
316
+ "events_url" =>
317
+ "https://api.github.com/repos/skywinder/changelog_test/issues/14/events",
318
+ "html_url" => "https://github.com/skywinder/changelog_test/issues/14",
319
+ "id" => 95_419_412,
320
+ "number" => 14,
321
+ "title" => "Issue closed from commit from PR",
322
+ "user" =>
323
+ { "login" => "skywinder",
324
+ "id" => 3_356_474,
325
+ "avatar_url" => "https://avatars.githubusercontent.com/u/3356474?v=3",
326
+ "gravatar_id" => "",
327
+ "url" => "https://api.github.com/users/skywinder",
328
+ "html_url" => "https://github.com/skywinder",
329
+ "followers_url" => "https://api.github.com/users/skywinder/followers",
330
+ "following_url" =>
331
+ "https://api.github.com/users/skywinder/following{/other_user}",
332
+ "gists_url" => "https://api.github.com/users/skywinder/gists{/gist_id}",
333
+ "starred_url" =>
334
+ "https://api.github.com/users/skywinder/starred{/owner}{/repo}",
335
+ "subscriptions_url" =>
336
+ "https://api.github.com/users/skywinder/subscriptions",
337
+ "organizations_url" => "https://api.github.com/users/skywinder/orgs",
338
+ "repos_url" => "https://api.github.com/users/skywinder/repos",
339
+ "events_url" => "https://api.github.com/users/skywinder/events{/privacy}",
340
+ "received_events_url" =>
341
+ "https://api.github.com/users/skywinder/received_events",
342
+ "type" => "User",
343
+ "site_admin" => false },
344
+ "labels" => [],
345
+ "state" => "closed",
346
+ "locked" => false,
347
+ "assignee" => nil,
348
+ "assignees" => [],
349
+ "milestone" => nil,
350
+ "comments" => 0,
351
+ "created_at" => "2015-07-16T12:06:08Z",
352
+ "updated_at" => "2015-07-16T12:21:42Z",
353
+ "closed_at" => "2015-07-16T12:21:42Z",
354
+ "body" => "" }]
355
+
356
+ # Check that they are blank to begin with
357
+ expect(issues.first["events"]).to be_nil
358
+
359
+ fetcher.fetch_events_async(issues)
360
+ issue_events = issues.first["events"]
361
+
362
+ expected_events = [{ "id" => 357_462_189,
363
+ "url" =>
364
+ "https://api.github.com/repos/skywinder/changelog_test/issues/events/357462189",
365
+ "actor" =>
366
+ { "login" => "skywinder",
367
+ "id" => 3_356_474,
368
+ "avatar_url" => "https://avatars.githubusercontent.com/u/3356474?v=3",
369
+ "gravatar_id" => "",
370
+ "url" => "https://api.github.com/users/skywinder",
371
+ "html_url" => "https://github.com/skywinder",
372
+ "followers_url" => "https://api.github.com/users/skywinder/followers",
373
+ "following_url" =>
374
+ "https://api.github.com/users/skywinder/following{/other_user}",
375
+ "gists_url" => "https://api.github.com/users/skywinder/gists{/gist_id}",
376
+ "starred_url" =>
377
+ "https://api.github.com/users/skywinder/starred{/owner}{/repo}",
378
+ "subscriptions_url" =>
379
+ "https://api.github.com/users/skywinder/subscriptions",
380
+ "organizations_url" => "https://api.github.com/users/skywinder/orgs",
381
+ "repos_url" => "https://api.github.com/users/skywinder/repos",
382
+ "events_url" => "https://api.github.com/users/skywinder/events{/privacy}",
383
+ "received_events_url" =>
384
+ "https://api.github.com/users/skywinder/received_events",
385
+ "type" => "User",
386
+ "site_admin" => false },
387
+ "event" => "referenced",
388
+ "commit_id" => "decfe840d1a1b86e0c28700de5362d3365a29555",
389
+ "commit_url" =>
390
+ "https://api.github.com/repos/skywinder/changelog_test/commits/decfe840d1a1b86e0c28700de5362d3365a29555",
391
+ "created_at" => "2015-07-16T12:21:16Z" },
392
+ { "id" => 357_462_542,
393
+ "url" =>
394
+ "https://api.github.com/repos/skywinder/changelog_test/issues/events/357462542",
395
+ "actor" =>
396
+ { "login" => "skywinder",
397
+ "id" => 3_356_474,
398
+ "avatar_url" => "https://avatars.githubusercontent.com/u/3356474?v=3",
399
+ "gravatar_id" => "",
400
+ "url" => "https://api.github.com/users/skywinder",
401
+ "html_url" => "https://github.com/skywinder",
402
+ "followers_url" => "https://api.github.com/users/skywinder/followers",
403
+ "following_url" =>
404
+ "https://api.github.com/users/skywinder/following{/other_user}",
405
+ "gists_url" => "https://api.github.com/users/skywinder/gists{/gist_id}",
406
+ "starred_url" =>
407
+ "https://api.github.com/users/skywinder/starred{/owner}{/repo}",
408
+ "subscriptions_url" =>
409
+ "https://api.github.com/users/skywinder/subscriptions",
410
+ "organizations_url" => "https://api.github.com/users/skywinder/orgs",
411
+ "repos_url" => "https://api.github.com/users/skywinder/repos",
412
+ "events_url" => "https://api.github.com/users/skywinder/events{/privacy}",
413
+ "received_events_url" =>
414
+ "https://api.github.com/users/skywinder/received_events",
415
+ "type" => "User",
416
+ "site_admin" => false },
417
+ "event" => "closed",
418
+ "commit_id" => "decfe840d1a1b86e0c28700de5362d3365a29555",
419
+ "commit_url" =>
420
+ "https://api.github.com/repos/skywinder/changelog_test/commits/decfe840d1a1b86e0c28700de5362d3365a29555",
421
+ "created_at" => "2015-07-16T12:21:42Z" }]
422
+
423
+ # Convert times to Time
424
+ expected_events.map! do |event|
425
+ event.each_pair do |k, v|
426
+ event[k] = Time.parse(v) if v =~ /^201[56]-/
427
+ end
428
+ end
429
+
430
+ expect(issue_events).to eq(expected_events)
431
+ end
432
+ end
433
+ end
434
+
435
+ describe "#fetch_date_of_tag" do
436
+ context "when API call is valid", :vcr do
437
+ it "returns date" do
438
+ tag = { "name" => "v0.0.3",
439
+ "zipball_url" =>
440
+ "https://api.github.com/repos/skywinder/changelog_test/zipball/v0.0.3",
441
+ "tarball_url" =>
442
+ "https://api.github.com/repos/skywinder/changelog_test/tarball/v0.0.3",
443
+ "commit" =>
444
+ { "sha" => "a0cba2b1a1ea9011ab07ee1ac140ba5a5eb8bd90",
445
+ "url" =>
446
+ "https://api.github.com/repos/skywinder/changelog_test/commits/a0cba2b1a1ea9011ab07ee1ac140ba5a5eb8bd90" } }
447
+
448
+ dt = fetcher.fetch_date_of_tag(tag)
449
+ expect(dt).to eq(Time.parse("2015-03-04 19:01:48 UTC"))
450
+ end
451
+ end
452
+ end
453
+
454
+ describe "#querystring_as_hash" do
455
+ it "works on the blank URL" do
456
+ expect { fetcher.send(:querystring_as_hash, "") }.not_to raise_error
457
+ end
458
+
459
+ it "where there are no querystring params" do
460
+ expect { fetcher.send(:querystring_as_hash, "http://example.com") }.not_to raise_error
461
+ end
462
+
463
+ it "returns a String-keyed Hash of querystring params" do
464
+ expect(fetcher.send(:querystring_as_hash, "http://example.com/o.html?str=18&wis=12")).to include("wis" => "12", "str" => "18")
465
+ end
466
+ end
467
+
468
+ describe "#fetch_commit" do
469
+ context "when API call is valid", :vcr do
470
+ it "returns commit" do
471
+ event = { "id" => 357_462_189,
472
+ "url" =>
473
+ "https://api.github.com/repos/skywinder/changelog_test/issues/events/357462189",
474
+ "actor" =>
475
+ { "login" => "skywinder",
476
+ "id" => 3_356_474,
477
+ "avatar_url" => "https://avatars.githubusercontent.com/u/3356474?v=3",
478
+ "gravatar_id" => "",
479
+ "url" => "https://api.github.com/users/skywinder",
480
+ "html_url" => "https://github.com/skywinder",
481
+ "followers_url" => "https://api.github.com/users/skywinder/followers",
482
+ "following_url" =>
483
+ "https://api.github.com/users/skywinder/following{/other_user}",
484
+ "gists_url" => "https://api.github.com/users/skywinder/gists{/gist_id}",
485
+ "starred_url" =>
486
+ "https://api.github.com/users/skywinder/starred{/owner}{/repo}",
487
+ "subscriptions_url" => "https://api.github.com/users/skywinder/subscriptions",
488
+ "organizations_url" => "https://api.github.com/users/skywinder/orgs",
489
+ "repos_url" => "https://api.github.com/users/skywinder/repos",
490
+ "events_url" => "https://api.github.com/users/skywinder/events{/privacy}",
491
+ "received_events_url" =>
492
+ "https://api.github.com/users/skywinder/received_events",
493
+ "type" => "User",
494
+ "site_admin" => false },
495
+ "event" => "referenced",
496
+ "commit_id" => "decfe840d1a1b86e0c28700de5362d3365a29555",
497
+ "commit_url" =>
498
+ "https://api.github.com/repos/skywinder/changelog_test/commits/decfe840d1a1b86e0c28700de5362d3365a29555",
499
+ "created_at" => "2015-07-16T12:21:16Z" }
500
+ commit = fetcher.fetch_commit(event)
501
+
502
+ expectations = [
503
+ %w(sha decfe840d1a1b86e0c28700de5362d3365a29555),
504
+ ["url",
505
+ "https://api.github.com/repos/skywinder/changelog_test/commits/decfe840d1a1b86e0c28700de5362d3365a29555"],
506
+ # OLD API: "https://api.github.com/repos/skywinder/changelog_test/git/commits/decfe840d1a1b86e0c28700de5362d3365a29555"],
507
+ ["html_url",
508
+ "https://github.com/skywinder/changelog_test/commit/decfe840d1a1b86e0c28700de5362d3365a29555"],
509
+ ["author",
510
+ { "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 }],
511
+ ["committer",
512
+ { "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 }],
513
+ ["parents",
514
+ [{ "sha" => "7ec095e5e3caceacedabf44d0b9b10da17c92e51",
515
+ "url" =>
516
+ "https://api.github.com/repos/skywinder/changelog_test/commits/7ec095e5e3caceacedabf44d0b9b10da17c92e51",
517
+ # OLD API: "https://api.github.com/repos/skywinder/changelog_test/git/commits/7ec095e5e3caceacedabf44d0b9b10da17c92e51",
518
+ "html_url" =>
519
+ "https://github.com/skywinder/changelog_test/commit/7ec095e5e3caceacedabf44d0b9b10da17c92e51" }]]
520
+ ]
521
+
522
+ expectations.each do |property, val|
523
+ expect(commit[property]).to eq(val)
524
+ end
525
+ end
526
+ end
527
+ end
528
+ end