txgh 1.0.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 (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