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.
- checksums.yaml +7 -0
- data/LICENSE +202 -0
- data/README.md +64 -0
- data/lib/ext/zipline/output_stream.rb +62 -0
- data/lib/txgh.rb +53 -0
- data/lib/txgh/app.rb +135 -0
- data/lib/txgh/category_support.rb +31 -0
- data/lib/txgh/config.rb +11 -0
- data/lib/txgh/config/config_pair.rb +36 -0
- data/lib/txgh/config/key_manager.rb +54 -0
- data/lib/txgh/config/provider_instance.rb +20 -0
- data/lib/txgh/config/provider_support.rb +26 -0
- data/lib/txgh/config/providers.rb +9 -0
- data/lib/txgh/config/providers/file_provider.rb +19 -0
- data/lib/txgh/config/providers/git_provider.rb +58 -0
- data/lib/txgh/config/providers/raw_provider.rb +19 -0
- data/lib/txgh/config/tx_config.rb +77 -0
- data/lib/txgh/config/tx_manager.rb +15 -0
- data/lib/txgh/diff_calculator.rb +90 -0
- data/lib/txgh/empty_resource_contents.rb +43 -0
- data/lib/txgh/errors.rb +9 -0
- data/lib/txgh/github_api.rb +83 -0
- data/lib/txgh/github_repo.rb +88 -0
- data/lib/txgh/github_request_auth.rb +28 -0
- data/lib/txgh/handlers.rb +12 -0
- data/lib/txgh/handlers/download_handler.rb +84 -0
- data/lib/txgh/handlers/github.rb +10 -0
- data/lib/txgh/handlers/github/delete_handler.rb +65 -0
- data/lib/txgh/handlers/github/handler.rb +20 -0
- data/lib/txgh/handlers/github/push_handler.rb +108 -0
- data/lib/txgh/handlers/github/request_handler.rb +106 -0
- data/lib/txgh/handlers/response.rb +17 -0
- data/lib/txgh/handlers/stream_response.rb +39 -0
- data/lib/txgh/handlers/tgz_stream_response.rb +41 -0
- data/lib/txgh/handlers/transifex.rb +8 -0
- data/lib/txgh/handlers/transifex/hook_handler.rb +77 -0
- data/lib/txgh/handlers/transifex/request_handler.rb +78 -0
- data/lib/txgh/handlers/triggers.rb +9 -0
- data/lib/txgh/handlers/triggers/handler.rb +66 -0
- data/lib/txgh/handlers/triggers/pull_handler.rb +29 -0
- data/lib/txgh/handlers/triggers/push_handler.rb +21 -0
- data/lib/txgh/handlers/zip_stream_response.rb +21 -0
- data/lib/txgh/merge_calculator.rb +74 -0
- data/lib/txgh/parse_config.rb +24 -0
- data/lib/txgh/resource_committer.rb +39 -0
- data/lib/txgh/resource_contents.rb +118 -0
- data/lib/txgh/resource_downloader.rb +141 -0
- data/lib/txgh/resource_updater.rb +104 -0
- data/lib/txgh/response_helpers.rb +30 -0
- data/lib/txgh/transifex_api.rb +165 -0
- data/lib/txgh/transifex_project.rb +37 -0
- data/lib/txgh/transifex_request_auth.rb +53 -0
- data/lib/txgh/tx_branch_resource.rb +59 -0
- data/lib/txgh/tx_logger.rb +12 -0
- data/lib/txgh/tx_resource.rb +66 -0
- data/lib/txgh/utils.rb +44 -0
- data/lib/txgh/version.rb +3 -0
- data/spec/app_spec.rb +346 -0
- data/spec/category_support_spec.rb +43 -0
- data/spec/config/config_pair_spec.rb +47 -0
- data/spec/config/key_manager_spec.rb +48 -0
- data/spec/config/provider_instance_spec.rb +30 -0
- data/spec/config/provider_support_spec.rb +55 -0
- data/spec/config/tx_config_spec.rb +49 -0
- data/spec/config/tx_manager_spec.rb +57 -0
- data/spec/diff_calculator_spec.rb +90 -0
- data/spec/github_api_spec.rb +148 -0
- data/spec/github_repo_spec.rb +178 -0
- data/spec/github_request_auth_spec.rb +39 -0
- data/spec/handlers/download_handler_spec.rb +81 -0
- data/spec/handlers/github/delete_handler_spec.rb +71 -0
- data/spec/handlers/github/push_handler_spec.rb +76 -0
- data/spec/handlers/tgz_stream_response_spec.rb +59 -0
- data/spec/handlers/transifex/hook_handler_spec.rb +115 -0
- data/spec/handlers/zip_stream_response_spec.rb +58 -0
- data/spec/helpers/github_payload_builder.rb +141 -0
- data/spec/helpers/integration_setup.rb +47 -0
- data/spec/helpers/nil_logger.rb +10 -0
- data/spec/helpers/standard_txgh_setup.rb +92 -0
- data/spec/helpers/test_provider.rb +12 -0
- data/spec/integration/cassettes/github_l10n_hook_endpoint.yml +536 -0
- data/spec/integration/cassettes/pull.yml +47 -0
- data/spec/integration/cassettes/push.yml +544 -0
- data/spec/integration/cassettes/transifex_hook_endpoint.yml +560 -0
- data/spec/integration/config/tx.config +10 -0
- data/spec/integration/hooks_spec.rb +158 -0
- data/spec/integration/payloads/github_postbody.json +161 -0
- data/spec/integration/payloads/github_postbody_l10n.json +136 -0
- data/spec/integration/payloads/github_postbody_release.json +136 -0
- data/spec/integration/triggers_spec.rb +45 -0
- data/spec/merge_calculator_spec.rb +112 -0
- data/spec/parse_config_spec.rb +52 -0
- data/spec/resource_committer_spec.rb +42 -0
- data/spec/resource_contents_spec.rb +212 -0
- data/spec/resource_downloader_spec.rb +205 -0
- data/spec/resource_updater_spec.rb +147 -0
- data/spec/spec_helper.rb +32 -0
- data/spec/transifex_api_spec.rb +345 -0
- data/spec/transifex_project_spec.rb +45 -0
- data/spec/transifex_request_auth_spec.rb +39 -0
- data/spec/tx_branch_resource_spec.rb +99 -0
- data/spec/tx_resource_spec.rb +47 -0
- data/spec/utils_spec.rb +58 -0
- data/txgh.gemspec +29 -0
- 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
|
data/spec/spec_helper.rb
ADDED
|
@@ -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
|