txgh-server 1.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/lib/txgh-server/application.rb +141 -0
  3. data/lib/txgh-server/download_handler.rb +85 -0
  4. data/lib/txgh-server/github_request_auth.rb +28 -0
  5. data/lib/txgh-server/response.rb +15 -0
  6. data/lib/txgh-server/response_helpers.rb +26 -0
  7. data/lib/txgh-server/stream_response.rb +37 -0
  8. data/lib/txgh-server/tgz_stream_response.rb +39 -0
  9. data/lib/txgh-server/transifex_request_auth.rb +53 -0
  10. data/lib/txgh-server/triggers/handler.rb +50 -0
  11. data/lib/txgh-server/triggers/pull_handler.rb +18 -0
  12. data/lib/txgh-server/triggers/push_handler.rb +18 -0
  13. data/lib/txgh-server/triggers.rb +7 -0
  14. data/lib/txgh-server/version.rb +3 -0
  15. data/lib/txgh-server/webhooks/github/delete_handler.rb +37 -0
  16. data/lib/txgh-server/webhooks/github/handler.rb +20 -0
  17. data/lib/txgh-server/webhooks/github/ping_handler.rb +18 -0
  18. data/lib/txgh-server/webhooks/github/push_handler.rb +124 -0
  19. data/lib/txgh-server/webhooks/github/request_handler.rb +113 -0
  20. data/lib/txgh-server/webhooks/github.rb +11 -0
  21. data/lib/txgh-server/webhooks/transifex/hook_handler.rb +94 -0
  22. data/lib/txgh-server/webhooks/transifex/request_handler.rb +78 -0
  23. data/lib/txgh-server/webhooks/transifex.rb +8 -0
  24. data/lib/txgh-server/webhooks.rb +6 -0
  25. data/lib/txgh-server/zip_stream_response.rb +19 -0
  26. data/lib/txgh-server.rb +23 -0
  27. data/spec/application_spec.rb +347 -0
  28. data/spec/download_handler_spec.rb +91 -0
  29. data/spec/github_request_auth_spec.rb +39 -0
  30. data/spec/helpers/github_payload_builder.rb +141 -0
  31. data/spec/helpers/integration_setup.rb +47 -0
  32. data/spec/integration/cassettes/github_l10n_hook_endpoint.yml +536 -0
  33. data/spec/integration/cassettes/pull.yml +47 -0
  34. data/spec/integration/cassettes/push.yml +544 -0
  35. data/spec/integration/cassettes/transifex_hook_endpoint.yml +221 -0
  36. data/spec/integration/config/tx.config +10 -0
  37. data/spec/integration/hooks_spec.rb +159 -0
  38. data/spec/integration/payloads/github_postbody.json +161 -0
  39. data/spec/integration/payloads/github_postbody_l10n.json +136 -0
  40. data/spec/integration/payloads/github_postbody_release.json +136 -0
  41. data/spec/integration/triggers_spec.rb +45 -0
  42. data/spec/spec_helper.rb +26 -0
  43. data/spec/tgz_stream_response_spec.rb +59 -0
  44. data/spec/transifex_request_auth_spec.rb +39 -0
  45. data/spec/webhooks/github/delete_handler_spec.rb +38 -0
  46. data/spec/webhooks/github/ping_handler_spec.rb +16 -0
  47. data/spec/webhooks/github/push_handler_spec.rb +106 -0
  48. data/spec/webhooks/transifex/hook_handler_spec.rb +136 -0
  49. data/spec/zip_stream_response_spec.rb +58 -0
  50. data/txgh-server.gemspec +24 -0
  51. metadata +170 -0
@@ -0,0 +1,347 @@
1
+ require 'spec_helper'
2
+ require 'rack/test'
3
+ require 'uri'
4
+
5
+ require 'helpers/github_payload_builder'
6
+ require 'helpers/standard_txgh_setup'
7
+
8
+ describe TxghServer::Application do
9
+ include Rack::Test::Methods
10
+ include StandardTxghSetup
11
+
12
+ def app
13
+ TxghServer::Application
14
+ end
15
+
16
+ describe '/health_check' do
17
+ it 'indicates the server is running, returns a 200' do
18
+ get '/health_check'
19
+ expect(last_response).to be_ok
20
+ expect(JSON.parse(last_response.body)).to eq({})
21
+ end
22
+ end
23
+
24
+ describe '/config' do
25
+ it 'fetches and returns the config for the given project' do
26
+ get '/config', project_slug: project_name
27
+ config = JSON.parse(last_response.body)['data']
28
+ expect(config).to include('resources')
29
+ expect(config).to_not include('branch_slug')
30
+ expect(config['resources'].first).to eq(
31
+ 'project_slug' => 'my_awesome_project',
32
+ 'resource_slug' => 'my_resource',
33
+ 'type' => 'YML',
34
+ 'source_lang' => 'en',
35
+ 'source_file' => 'sample.yml',
36
+ 'translation_file' => 'translations/<lang>/sample.yml'
37
+ )
38
+ end
39
+
40
+ it 'fetches and returns the config for the given project and branch' do
41
+ get '/config', project_slug: project_name, branch: branch
42
+ config = JSON.parse(last_response.body)['data']
43
+ expect(config).to include('resources')
44
+ expect(config['branch_slug']).to eq('heads_master')
45
+ expect(config['resources'].first).to eq(
46
+ 'project_slug' => 'my_awesome_project',
47
+ 'resource_slug' => 'my_resource',
48
+ 'type' => 'YML',
49
+ 'source_lang' => 'en',
50
+ 'source_file' => 'sample.yml',
51
+ 'translation_file' => 'translations/<lang>/sample.yml'
52
+ )
53
+ end
54
+
55
+ it "responds with not found when config can't be found" do
56
+ message = 'Red alert!'
57
+
58
+ expect(Txgh::Config::TxManager).to(
59
+ receive(:tx_config).and_raise(Txgh::ConfigNotFoundError, message)
60
+ )
61
+
62
+ get '/config', project_slug: project_name
63
+ expect(last_response.status).to eq(404)
64
+ response = JSON.parse(last_response.body)
65
+ expect(response).to eq([
66
+ 'error' => message
67
+ ])
68
+ end
69
+
70
+ it 'responds with internal error when an unexpected error occurs' do
71
+ message = 'Red alert!'
72
+
73
+ expect(Txgh::Config::TxManager).to(
74
+ receive(:tx_config).and_raise(StandardError, message)
75
+ )
76
+
77
+ get '/config', project_slug: project_name
78
+ expect(last_response.status).to eq(500)
79
+ response = JSON.parse(last_response.body)
80
+ expect(response).to eq([
81
+ 'error' => message
82
+ ])
83
+ end
84
+ end
85
+ end
86
+
87
+ describe TxghServer::WebhookEndpoints do
88
+ include Rack::Test::Methods
89
+ include StandardTxghSetup
90
+ include TxghServer::ResponseHelpers
91
+
92
+ def app
93
+ TxghServer::WebhookEndpoints
94
+ end
95
+
96
+ let(:config) do
97
+ Txgh::Config::ConfigPair.new(project_config, repo_config)
98
+ end
99
+
100
+ before(:each) do
101
+ allow(Txgh::Config::KeyManager).to(
102
+ receive(:config_from_project).with(project_name).and_return(config)
103
+ )
104
+
105
+ allow(Txgh::Config::KeyManager).to(
106
+ receive(:config_from_repo).with(repo_name).and_return(config)
107
+ )
108
+ end
109
+
110
+ describe '/transifex' do
111
+ def sign_with(body)
112
+ header(
113
+ TxghServer::TransifexRequestAuth::TRANSIFEX_HEADER,
114
+ TxghServer::TransifexRequestAuth.header_value(
115
+ body, config.transifex_project.webhook_secret
116
+ )
117
+ )
118
+ end
119
+
120
+ let(:handler) { double(:handler) }
121
+
122
+ let(:params) do
123
+ {
124
+ 'project' => project_name,
125
+ 'resource' => resource_slug,
126
+ 'language' => language,
127
+ 'translated' => '100'
128
+ }
129
+ end
130
+
131
+ let(:payload) { URI.encode_www_form(params.to_a) }
132
+
133
+ before(:each) do
134
+ allow(TxghServer::Webhooks::Transifex::HookHandler).to(
135
+ receive(:new) do |options|
136
+ expect(options[:project].name).to eq(project_name)
137
+ expect(options[:repo].name).to eq(repo_name)
138
+ handler
139
+ end
140
+ )
141
+ end
142
+
143
+ it 'creates a handler and executes it' do
144
+ expect(handler).to(
145
+ receive(:execute).and_return(respond_with(200, true))
146
+ )
147
+
148
+ payload = URI.encode_www_form(params.to_a)
149
+ sign_with payload
150
+ post '/transifex', payload
151
+ expect(last_response).to be_ok
152
+ end
153
+
154
+ it 'returns unauthorized if not properly signed' do
155
+ post '/transifex', payload
156
+ expect(last_response.status).to eq(401)
157
+ end
158
+
159
+ it 'returns internal error on unexpected error' do
160
+ expect(Txgh::Config::KeyManager).to(
161
+ receive(:config_from_project).and_raise(StandardError)
162
+ )
163
+
164
+ sign_with payload
165
+ post '/transifex', payload
166
+ expect(last_response.status).to eq(500)
167
+ expect(JSON.parse(last_response.body)).to eq([
168
+ 'error' => 'Internal server error: StandardError'
169
+ ])
170
+ end
171
+ end
172
+
173
+ describe '/github' do
174
+ def sign_with(body)
175
+ header(
176
+ TxghServer::GithubRequestAuth::GITHUB_HEADER,
177
+ TxghServer::GithubRequestAuth.header_value(
178
+ body, config.github_repo.webhook_secret
179
+ )
180
+ )
181
+ end
182
+
183
+ describe 'push event' do
184
+ let(:handler) { double(:handler) }
185
+
186
+ before(:each) do
187
+ allow(TxghServer::Webhooks::Github::PushHandler).to(
188
+ receive(:new) do |options|
189
+ expect(options[:project].name).to eq(project_name)
190
+ expect(options[:repo].name).to eq(repo_name)
191
+ handler
192
+ end
193
+ )
194
+ end
195
+
196
+ it 'forwards the request to the github request handler' do
197
+ expect(handler).to(
198
+ receive(:execute).and_return(respond_with(200, true))
199
+ )
200
+
201
+ payload = GithubPayloadBuilder.push_payload(repo_name, ref)
202
+
203
+ sign_with payload.to_json
204
+ header 'X-GitHub-Event', 'push'
205
+ post '/github', payload.to_json
206
+
207
+ expect(last_response).to be_ok
208
+ end
209
+
210
+ it 'returns unauthorized if not properly signed' do
211
+ payload = GithubPayloadBuilder.push_payload(repo_name, ref)
212
+
213
+ header 'X-GitHub-Event', 'push'
214
+ post '/github', payload.to_json
215
+
216
+ expect(last_response.status).to eq(401)
217
+ end
218
+
219
+ it 'returns invalid request if event unrecognized' do
220
+ header 'X-GitHub-Event', 'foobar'
221
+ post '/github'
222
+
223
+ expect(last_response.status).to eq(400)
224
+ end
225
+
226
+ it 'returns internal error on unexpected error' do
227
+ payload = GithubPayloadBuilder.push_payload(repo_name, ref)
228
+
229
+ expect(Txgh::Config::KeyManager).to(
230
+ receive(:config_from_repo).and_raise(StandardError)
231
+ )
232
+
233
+ header 'X-GitHub-Event', 'push'
234
+ post '/github', payload.to_json
235
+
236
+ expect(last_response.status).to eq(500)
237
+ expect(JSON.parse(last_response.body)).to eq([
238
+ 'error' => 'Internal server error: StandardError'
239
+ ])
240
+ end
241
+ end
242
+ end
243
+ end
244
+
245
+ describe TxghServer::TriggerEndpoints do
246
+ include Rack::Test::Methods
247
+ include StandardTxghSetup
248
+
249
+ def app
250
+ TxghServer::TriggerEndpoints
251
+ end
252
+
253
+ let(:config) do
254
+ Txgh::Config::ConfigPair.new(project_config, repo_config)
255
+ end
256
+
257
+ before(:each) do
258
+ allow(Txgh::Config::KeyManager).to(
259
+ receive(:config_from_project).with(project_name).and_return(config)
260
+ )
261
+
262
+ allow(Txgh::Config::KeyManager).to(
263
+ receive(:config_from_repo).with(repo_name).and_return(config)
264
+ )
265
+ end
266
+
267
+ describe '/push' do
268
+ let(:params) do
269
+ { project_slug: project_name, resource_slug: resource_slug, branch: branch }
270
+ end
271
+
272
+ it 'updates the expected resource' do
273
+ updater = double(:updater)
274
+ expect(Txgh::ResourceUpdater).to receive(:new).and_return(updater)
275
+ expect(Txgh::GithubApi).to receive(:new).and_return(github_api)
276
+ expect(github_api).to receive(:get_ref).and_return(object: { sha: 'abc123' })
277
+
278
+ expect(updater).to receive(:update_resource) do |resource, sha|
279
+ expected_branch = Txgh::Utils.absolute_branch(branch)
280
+ expect(resource.branch).to eq(expected_branch)
281
+ expect(resource.project_slug).to eq(project_name)
282
+ expect(resource.resource_slug).to(
283
+ eq("#{resource_slug}-#{Txgh::Utils.slugify(expected_branch)}")
284
+ )
285
+ end
286
+
287
+ patch '/push', params
288
+ expect(last_response).to be_ok
289
+ end
290
+
291
+ it 'returns internal error on unexpected error' do
292
+ expect(Txgh::Config::KeyManager).to(
293
+ receive(:config_from_project).and_raise(StandardError)
294
+ )
295
+
296
+ patch '/push', params
297
+
298
+ expect(last_response.status).to eq(500)
299
+ expect(JSON.parse(last_response.body)).to eq([
300
+ { 'error' => 'Internal server error: StandardError' }
301
+ ])
302
+ end
303
+ end
304
+
305
+ describe '/pull' do
306
+ let(:params) do
307
+ { project_slug: project_name, resource_slug: resource_slug, branch: branch }
308
+ end
309
+
310
+ it 'updates translations (in all locales) in the expected repo' do
311
+ committer = double(:committer)
312
+ languages = [{ 'language_code' => 'pt' }, { 'language_code' => 'ja' }]
313
+ project_config['languages'] = %w(pt ja)
314
+ expect(Txgh::ResourceCommitter).to receive(:new).and_return(committer)
315
+ expect(Txgh::TransifexApi).to receive(:new).and_return(transifex_api)
316
+ expect(transifex_api).to receive(:get_languages).and_return(languages)
317
+
318
+ languages.each do |language|
319
+ expect(committer).to receive(:commit_resource) do |resource, branch, lang|
320
+ expect(branch).to eq(branch)
321
+ expect(lang).to eq(language['language_code'])
322
+ expect(resource.branch).to eq(branch)
323
+ expect(resource.project_slug).to eq(project_name)
324
+ expect(resource.resource_slug).to(
325
+ eq("#{resource_slug}-#{Txgh::Utils.slugify(branch)}")
326
+ )
327
+ end
328
+ end
329
+
330
+ patch '/pull', params
331
+ expect(last_response).to be_ok
332
+ end
333
+
334
+ it 'returns internal error on unexpected error' do
335
+ expect(Txgh::Config::KeyManager).to(
336
+ receive(:config_from_project).and_raise(StandardError)
337
+ )
338
+
339
+ patch '/pull', params
340
+
341
+ expect(last_response.status).to eq(500)
342
+ expect(JSON.parse(last_response.body)).to eq([
343
+ { 'error' => 'Internal server error: StandardError' }
344
+ ])
345
+ end
346
+ end
347
+ end
@@ -0,0 +1,91 @@
1
+ require 'spec_helper'
2
+ require 'helpers/standard_txgh_setup'
3
+ require 'yaml'
4
+
5
+ include TxghServer
6
+
7
+ describe DownloadHandler do
8
+ include StandardTxghSetup
9
+
10
+ let(:format) { DownloadHandler::DEFAULT_FORMAT }
11
+
12
+ let(:params) do
13
+ {
14
+ 'format' => format,
15
+ 'project_slug' => project_name,
16
+ 'branch' => ref
17
+ }
18
+ end
19
+
20
+ context '.handle_request' do
21
+ let(:request) do
22
+ double(:request).tap do |dbl|
23
+ allow(dbl).to receive(:params).and_return(params)
24
+ allow(dbl).to receive(:env).and_return(env)
25
+ end
26
+ end
27
+
28
+ let(:env) do
29
+ { 'REQUEST_PATH' => "path/to/#{project_name}#{format}" }
30
+ end
31
+
32
+ it 'responds with a streaming zip and has the project name as the attachment' do
33
+ response = DownloadHandler.handle_request(request)
34
+ expect(response).to be_streaming
35
+ expect(response).to be_a(ZipStreamResponse)
36
+ expect(response.attachment).to eq(project_name)
37
+ end
38
+
39
+ context 'with a tgz format specified' do
40
+ let(:format) { '.tgz' }
41
+
42
+ it 'responds with a streaming tgz download' do
43
+ response = DownloadHandler.handle_request(request)
44
+ expect(response).to be_streaming
45
+ expect(response).to be_a(TgzStreamResponse)
46
+ end
47
+ end
48
+
49
+ context 'when an error occurs' do
50
+ before(:each) do
51
+ expect(request).to receive(:params).and_raise(StandardError)
52
+ response = DownloadHandler.handle_request(request)
53
+ expect(response).to_not be_streaming
54
+ expect(response.status).to eq(500)
55
+ end
56
+ end
57
+ end
58
+
59
+ context '#execute' do
60
+ let(:handler) do
61
+ DownloadHandler.new(transifex_project, github_repo, params, logger)
62
+ end
63
+
64
+ it 'responds with a streaming zip download' do
65
+ expect(handler.execute).to be_a(ZipStreamResponse)
66
+ end
67
+
68
+ it 'responds with the project name as the attachment' do
69
+ response = handler.execute
70
+ expect(response.attachment).to eq(project_name)
71
+ end
72
+
73
+ context 'with a tgz format specified' do
74
+ let(:format) { '.tgz' }
75
+
76
+ it 'responds with a streaming tgz download' do
77
+ expect(handler.execute).to be_a(TgzStreamResponse)
78
+ end
79
+ end
80
+
81
+ context 'with a set of languages' do
82
+ let(:supported_languages) { %w(is fr) }
83
+
84
+ it "downloads translations for the project's supported languages" do
85
+ allow(transifex_api).to receive(:download)
86
+ files = handler.execute.enum.to_a.map(&:first)
87
+ expect(files).to eq(%w(translations/is/sample.yml translations/fr/sample.yml))
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+ require 'rack'
3
+
4
+ include TxghServer
5
+
6
+ describe GithubRequestAuth do
7
+ let(:secret) { 'abc123' }
8
+ let(:params) { '{"param1":"value1","param2":"value2","param3":123}' }
9
+ let(:valid_signature) { 'ea62c3f65c8e42f155d96a25b7ba6eb5d320630e' }
10
+
11
+ describe '.authentic_request?' do
12
+ it 'returns true if the request is signed correctly' do
13
+ request = Rack::Request.new(
14
+ GithubRequestAuth::RACK_HEADER => "sha1=#{valid_signature}",
15
+ 'rack.input' => StringIO.new(params)
16
+ )
17
+
18
+ authentic = GithubRequestAuth.authentic_request?(request, secret)
19
+ expect(authentic).to eq(true)
20
+ end
21
+
22
+ it 'returns false if the request is not signed correctly' do
23
+ request = Rack::Request.new(
24
+ GithubRequestAuth::RACK_HEADER => 'incorrect',
25
+ 'rack.input' => StringIO.new(params)
26
+ )
27
+
28
+ authentic = GithubRequestAuth.authentic_request?(request, secret)
29
+ expect(authentic).to eq(false)
30
+ end
31
+ end
32
+
33
+ describe '.header' do
34
+ it 'calculates the signature and formats it as an http header' do
35
+ value = GithubRequestAuth.header_value(params, secret)
36
+ expect(value).to eq("sha1=#{valid_signature}")
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,141 @@
1
+ require 'json'
2
+
3
+ class GithubPayloadBuilder
4
+ class << self
5
+ def push_payload(*args)
6
+ GithubPushPayload.new(*args)
7
+ end
8
+
9
+ def delete_payload(*args)
10
+ GithubDeletePayload.new(*args)
11
+ end
12
+ end
13
+ end
14
+
15
+ class GithubPayload
16
+ def to_h
17
+ # convert symbolized keys to strings
18
+ JSON.parse(to_json)
19
+ end
20
+
21
+ def to_json
22
+ @result.to_json
23
+ end
24
+
25
+ protected
26
+
27
+ def digits
28
+ @@digits ||= ('a'..'f').to_a + ('0'..'9').to_a
29
+ end
30
+
31
+ def generate_timestamp
32
+ Time.now.strftime('%Y-%m-%dT%H:%M:%S%:z')
33
+ end
34
+
35
+ def generate_sha
36
+ blank_commit_id.gsub(/0/) { digits.sample }
37
+ end
38
+
39
+ def blank_commit_id
40
+ '0' * 40
41
+ end
42
+ end
43
+
44
+ class GithubDeletePayload < GithubPayload
45
+ attr_reader :repo, :ref
46
+
47
+ def initialize(repo, ref)
48
+ @repo = repo
49
+ @ref = ref
50
+
51
+ @result = {
52
+ ref: "refs/#{ref}",
53
+ ref_type: 'branch',
54
+ pusher_type: 'user',
55
+
56
+ repository: {
57
+ name: repo.split('/').last,
58
+ full_name: repo,
59
+ owner: {
60
+ login: repo.split('/').first
61
+ }
62
+ },
63
+
64
+ sender: {
65
+ login: repo.split('/').first,
66
+ type: 'User'
67
+ }
68
+ }
69
+ end
70
+ end
71
+
72
+ class GithubPushPayload < GithubPayload
73
+ attr_reader :repo, :ref, :before, :after
74
+
75
+ DEFAULT_USER = {
76
+ name: 'Test User',
77
+ email: 'test@user.com',
78
+ username: 'testuser'
79
+ }
80
+
81
+ def initialize(repo, ref, before = nil, after = nil)
82
+ @repo = repo
83
+ @ref = ref
84
+ @before = before || blank_commit_id
85
+ @after = after || generate_sha
86
+
87
+ @result = {
88
+ ref: "refs/#{ref}",
89
+ before: @before,
90
+ after: @after,
91
+ created: true,
92
+ deleted: false,
93
+ forced: true,
94
+ base_ref: nil,
95
+ compare: "https://github.com/#{@repo}/commit/#{@after[0..12]}",
96
+ commits: [],
97
+ repository: {
98
+ name: repo.split('/').last,
99
+ full_name: repo,
100
+ owner: {
101
+ name: repo.split('/').first
102
+ }
103
+ }
104
+ }
105
+ end
106
+
107
+ def add_commit(options = {})
108
+ id = if commits.empty? && !options.include?(:id)
109
+ after
110
+ else
111
+ options.fetch(:id) { generate_sha }
112
+ end
113
+
114
+ commit_data = {
115
+ id: id,
116
+ distinct: options.fetch(:distinct, true),
117
+ message: options.fetch(:message, 'Default commit message'),
118
+ timestamp: options.fetch(:timestamp) { generate_timestamp },
119
+ url: "https://github.com/#{repo}/commit/#{id}",
120
+ author: options.fetch(:author, DEFAULT_USER),
121
+ committer: options.fetch(:committer, DEFAULT_USER),
122
+ added: options.fetch(:added, []),
123
+ removed: options.fetch(:removed, []),
124
+ modified: options.fetch(:modified, [])
125
+ }
126
+
127
+ if commit_data[:id] == after
128
+ @result[:head_commit] = commit_data
129
+ end
130
+
131
+ commits << commit_data
132
+ end
133
+
134
+ def commits
135
+ @result[:commits]
136
+ end
137
+
138
+ def head_commit
139
+ @result[:head_commit]
140
+ end
141
+ end
@@ -0,0 +1,47 @@
1
+ module IntegrationSetup
2
+ extend RSpec::SharedContext
3
+
4
+ let(:base_config) do
5
+ {
6
+ 'github' => {
7
+ 'repos' => {
8
+ 'txgh-bot/txgh-test-resources' => {
9
+ 'api_username' => 'txgh-bot',
10
+ # github will auto-revoke a token if they notice it in one of your commits ;)
11
+ 'api_token' => Base64.decode64('YjViYWY3Nzk5NTdkMzVlMmI0OGZmYjk4YThlY2M1ZDY0NzAwNWRhZA=='),
12
+ 'push_source_to' => 'test-project-88',
13
+ 'branch' => 'master'
14
+ }
15
+ }
16
+ },
17
+ 'transifex' => {
18
+ 'projects' => {
19
+ 'test-project-88' => {
20
+ 'tx_config' => 'file://./config/tx.config',
21
+ 'api_username' => 'txgh.bot',
22
+ 'api_password' => '2aqFGW99fPRKWvXBPjbrxkdiR',
23
+ 'push_translations_to' => 'txgh-bot/txgh-test-resources'
24
+ }
25
+ }
26
+ }
27
+ }
28
+ end
29
+
30
+ before(:all) do
31
+ VCR.configure do |config|
32
+ config.filter_sensitive_data('<GITHUB_TOKEN>') do
33
+ base_config['github']['repos']['txgh-bot/txgh-test-resources']['api_token']
34
+ end
35
+
36
+ config.filter_sensitive_data('<TRANSIFEX_PASSWORD>') do
37
+ base_config['transifex']['projects']['test-project-88']['api_password']
38
+ end
39
+ end
40
+ end
41
+
42
+ before(:each) do
43
+ allow(Txgh::Config::KeyManager).to(
44
+ receive(:base_config).and_return(base_config)
45
+ )
46
+ end
47
+ end