github_changelog_generator 1.13.2 → 1.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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