txgh 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +202 -0
  3. data/README.md +64 -0
  4. data/lib/ext/zipline/output_stream.rb +62 -0
  5. data/lib/txgh.rb +53 -0
  6. data/lib/txgh/app.rb +135 -0
  7. data/lib/txgh/category_support.rb +31 -0
  8. data/lib/txgh/config.rb +11 -0
  9. data/lib/txgh/config/config_pair.rb +36 -0
  10. data/lib/txgh/config/key_manager.rb +54 -0
  11. data/lib/txgh/config/provider_instance.rb +20 -0
  12. data/lib/txgh/config/provider_support.rb +26 -0
  13. data/lib/txgh/config/providers.rb +9 -0
  14. data/lib/txgh/config/providers/file_provider.rb +19 -0
  15. data/lib/txgh/config/providers/git_provider.rb +58 -0
  16. data/lib/txgh/config/providers/raw_provider.rb +19 -0
  17. data/lib/txgh/config/tx_config.rb +77 -0
  18. data/lib/txgh/config/tx_manager.rb +15 -0
  19. data/lib/txgh/diff_calculator.rb +90 -0
  20. data/lib/txgh/empty_resource_contents.rb +43 -0
  21. data/lib/txgh/errors.rb +9 -0
  22. data/lib/txgh/github_api.rb +83 -0
  23. data/lib/txgh/github_repo.rb +88 -0
  24. data/lib/txgh/github_request_auth.rb +28 -0
  25. data/lib/txgh/handlers.rb +12 -0
  26. data/lib/txgh/handlers/download_handler.rb +84 -0
  27. data/lib/txgh/handlers/github.rb +10 -0
  28. data/lib/txgh/handlers/github/delete_handler.rb +65 -0
  29. data/lib/txgh/handlers/github/handler.rb +20 -0
  30. data/lib/txgh/handlers/github/push_handler.rb +108 -0
  31. data/lib/txgh/handlers/github/request_handler.rb +106 -0
  32. data/lib/txgh/handlers/response.rb +17 -0
  33. data/lib/txgh/handlers/stream_response.rb +39 -0
  34. data/lib/txgh/handlers/tgz_stream_response.rb +41 -0
  35. data/lib/txgh/handlers/transifex.rb +8 -0
  36. data/lib/txgh/handlers/transifex/hook_handler.rb +77 -0
  37. data/lib/txgh/handlers/transifex/request_handler.rb +78 -0
  38. data/lib/txgh/handlers/triggers.rb +9 -0
  39. data/lib/txgh/handlers/triggers/handler.rb +66 -0
  40. data/lib/txgh/handlers/triggers/pull_handler.rb +29 -0
  41. data/lib/txgh/handlers/triggers/push_handler.rb +21 -0
  42. data/lib/txgh/handlers/zip_stream_response.rb +21 -0
  43. data/lib/txgh/merge_calculator.rb +74 -0
  44. data/lib/txgh/parse_config.rb +24 -0
  45. data/lib/txgh/resource_committer.rb +39 -0
  46. data/lib/txgh/resource_contents.rb +118 -0
  47. data/lib/txgh/resource_downloader.rb +141 -0
  48. data/lib/txgh/resource_updater.rb +104 -0
  49. data/lib/txgh/response_helpers.rb +30 -0
  50. data/lib/txgh/transifex_api.rb +165 -0
  51. data/lib/txgh/transifex_project.rb +37 -0
  52. data/lib/txgh/transifex_request_auth.rb +53 -0
  53. data/lib/txgh/tx_branch_resource.rb +59 -0
  54. data/lib/txgh/tx_logger.rb +12 -0
  55. data/lib/txgh/tx_resource.rb +66 -0
  56. data/lib/txgh/utils.rb +44 -0
  57. data/lib/txgh/version.rb +3 -0
  58. data/spec/app_spec.rb +346 -0
  59. data/spec/category_support_spec.rb +43 -0
  60. data/spec/config/config_pair_spec.rb +47 -0
  61. data/spec/config/key_manager_spec.rb +48 -0
  62. data/spec/config/provider_instance_spec.rb +30 -0
  63. data/spec/config/provider_support_spec.rb +55 -0
  64. data/spec/config/tx_config_spec.rb +49 -0
  65. data/spec/config/tx_manager_spec.rb +57 -0
  66. data/spec/diff_calculator_spec.rb +90 -0
  67. data/spec/github_api_spec.rb +148 -0
  68. data/spec/github_repo_spec.rb +178 -0
  69. data/spec/github_request_auth_spec.rb +39 -0
  70. data/spec/handlers/download_handler_spec.rb +81 -0
  71. data/spec/handlers/github/delete_handler_spec.rb +71 -0
  72. data/spec/handlers/github/push_handler_spec.rb +76 -0
  73. data/spec/handlers/tgz_stream_response_spec.rb +59 -0
  74. data/spec/handlers/transifex/hook_handler_spec.rb +115 -0
  75. data/spec/handlers/zip_stream_response_spec.rb +58 -0
  76. data/spec/helpers/github_payload_builder.rb +141 -0
  77. data/spec/helpers/integration_setup.rb +47 -0
  78. data/spec/helpers/nil_logger.rb +10 -0
  79. data/spec/helpers/standard_txgh_setup.rb +92 -0
  80. data/spec/helpers/test_provider.rb +12 -0
  81. data/spec/integration/cassettes/github_l10n_hook_endpoint.yml +536 -0
  82. data/spec/integration/cassettes/pull.yml +47 -0
  83. data/spec/integration/cassettes/push.yml +544 -0
  84. data/spec/integration/cassettes/transifex_hook_endpoint.yml +560 -0
  85. data/spec/integration/config/tx.config +10 -0
  86. data/spec/integration/hooks_spec.rb +158 -0
  87. data/spec/integration/payloads/github_postbody.json +161 -0
  88. data/spec/integration/payloads/github_postbody_l10n.json +136 -0
  89. data/spec/integration/payloads/github_postbody_release.json +136 -0
  90. data/spec/integration/triggers_spec.rb +45 -0
  91. data/spec/merge_calculator_spec.rb +112 -0
  92. data/spec/parse_config_spec.rb +52 -0
  93. data/spec/resource_committer_spec.rb +42 -0
  94. data/spec/resource_contents_spec.rb +212 -0
  95. data/spec/resource_downloader_spec.rb +205 -0
  96. data/spec/resource_updater_spec.rb +147 -0
  97. data/spec/spec_helper.rb +32 -0
  98. data/spec/transifex_api_spec.rb +345 -0
  99. data/spec/transifex_project_spec.rb +45 -0
  100. data/spec/transifex_request_auth_spec.rb +39 -0
  101. data/spec/tx_branch_resource_spec.rb +99 -0
  102. data/spec/tx_resource_spec.rb +47 -0
  103. data/spec/utils_spec.rb +58 -0
  104. data/txgh.gemspec +29 -0
  105. metadata +296 -0
@@ -0,0 +1,205 @@
1
+ require 'spec_helper'
2
+ require 'helpers/standard_txgh_setup'
3
+ require 'yaml'
4
+
5
+ include Txgh
6
+
7
+ describe ResourceDownloader do
8
+ include StandardTxghSetup
9
+
10
+ let(:api_languages) { %w(es de ja) }
11
+ let(:format) { '.zip' }
12
+
13
+ let(:downloader) do
14
+ ResourceDownloader.new(transifex_project, github_repo, ref)
15
+ end
16
+
17
+ before(:each) do
18
+ allow(transifex_api).to(
19
+ receive(:get_languages).with(project_name).and_return(
20
+ api_languages.map do |language_code|
21
+ { 'language_code' => language_code }
22
+ end
23
+ )
24
+ )
25
+
26
+ allow(transifex_api).to(
27
+ receive(:download) do |resource, language|
28
+ translations_for(resource, language)
29
+ end
30
+ )
31
+ end
32
+
33
+ def translations_for(resource, language)
34
+ outdent(%Q{
35
+ #{language}:
36
+ string: ! "translation"
37
+ })
38
+ end
39
+
40
+ context 'with a basic resource' do
41
+ let(:resource) do
42
+ tx_config.resource(resource_slug)
43
+ end
44
+
45
+ it 'downloads the resource in all languages' do
46
+ expect(downloader.each.to_a).to eq(
47
+ api_languages.map do |language|
48
+ [
49
+ "translations/#{language}/sample.yml",
50
+ translations_for(resource, language)
51
+ ]
52
+ end
53
+ )
54
+ end
55
+ end
56
+
57
+ context 'with more than one resource' do
58
+ before(:each) do
59
+ tx_config.resources << Txgh::TxResource.new(
60
+ project_name, "#{resource_slug}_second", 'YML',
61
+ 'en', 'en.yml', '', 'translations/<lang>/sample2.yml'
62
+ )
63
+
64
+ allow(Txgh::Config::TxManager).to(
65
+ receive(:tx_config).and_return(tx_config)
66
+ )
67
+ end
68
+
69
+ context 'when told to process all branches' do
70
+ let(:branch) { 'all' }
71
+
72
+ it 'includes all resources' do
73
+ resource = tx_config.resource(resource_slug)
74
+ actual_results = downloader.each.to_a
75
+
76
+ expected_results = api_languages.map do |language|
77
+ [
78
+ "translations/#{language}/sample.yml",
79
+ translations_for(resource, language)
80
+ ]
81
+ end
82
+
83
+ expected_results += api_languages.map do |language|
84
+ [
85
+ "translations/#{language}/sample2.yml",
86
+ translations_for(resource, language)
87
+ ]
88
+ end
89
+
90
+ expect(actual_results).to eq(expected_results)
91
+ end
92
+ end
93
+ end
94
+
95
+ context 'when told to upload diffs' do
96
+ let(:diff_point) { 'heads/master' }
97
+ let(:ref) { 'heads/mybranch' }
98
+
99
+ before(:each) do
100
+ allow(github_api).to receive(:download) do |repo_name, file, branch|
101
+ source_for(branch)
102
+ end
103
+ end
104
+
105
+ def source_for(branch)
106
+ if branch == diff_point
107
+ diff_point_source_for(branch)
108
+ else
109
+ head_source_for(branch)
110
+ end
111
+ end
112
+
113
+ def head_source_for(branch)
114
+ # picard unmodified, janeway modified, sisko added, sulu removed
115
+ outdent(%Q{
116
+ en:
117
+ picard: ! "enterprise"
118
+ janeway: ! "uss voyager"
119
+ sisko: ! "deep space nine"
120
+ })
121
+ end
122
+
123
+ def diff_point_source_for(branch)
124
+ outdent(%Q{
125
+ en:
126
+ picard: ! "enterprise"
127
+ janeway: ! "voyager"
128
+ sulu: ! "excelsior"
129
+ })
130
+ end
131
+
132
+ def translations_for(resource, language)
133
+ branch = resource.respond_to?(:branch) ? resource.branch : nil
134
+
135
+ if branch == diff_point
136
+ diff_point_translations_for(language)
137
+ else
138
+ head_translations_for(language)
139
+ end
140
+ end
141
+
142
+ # picard unmodified, janeway modified, sisko added, sulu removed
143
+ def head_translations_for(language)
144
+ outdent(%Q{
145
+ #{language}:
146
+ picard: ! "enterprise (head trans)"
147
+ janeway: ! "uss voyager (head trans)"
148
+ sisko: ! "deep space nine (head trans)"
149
+ })
150
+ end
151
+
152
+ def diff_point_translations_for(language)
153
+ # diff point strings (i.e. strings in master)
154
+ outdent(%Q{
155
+ #{language}:
156
+ picard: ! "enterprise (dp trans)"
157
+ janeway: ! "voyager (dp trans)"
158
+ sulu: ! "excelsior (dp trans)"
159
+ })
160
+ end
161
+
162
+ it 'merges the head and diff point strings together' do
163
+ # picard unmodified, janeway modified, sisko added, sulu removed
164
+ expect(downloader.each.to_a).to eq(
165
+ api_languages.map do |language|
166
+ [
167
+ "translations/#{language}/sample.yml",
168
+ outdent(%Q{
169
+ #{language}:
170
+ picard: ! "enterprise (dp trans)"
171
+ janeway: ! "uss voyager (head trans)"
172
+ sisko: ! "deep space nine (head trans)"
173
+ })
174
+ ]
175
+ end
176
+ )
177
+ end
178
+
179
+ it "works even if the resource doesn't exist in transifex" do
180
+ allow(transifex_api).to(
181
+ receive(:download) do |resource, language|
182
+ if resource.respond_to?(:branch)
183
+ translations_for(resource, language)
184
+ else
185
+ raise TransifexNotFoundError
186
+ end
187
+ end
188
+ )
189
+
190
+ results = downloader.each.to_a
191
+ expect(results).to eq(
192
+ api_languages.map do |language|
193
+ [
194
+ "translations/#{language}/sample.yml",
195
+ outdent(%Q{
196
+ #{language}:
197
+ picard: ! "enterprise (dp trans)"
198
+ janeway: ! "voyager (dp trans)"
199
+ })
200
+ ]
201
+ end
202
+ )
203
+ end
204
+ end
205
+ end
@@ -0,0 +1,147 @@
1
+ require 'spec_helper'
2
+ require 'helpers/standard_txgh_setup'
3
+
4
+ include Txgh
5
+
6
+ describe ResourceUpdater do
7
+ include StandardTxghSetup
8
+
9
+ let(:updater) do
10
+ ResourceUpdater.new(transifex_project, github_repo, logger)
11
+ end
12
+
13
+ let(:branch) { nil }
14
+ let(:ref) { nil }
15
+ let(:resource) { tx_config.resource(resource_slug, ref) }
16
+ let(:commit_sha) { '8765309' }
17
+
18
+ let(:modified_files) do
19
+ [{ 'path' => resource.source_file, 'sha' => 'def456' }]
20
+ end
21
+
22
+ def phrases_for(path)
23
+ YAML.load("|
24
+ en:
25
+ welcome: Hello
26
+ goodbye: Goodbye
27
+ new_phrase: I'm new
28
+ ")
29
+ end
30
+
31
+ before(:each) do
32
+ tree_sha = 'abc123'
33
+
34
+ allow(github_api).to(
35
+ receive(:get_commit).with(repo_name, commit_sha) do
36
+ { 'commit' => { 'tree' => { 'sha' => tree_sha } } }
37
+ end
38
+ )
39
+
40
+ allow(github_api).to(
41
+ receive(:tree).with(repo_name, tree_sha) do
42
+ { 'tree' => modified_files }
43
+ end
44
+ )
45
+
46
+ modified_files.each do |file|
47
+ translations = phrases_for(file['path'])
48
+
49
+ allow(github_api).to(
50
+ receive(:blob).with(repo_name, file['sha']) do
51
+ { 'content' => translations, 'encoding' => 'utf-8' }
52
+ end
53
+ )
54
+ end
55
+ end
56
+
57
+ it 'correctly uploads modified files to transifex' do
58
+ modified_files.each do |file|
59
+ translations = phrases_for(file['path'])
60
+
61
+ expect(transifex_api).to(
62
+ receive(:create_or_update) do |resource, content|
63
+ expect(resource.source_file).to eq(file['path'])
64
+ expect(content).to eq(translations)
65
+ end
66
+ )
67
+ end
68
+
69
+ updater.update_resource(resource, commit_sha)
70
+ end
71
+
72
+ context 'when asked to process all branches' do
73
+ let(:branch) { 'all' }
74
+ let(:ref) { 'heads/master' }
75
+
76
+ it 'uploads by branch name if asked' do
77
+ allow(transifex_api).to receive(:resource_exists?).and_return(false)
78
+
79
+ modified_files.each do |file|
80
+ translations = phrases_for(file['path'])
81
+
82
+ expect(transifex_api).to(
83
+ receive(:create) do |resource, content, categories|
84
+ expect(resource.source_file).to eq(file['path'])
85
+ expect(content).to eq(translations)
86
+ expect(categories).to include("branch:#{ref}")
87
+ end
88
+ )
89
+ end
90
+
91
+ updater.update_resource(resource, commit_sha)
92
+ end
93
+
94
+ it 'adds categories when passed in' do
95
+ expect(transifex_api).to receive(:resource_exists?).and_return(false)
96
+
97
+ modified_files.each do |file|
98
+ translations = phrases_for(file['path'])
99
+
100
+ expect(transifex_api).to(
101
+ receive(:create) do |resource, content, categories|
102
+ expect(categories).to include('foo:bar')
103
+ end
104
+ )
105
+ end
106
+
107
+ updater.update_resource(resource, commit_sha, { 'foo' => 'bar' })
108
+ end
109
+ end
110
+
111
+ context 'when asked to upload diffs' do
112
+ let(:diff_point) { 'heads/diff_point' }
113
+ let(:resource) do
114
+ TxResource.new(
115
+ project_name, resource_slug, 'YAML',
116
+ 'en', 'en.yml', '', 'translation_file'
117
+ )
118
+ end
119
+
120
+ it 'uploads a diff instead of the whole resource' do
121
+ expect(github_api).to(
122
+ receive(:download)
123
+ .with(repo_name, 'en.yml', diff_point)
124
+ .and_return(YAML.load("|
125
+ en:
126
+ welcome: Hello
127
+ goodbye: Goodbye
128
+ "))
129
+ )
130
+
131
+ diff = YAML.load(%Q(|
132
+ en:
133
+ new_phrase: ! "I'm new"
134
+ ))
135
+
136
+ expect(updater).to(
137
+ receive(:upload_by_branch).with(resource, diff, anything)
138
+ )
139
+
140
+ allow(updater).to(
141
+ receive(:categories_for).and_return({})
142
+ )
143
+
144
+ updater.update_resource(resource, commit_sha)
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,32 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'pry-byebug'
4
+ require 'rake'
5
+ require 'rspec'
6
+ require 'txgh'
7
+ require 'vcr'
8
+ require 'webmock'
9
+ require 'yaml'
10
+
11
+ module SpecHelpers
12
+ def outdent(str)
13
+ # The special YAML pipe operator treats the text that follows as literal,
14
+ # and includes newlines, tabs, and spaces. It also strips leading tabs and
15
+ # spaces. This means you can include a fully indented bit of, say, source
16
+ # code in your source code, and it will give you back a string with all the
17
+ # indentation preserved (but without any leading indentation).
18
+ YAML.load("|#{str}")
19
+ end
20
+ end
21
+
22
+ RSpec.configure do |config|
23
+ config.filter_run(focus: true)
24
+ config.run_all_when_everything_filtered = true
25
+ config.filter_run_excluding(integration: true) unless ENV['FULL_SPEC']
26
+ config.include(SpecHelpers)
27
+ end
28
+
29
+ VCR.configure do |config|
30
+ config.cassette_library_dir = 'spec/integration/cassettes'
31
+ config.hook_into :webmock
32
+ end
@@ -0,0 +1,345 @@
1
+ require 'spec_helper'
2
+ require 'helpers/standard_txgh_setup'
3
+
4
+ include Txgh
5
+
6
+ describe TransifexApi do
7
+ include StandardTxghSetup
8
+
9
+ let(:connection) { double(:connection) }
10
+ let(:api) { TransifexApi.create_from_connection(connection) }
11
+ let(:resource) { tx_config.resources.first }
12
+ let(:response) { double(:response) }
13
+
14
+ describe '#create_or_update' do
15
+ context 'with a preexisting resource' do
16
+ before(:each) do
17
+ expect(api).to receive(:resource_exists?).and_return(true)
18
+ end
19
+
20
+ it 'updates the resource with new content' do
21
+ expect(api).to receive(:update_details).with(resource, categories: [])
22
+ expect(api).to receive(:update_content).with(resource, 'new_content')
23
+ expect(api).to receive(:get_resource).and_return({})
24
+
25
+ api.create_or_update(resource, 'new_content')
26
+ end
27
+
28
+ it "additively updates the resource's categories" do
29
+ expect(api).to receive(:update_details) do |rsrc, details|
30
+ expect(details[:categories].sort).to eq(['branch:foobar', 'name:Jesse James'])
31
+ end
32
+
33
+ expect(api).to receive(:update_content).with(resource, 'new_content')
34
+ expect(api).to receive(:get_resource).and_return({ 'categories' => ['name:Jesse James'] })
35
+
36
+ api.create_or_update(resource, 'new_content', ['branch:foobar'])
37
+ end
38
+
39
+ it 'only submits a unique set of categories' do
40
+ expect(api).to receive(:update_details).with(resource, categories: ['branch:foobar'])
41
+ expect(api).to receive(:update_content).with(resource, 'new_content')
42
+ expect(api).to receive(:get_resource).and_return({ 'categories' => ['branch:foobar'] })
43
+
44
+ api.create_or_update(resource, 'new_content', ['branch:foobar'])
45
+ end
46
+ end
47
+
48
+ context 'with a non-existent resource' do
49
+ before(:each) do
50
+ expect(api).to receive(:resource_exists?).and_return(false)
51
+ end
52
+
53
+ it 'makes a request with the correct parameters' do
54
+ expect(connection).to receive(:post) do |url, payload|
55
+ expect(url).to(
56
+ end_with("project/#{project_name}/resources/")
57
+ )
58
+
59
+ expect(payload[:slug]).to eq(resource_slug)
60
+ expect(payload[:name]).to eq(resource.source_file)
61
+ expect(payload[:i18n_type]).to eq('YML')
62
+
63
+ response
64
+ end
65
+
66
+ allow(response).to receive(:status).and_return(200)
67
+ allow(response).to receive(:body).and_return("{}")
68
+ api.create_or_update(resource, 'new_content')
69
+ end
70
+ end
71
+ end
72
+
73
+ describe '#create' do
74
+ it 'makes a request with the correct parameters' do
75
+ expect(connection).to receive(:post) do |url, payload|
76
+ expect(url).to(
77
+ end_with("project/#{project_name}/resources/")
78
+ )
79
+
80
+ expect(payload[:content].io.string).to eq('new_content')
81
+ expect(payload[:categories]).to eq('abc def')
82
+ response
83
+ end
84
+
85
+ allow(response).to receive(:status).and_return(200)
86
+ api.create(resource, 'new_content', ['abc', 'def'])
87
+ end
88
+
89
+ it 'submits de-duped categories' do
90
+ expect(connection).to receive(:post) do |url, payload|
91
+ expect(payload[:categories]).to eq('abc')
92
+ response
93
+ end
94
+
95
+ allow(response).to receive(:status).and_return(200)
96
+ api.create(resource, 'new_content', ['abc', 'abc'])
97
+ end
98
+
99
+ it 'raises an exception if the api responds with an error code' do
100
+ allow(connection).to receive(:post).and_return(response)
101
+ allow(response).to receive(:status).and_return(404)
102
+ allow(response).to receive(:body).and_return('{}')
103
+ expect { api.create(resource, 'new_content') }.to raise_error(TransifexApiError)
104
+ end
105
+ end
106
+
107
+ describe '#delete' do
108
+ it 'deletes the given resource' do
109
+ expect(connection).to receive(:delete) do |url|
110
+ expect(url).to(
111
+ end_with("project/#{project_name}/resource/#{resource_slug}/")
112
+ )
113
+ end
114
+
115
+ api.delete(resource)
116
+ end
117
+ end
118
+
119
+ describe '#update_content' do
120
+ it 'makes a request with the correct parameters' do
121
+ expect(connection).to receive(:put) do |url, payload|
122
+ expect(url).to(
123
+ end_with("project/#{project_name}/resource/#{resource_slug}/content/")
124
+ )
125
+
126
+ expect(payload[:content].io.string).to eq('new_content')
127
+ response
128
+ end
129
+
130
+ allow(response).to receive(:status).and_return(200)
131
+ api.update_content(resource, 'new_content')
132
+ end
133
+
134
+ it 'raises an exception if the api responds with an error code' do
135
+ allow(connection).to receive(:put).and_return(response)
136
+ allow(response).to receive(:status).and_return(404)
137
+ allow(response).to receive(:body).and_return('{}')
138
+ expect { api.update_content(resource, 'new_content') }.to raise_error(TransifexApiError)
139
+ end
140
+ end
141
+
142
+ describe '#update_details' do
143
+ it 'makes a request with the correct parameters' do
144
+ expect(connection).to receive(:put) do |url, payload|
145
+ expect(url).to(
146
+ end_with("project/#{project_name}/resource/#{resource_slug}/")
147
+ )
148
+
149
+ expect(payload[:i18n_type]).to eq('FOO')
150
+ expect(payload[:categories]).to eq(['abc'])
151
+ response
152
+ end
153
+
154
+ allow(response).to receive(:status).and_return(200)
155
+ api.update_details(resource, i18n_type: 'FOO', categories: ['abc'])
156
+ end
157
+
158
+ it 'raises an exception if the api responds with an error code' do
159
+ allow(connection).to receive(:put).and_return(response)
160
+ allow(response).to receive(:status).and_return(404)
161
+ allow(response).to receive(:body).and_return('{}')
162
+ expect { api.update_details(resource, {}) }.to raise_error(TransifexApiError)
163
+ end
164
+ end
165
+
166
+ describe '#resource_exists?' do
167
+ it 'makes a request with the correct parameters' do
168
+ expect(connection).to receive(:get) do |url|
169
+ expect(url).to(
170
+ end_with("project/#{project_name}/resource/#{resource_slug}/")
171
+ )
172
+
173
+ response
174
+ end
175
+
176
+ allow(response).to receive(:status).and_return(200)
177
+ api.resource_exists?(resource)
178
+ end
179
+
180
+ it 'returns true if the api responds with a 200 status code' do
181
+ allow(connection).to receive(:get).and_return(response)
182
+ allow(response).to receive(:status).and_return(200)
183
+ expect(api.resource_exists?(resource)).to eq(true)
184
+ end
185
+
186
+ it 'returns false if the api does not respond with a 200 status code' do
187
+ allow(connection).to receive(:get).and_return(response)
188
+ allow(response).to receive(:status).and_return(404)
189
+ expect(api.resource_exists?(resource)).to eq(false)
190
+ end
191
+ end
192
+
193
+ describe '#download' do
194
+ let(:language) { 'pt-BR' }
195
+
196
+ it 'makes a request with the correct parameters' do
197
+ expect(connection).to receive(:get) do |url|
198
+ expect(url).to(
199
+ end_with("project/#{project_name}/resource/#{resource_slug}/translation/#{language}/")
200
+ )
201
+
202
+ response
203
+ end
204
+
205
+ allow(response).to receive(:status).and_return(200)
206
+ allow(response).to receive(:body).and_return('{}')
207
+ api.download(resource, language)
208
+ end
209
+
210
+ it 'parses and returns the response content' do
211
+ allow(connection).to receive(:get).and_return(response)
212
+ allow(response).to receive(:status).and_return(200)
213
+ allow(response).to receive(:body).and_return('{"content": "foobar"}')
214
+ expect(api.download(resource, language)).to eq('foobar')
215
+ end
216
+
217
+ it 'raises an exception if the api responds with an error code' do
218
+ allow(connection).to receive(:get).and_return(response)
219
+ allow(response).to receive(:status).and_return(401)
220
+ allow(response).to receive(:body).and_return('{}')
221
+ expect { api.download(resource, language) }.to raise_error(TransifexApiError)
222
+ end
223
+
224
+ it 'raises a specific exception if the api responds with a 404 not found' do
225
+ allow(connection).to receive(:get).and_return(response)
226
+ allow(response).to receive(:status).and_return(404)
227
+ allow(response).to receive(:body).and_return('{}')
228
+ expect { api.download(resource, language) }.to raise_error(
229
+ TransifexNotFoundError
230
+ )
231
+ end
232
+ end
233
+
234
+ describe '#get_resource' do
235
+ it 'makes a request with the correct parameters' do
236
+ expect(connection).to receive(:get) do |url, payload|
237
+ expect(url).to(
238
+ end_with("project/#{project_name}/resource/#{resource_slug}/")
239
+ )
240
+
241
+ response
242
+ end
243
+
244
+ allow(response).to receive(:status).and_return(200)
245
+ allow(response).to receive(:body).and_return('{"foo":"bar"}')
246
+ expect(api.get_resource(*resource.slugs)).to eq({ 'foo' => 'bar' })
247
+ end
248
+
249
+ it 'raises an exception if the api responds with an error code' do
250
+ allow(connection).to receive(:get).and_return(response)
251
+ allow(response).to receive(:status).and_return(404)
252
+ allow(response).to receive(:body).and_return('{}')
253
+ expect { api.get_resource(*resource.slugs) }.to raise_error(TransifexApiError)
254
+ end
255
+ end
256
+
257
+ describe '#get_resources' do
258
+ it 'makes a request with the correct parameters' do
259
+ expect(connection).to receive(:get) do |url, payload|
260
+ expect(url).to(
261
+ end_with("project/#{project_name}/resources/")
262
+ )
263
+
264
+ response
265
+ end
266
+
267
+ allow(response).to receive(:status).and_return(200)
268
+ allow(response).to receive(:body).and_return('{"foo":"bar"}')
269
+ expect(api.get_resources(project_name)).to eq({ 'foo' => 'bar' })
270
+ end
271
+
272
+ it 'raises an exception if the api responds with an error code' do
273
+ allow(connection).to receive(:get).and_return(response)
274
+ allow(response).to receive(:status).and_return(404)
275
+ allow(response).to receive(:body).and_return('{}')
276
+ expect { api.get_resources(project_name) }.to raise_error(TransifexApiError)
277
+ end
278
+ end
279
+
280
+ describe '#get_languages' do
281
+ it 'makes a request with the correct parameters' do
282
+ expect(connection).to receive(:get) do |url, payload|
283
+ expect(url).to(
284
+ end_with("project/#{project_name}/languages/")
285
+ )
286
+
287
+ response
288
+ end
289
+
290
+ allow(response).to receive(:status).and_return(200)
291
+ allow(response).to receive(:body).and_return('[{"language_code":"de"}]')
292
+ expect(api.get_languages(project_name)).to eq([{ 'language_code' => 'de' }])
293
+ end
294
+
295
+ it 'raises an exception if the api responds with an error code' do
296
+ allow(connection).to receive(:get).and_return(response)
297
+ allow(response).to receive(:status).and_return(404)
298
+ allow(response).to receive(:body).and_return('{}')
299
+ expect { api.get_languages(project_name) }.to raise_error(TransifexApiError)
300
+ end
301
+ end
302
+
303
+ describe '#get_project' do
304
+ it 'makes a request with the correct parameters' do
305
+ expect(connection).to receive(:get) do |url, payload|
306
+ expect(url).to(
307
+ end_with("project/#{project_name}/")
308
+ )
309
+
310
+ response
311
+ end
312
+
313
+ allow(response).to receive(:status).and_return(200)
314
+ allow(response).to receive(:body).and_return('{"slug":"projectslug"}')
315
+ expect(api.get_project(project_name)).to eq({ 'slug' => 'projectslug' })
316
+ end
317
+
318
+ it 'raises an exception if the api responds with an error code' do
319
+ allow(connection).to receive(:get).and_return(response)
320
+ allow(response).to receive(:status).and_return(404)
321
+ allow(response).to receive(:body).and_return('{}')
322
+ expect { api.get_project(project_name) }.to raise_error(TransifexApiError)
323
+ end
324
+ end
325
+
326
+ describe '#get_formats' do
327
+ it 'makes a request with the correct parameters' do
328
+ expect(connection).to receive(:get) do |url, payload|
329
+ expect(url).to end_with("formats/")
330
+ response
331
+ end
332
+
333
+ allow(response).to receive(:status).and_return(200)
334
+ allow(response).to receive(:body).and_return('{}')
335
+ expect(api.get_formats).to eq({})
336
+ end
337
+
338
+ it 'raises an exception if the api responds with an error code' do
339
+ allow(connection).to receive(:get).and_return(response)
340
+ allow(response).to receive(:status).and_return(404)
341
+ allow(response).to receive(:body).and_return('{}')
342
+ expect { api.get_formats }.to raise_error(TransifexApiError)
343
+ end
344
+ end
345
+ end