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,43 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
include Txgh
|
|
4
|
+
|
|
5
|
+
describe CategorySupport do
|
|
6
|
+
describe '.deserialize_categories' do
|
|
7
|
+
it 'converts an array of categories into a hash' do
|
|
8
|
+
categories = %w(captain:janeway commander:chakotay)
|
|
9
|
+
result = CategorySupport.deserialize_categories(categories)
|
|
10
|
+
expect(result).to eq('captain' => 'janeway', 'commander' => 'chakotay')
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it 'converts an array of space-separated categories' do
|
|
14
|
+
categories = ['captain:janeway commander:chakotay']
|
|
15
|
+
result = CategorySupport.deserialize_categories(categories)
|
|
16
|
+
expect(result).to eq('captain' => 'janeway', 'commander' => 'chakotay')
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
describe '.serialize_categories' do
|
|
21
|
+
it 'converts a hash of categories into an array' do
|
|
22
|
+
categories = { 'captain' => 'janeway', 'commander' => 'chakotay' }
|
|
23
|
+
result = CategorySupport.serialize_categories(categories)
|
|
24
|
+
expect(result.sort).to eq(['captain:janeway', 'commander:chakotay'])
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
describe '.escape_category' do
|
|
29
|
+
it 'replaces spaces in category values' do
|
|
30
|
+
expect(CategorySupport.escape_category('Katherine Janeway')).to(
|
|
31
|
+
eq('Katherine_Janeway')
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
describe '.join_categories' do
|
|
37
|
+
it 'joins an array of categories by spaces' do
|
|
38
|
+
expect(CategorySupport.join_categories(%w(foo:bar baz:boo))).to(
|
|
39
|
+
eq('foo:bar baz:boo')
|
|
40
|
+
)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'helpers/standard_txgh_setup'
|
|
3
|
+
|
|
4
|
+
include Txgh::Config
|
|
5
|
+
|
|
6
|
+
describe ConfigPair do
|
|
7
|
+
include StandardTxghSetup
|
|
8
|
+
|
|
9
|
+
let(:config) do
|
|
10
|
+
ConfigPair.new(project_config, repo_config)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
describe '#github_repo' do
|
|
14
|
+
it 'instantiates a github repo with the right config' do
|
|
15
|
+
repo = config.github_repo
|
|
16
|
+
expect(repo).to be_a(GithubRepo)
|
|
17
|
+
expect(repo.name).to eq(repo_name)
|
|
18
|
+
expect(repo.branch).to eq(branch)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
describe '#transifex_project' do
|
|
23
|
+
it 'instantiates a transifex project with the right config' do
|
|
24
|
+
project = config.transifex_project
|
|
25
|
+
expect(project).to be_a(TransifexProject)
|
|
26
|
+
expect(project.name).to eq(project_name)
|
|
27
|
+
expect(tx_config.resources.first.resource_slug).to eq(resource_slug)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
describe '#transifex_api' do
|
|
32
|
+
it 'instantiates an API instance' do
|
|
33
|
+
api = config.transifex_api
|
|
34
|
+
expect(api).to be_a(TransifexApi)
|
|
35
|
+
expect(api.connection.headers).to include('Authorization')
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
describe '#github_api' do
|
|
40
|
+
it 'instantiates an API instance' do
|
|
41
|
+
api = config.github_api
|
|
42
|
+
expect(api).to be_a(GithubApi)
|
|
43
|
+
expect(api.client.login).to eq(repo_config['api_username'])
|
|
44
|
+
expect(api.client.access_token).to eq(repo_config['api_token'])
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'helpers/standard_txgh_setup'
|
|
3
|
+
|
|
4
|
+
include Txgh
|
|
5
|
+
include Txgh::Config
|
|
6
|
+
|
|
7
|
+
describe KeyManager do
|
|
8
|
+
include StandardTxghSetup
|
|
9
|
+
|
|
10
|
+
describe '.config_from_project' do
|
|
11
|
+
it 'creates a config object' do
|
|
12
|
+
config = KeyManager.config_from_project(project_name)
|
|
13
|
+
expect(config).to be_a(Txgh::Config)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'creates a config object that contains both project and repo configs' do
|
|
17
|
+
config = KeyManager.config_from_project(project_name)
|
|
18
|
+
expect(config.project_config).to eq(project_config)
|
|
19
|
+
expect(config.repo_config).to eq(repo_config)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
describe '.config_from_repo' do
|
|
24
|
+
it 'creates a config object' do
|
|
25
|
+
config = KeyManager.config_from_repo(repo_name)
|
|
26
|
+
expect(config).to be_a(Txgh::Config)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'creates a config object that contains both project and repo configs' do
|
|
30
|
+
config = KeyManager.config_from_repo(repo_name)
|
|
31
|
+
expect(config.project_config).to eq(project_config)
|
|
32
|
+
expect(config.repo_config).to eq(repo_config)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
describe '.config_from' do
|
|
37
|
+
it 'creates a config object' do
|
|
38
|
+
config = KeyManager.config_from(project_name, repo_name)
|
|
39
|
+
expect(config).to be_a(Txgh::Config)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it 'creates a config object that contains both project and repo configs' do
|
|
43
|
+
config = KeyManager.config_from(project_name, repo_name)
|
|
44
|
+
expect(config.project_config).to eq(project_config)
|
|
45
|
+
expect(config.repo_config).to eq(repo_config)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'helpers/test_provider'
|
|
3
|
+
|
|
4
|
+
include Txgh
|
|
5
|
+
include Txgh::Config
|
|
6
|
+
|
|
7
|
+
describe ProviderInstance do
|
|
8
|
+
let(:provider) { TestProvider }
|
|
9
|
+
let(:payload) { :fake_payload }
|
|
10
|
+
let(:parser) { :fake_parser }
|
|
11
|
+
let(:options) { :fake_options }
|
|
12
|
+
let(:instance) { ProviderInstance.new(provider, parser) }
|
|
13
|
+
|
|
14
|
+
describe '#supports?' do
|
|
15
|
+
it 'returns true if the scheme matches' do
|
|
16
|
+
expect(instance.supports?('test')).to eq(true)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "returns false if the scheme doesn't match" do
|
|
20
|
+
expect(instance.supports?('foo')).to eq(false)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
describe '#load' do
|
|
25
|
+
it "calls the provider's load method passing the parser and options" do
|
|
26
|
+
expect(provider).to receive(:load).with(payload, parser, options)
|
|
27
|
+
instance.load(payload, options)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'helpers/test_provider'
|
|
3
|
+
|
|
4
|
+
include Txgh
|
|
5
|
+
include Txgh::Config
|
|
6
|
+
|
|
7
|
+
describe ProviderSupport do
|
|
8
|
+
let(:klass) do
|
|
9
|
+
Class.new { extend ProviderSupport }
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
let(:provider) { TestProvider }
|
|
13
|
+
|
|
14
|
+
describe '#register_provider' do
|
|
15
|
+
it 'adds an instance to the provider list' do
|
|
16
|
+
expect { klass.register_provider(:fake_provider, :fake_parser) }.to(
|
|
17
|
+
change { klass.providers.size }.by(1)
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
instance = klass.providers.first
|
|
21
|
+
expect(instance.provider).to eq(:fake_provider)
|
|
22
|
+
expect(instance.parser).to eq(:fake_parser)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
describe '#provider_for' do
|
|
27
|
+
it 'returns nil if no provider can be found' do
|
|
28
|
+
expect(klass.provider_for('foo')).to be_nil
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
describe '#split_uri' do
|
|
33
|
+
it 'separates the scheme and payload' do
|
|
34
|
+
expect(klass.split_uri('foo://bar')).to eq(%w(foo bar))
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it 'determines that the scheme is nil if not given' do
|
|
38
|
+
expect(klass.split_uri('bar')).to eq([nil, 'bar'])
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
context 'with a registered provider' do
|
|
43
|
+
before(:each) do
|
|
44
|
+
klass.register_provider(provider, :fake_parser)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
describe '#provider_for' do
|
|
48
|
+
it 'finds the first provider that matches the given scheme' do
|
|
49
|
+
instance = klass.provider_for('test')
|
|
50
|
+
expect(instance.provider).to eq(provider)
|
|
51
|
+
expect(instance.parser).to eq(:fake_parser)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'helpers/standard_txgh_setup'
|
|
3
|
+
|
|
4
|
+
include Txgh::Config
|
|
5
|
+
|
|
6
|
+
describe TxConfig do
|
|
7
|
+
include StandardTxghSetup
|
|
8
|
+
|
|
9
|
+
describe '.load' do
|
|
10
|
+
it 'parses the config correctly' do
|
|
11
|
+
config_str = """
|
|
12
|
+
[main]
|
|
13
|
+
host = https://www.transifex.com
|
|
14
|
+
lang_map = pt-BR:pt, ko-KR:ko
|
|
15
|
+
|
|
16
|
+
[my_proj.my_resource]
|
|
17
|
+
file_filter = translations/<lang>/sample.po
|
|
18
|
+
source_file = sample.po
|
|
19
|
+
source_lang = en
|
|
20
|
+
type = PO
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
config = TxConfig.load(config_str)
|
|
24
|
+
expect(config.lang_map).to eq('pt-BR' => 'pt', 'ko-KR' => 'ko')
|
|
25
|
+
expect(config.resources.size).to eq(1)
|
|
26
|
+
|
|
27
|
+
resource = config.resources.first
|
|
28
|
+
expect(resource.project_slug).to eq('my_proj')
|
|
29
|
+
expect(resource.resource_slug).to eq('my_resource')
|
|
30
|
+
expect(resource.source_file).to eq('sample.po')
|
|
31
|
+
expect(resource.source_lang).to eq('en')
|
|
32
|
+
expect(resource.translation_file).to eq('translations/<lang>/sample.po')
|
|
33
|
+
expect(resource.type).to eq('PO')
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
describe '#resource' do
|
|
38
|
+
it 'finds the resource by slug' do
|
|
39
|
+
resource = tx_config.resource(resource_slug)
|
|
40
|
+
expect(resource).to be_a(TxResource)
|
|
41
|
+
expect(resource.resource_slug).to eq(resource_slug)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'returns nil if there is no resource with the given slug' do
|
|
45
|
+
resource = tx_config.resource('foobarbaz')
|
|
46
|
+
expect(resource).to be_nil
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'helpers/standard_txgh_setup'
|
|
3
|
+
|
|
4
|
+
include Txgh
|
|
5
|
+
include Txgh::Config
|
|
6
|
+
|
|
7
|
+
describe TxManager do
|
|
8
|
+
include StandardTxghSetup
|
|
9
|
+
|
|
10
|
+
describe '.tx_config' do
|
|
11
|
+
let(:config) { KeyManager.config_from(project_name, repo_name) }
|
|
12
|
+
let(:project) { config.transifex_project }
|
|
13
|
+
let(:repo) { config.github_repo }
|
|
14
|
+
|
|
15
|
+
it 'loads tx config from the given file' do
|
|
16
|
+
path = 'file://path/to/tx_config'
|
|
17
|
+
project_config.merge!('tx_config' => path)
|
|
18
|
+
expect(TxConfig).to receive(:load_file).with('path/to/tx_config').and_return(:tx_config)
|
|
19
|
+
config = TxManager.tx_config(project, repo)
|
|
20
|
+
expect(config).to eq(:tx_config)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
context 'with git-based config' do
|
|
24
|
+
before(:each) do
|
|
25
|
+
project_config.merge!('tx_config' => 'git://./tx.config')
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it 'raises an error if asked to load config from a git repository and no ref is given' do
|
|
29
|
+
expect { TxManager.tx_config(project, repo) }.to raise_error(TxghError)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "raises an error if the git repo doesn't contain the requested config file" do
|
|
33
|
+
expect(repo.api).to receive(:download).and_raise(Octokit::NotFound)
|
|
34
|
+
expect { TxManager.tx_config(project, repo, 'my_branch') }.to(
|
|
35
|
+
raise_error(ConfigNotFoundError)
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it 'loads tx config from a git repository' do
|
|
40
|
+
expect(repo.api).to(
|
|
41
|
+
receive(:download)
|
|
42
|
+
.with(repo.name, './tx.config', 'my_branch')
|
|
43
|
+
.and_return("[main]\nlang_map = ko:ko_KR")
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
config = TxManager.tx_config(project, repo, 'my_branch')
|
|
47
|
+
expect(config.lang_map).to eq({ 'ko' => 'ko_KR' })
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it 'loads raw tx config' do
|
|
52
|
+
project_config.merge!('tx_config' => "raw://[main]\nlang_map = ko:ko_KR")
|
|
53
|
+
config = TxManager.tx_config(project, repo)
|
|
54
|
+
expect(config.lang_map).to eq({ 'ko' => 'ko_KR' })
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
include Txgh
|
|
4
|
+
|
|
5
|
+
describe DiffCalculator do
|
|
6
|
+
def phrase(key, string)
|
|
7
|
+
{ 'key' => key, 'string' => string }
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
describe '.compare' do
|
|
11
|
+
let(:diff) do
|
|
12
|
+
DiffCalculator.compare(head_phrases, diff_point_phrases)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
context 'with phrases added to HEAD' do
|
|
16
|
+
let(:head_phrases) do
|
|
17
|
+
diff_point_phrases + [
|
|
18
|
+
phrase('TheNextGeneration', 'Jean Luc Picard')
|
|
19
|
+
]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
let(:diff_point_phrases) do
|
|
23
|
+
[
|
|
24
|
+
phrase('Voyager', 'Kathryn Janeway'),
|
|
25
|
+
phrase('DeepSpaceNine', 'Benjamin Sisko'),
|
|
26
|
+
]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'includes the new string' do
|
|
30
|
+
expect(diff[:added].size).to eq(1)
|
|
31
|
+
expect(diff[:modified].size).to eq(0)
|
|
32
|
+
phrase = diff[:added].first
|
|
33
|
+
expect(phrase['key']).to eq('TheNextGeneration')
|
|
34
|
+
expect(phrase['string']).to eq('Jean Luc Picard')
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
context 'with phrases removed from HEAD' do
|
|
39
|
+
let(:head_phrases) do
|
|
40
|
+
[]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
let(:diff_point_phrases) do
|
|
44
|
+
[phrase('Voyager', 'Kathryn Janeway')]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it 'does not include the new string if string has been removed' do
|
|
48
|
+
expect(diff[:added].size).to eq(0)
|
|
49
|
+
expect(diff[:modified].size).to eq(0)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
context 'with phrases modified in HEAD' do
|
|
54
|
+
let(:head_phrases) do
|
|
55
|
+
[phrase('TheNextGeneration', 'Jean Luc Picard (rocks)')]
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
let(:diff_point_phrases) do
|
|
59
|
+
[phrase('TheNextGeneration', 'Jean Luc Picard')]
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it 'includes the modified string' do
|
|
63
|
+
expect(diff[:added].size).to eq(0)
|
|
64
|
+
expect(diff[:modified].size).to eq(1)
|
|
65
|
+
phrase = diff[:modified].first
|
|
66
|
+
expect(phrase['key']).to eq('TheNextGeneration')
|
|
67
|
+
expect(phrase['string']).to eq('Jean Luc Picard (rocks)')
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
context 'with no phrases modified, added, or removed' do
|
|
72
|
+
let(:head_phrases) do
|
|
73
|
+
[
|
|
74
|
+
phrase('TheNextGeneration', 'Jean Luc Picard'),
|
|
75
|
+
phrase('Voyager', 'Kathryn Janeway'),
|
|
76
|
+
phrase('DeepSpaceNine', 'Benjamin Sisko')
|
|
77
|
+
]
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
let(:diff_point_phrases) do
|
|
81
|
+
head_phrases
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it 'does not include any phrases' do
|
|
85
|
+
expect(diff[:added].size).to eq(0)
|
|
86
|
+
expect(diff[:modified].size).to eq(0)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
include Txgh
|
|
4
|
+
|
|
5
|
+
describe GithubApi do
|
|
6
|
+
let(:client) { double(:client) }
|
|
7
|
+
let(:api) { GithubApi.create_from_client(client) }
|
|
8
|
+
let(:repo) { 'my_org/my_repo' }
|
|
9
|
+
let(:branch) { 'master' }
|
|
10
|
+
let(:sha) { 'abc123' }
|
|
11
|
+
|
|
12
|
+
describe '#tree' do
|
|
13
|
+
it 'retrieves a git tree using the client' do
|
|
14
|
+
expect(client).to receive(:tree).with(repo, sha, recursive: 1)
|
|
15
|
+
api.tree(repo, sha)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
describe '#blob' do
|
|
20
|
+
it 'retrieves a git blob using the client' do
|
|
21
|
+
expect(client).to receive(:blob).with(repo, sha)
|
|
22
|
+
api.blob(repo, sha)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
describe '#create_ref' do
|
|
27
|
+
it 'creates the given ref using the client' do
|
|
28
|
+
expect(client).to receive(:create_ref).with(repo, branch, sha)
|
|
29
|
+
api.create_ref(repo, branch, sha)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it 'returns false on client error' do
|
|
33
|
+
expect(client).to receive(:create_ref).and_raise(StandardError)
|
|
34
|
+
expect(api.create_ref(repo, branch, sha)).to eq(false)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
describe '#commit' do
|
|
39
|
+
let(:path) { 'path/to/translations' }
|
|
40
|
+
let(:other_path) { 'other/path/to/translations' }
|
|
41
|
+
|
|
42
|
+
before(:each) do
|
|
43
|
+
allow(client).to receive(:create_blob).with(repo, :new_content).and_return(:blob_sha)
|
|
44
|
+
allow(client).to receive(:ref).with(repo, branch).and_return(object: { sha: :branch_sha })
|
|
45
|
+
allow(client).to receive(:commit).with(repo, :branch_sha).and_return(commit: { tree: { sha: :base_tree_sha } })
|
|
46
|
+
allow(client).to receive(:create_tree).and_return(sha: :new_tree_sha)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it 'creates a new commit and updates the branch' do
|
|
50
|
+
expect(client).to(
|
|
51
|
+
receive(:create_commit)
|
|
52
|
+
.with(repo, "Updating translations for #{path}", :new_tree_sha, :branch_sha)
|
|
53
|
+
.and_return(sha: :new_commit_sha)
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
expect(client).to receive(:update_ref).with(repo, branch, :new_commit_sha, false)
|
|
57
|
+
api.commit(repo, branch, { path => :new_content }, true)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it 'updates multiple files at a time' do
|
|
61
|
+
allow(client).to receive(:create_blob).with(repo, :other_content).and_return(:blob_sha_2)
|
|
62
|
+
|
|
63
|
+
expect(client).to(
|
|
64
|
+
receive(:create_commit)
|
|
65
|
+
.with(repo, "Updating translations for #{path}, #{other_path}", :new_tree_sha, :branch_sha)
|
|
66
|
+
.and_return(sha: :new_commit_sha)
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
expect(client).to receive(:update_ref).with(repo, branch, :new_commit_sha, false)
|
|
70
|
+
content_map = { path => :new_content, other_path => :other_content }
|
|
71
|
+
api.commit(repo, branch, content_map, true)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
context 'with an empty commit' do
|
|
75
|
+
before(:each) do
|
|
76
|
+
allow(client).to(
|
|
77
|
+
receive(:compare)
|
|
78
|
+
.with(repo, :branch_sha, :new_commit_sha)
|
|
79
|
+
.and_return(files: [])
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
expect(client).to(
|
|
83
|
+
receive(:create_commit)
|
|
84
|
+
.with(repo, "Updating translations for #{path}", :new_tree_sha, :branch_sha)
|
|
85
|
+
.and_return(sha: :new_commit_sha)
|
|
86
|
+
)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it 'does not allow empty commits by default' do
|
|
90
|
+
expect(client).to_not receive(:update_ref)
|
|
91
|
+
api.commit(repo, branch, { path => :new_content })
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
context 'with a non-empty commit' do
|
|
96
|
+
before(:each) do
|
|
97
|
+
allow(client).to(
|
|
98
|
+
receive(:compare)
|
|
99
|
+
.with(repo, :branch_sha, :new_commit_sha)
|
|
100
|
+
.and_return(files: %w(abc def))
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
expect(client).to(
|
|
104
|
+
receive(:create_commit)
|
|
105
|
+
.with(repo, "Updating translations for #{path}", :new_tree_sha, :branch_sha)
|
|
106
|
+
.and_return(sha: :new_commit_sha)
|
|
107
|
+
)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it 'updates the ref as expected' do
|
|
111
|
+
expect(client).to receive(:update_ref).with(repo, branch, :new_commit_sha, false)
|
|
112
|
+
api.commit(repo, branch, { path => :new_content })
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
describe '#get_commit' do
|
|
118
|
+
it 'retrieves the given commit using the client' do
|
|
119
|
+
expect(client).to receive(:commit).with(repo, sha)
|
|
120
|
+
api.get_commit(repo, sha)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
describe '#get_ref' do
|
|
125
|
+
it 'retrieves the given ref (i.e. branch) using the client' do
|
|
126
|
+
expect(client).to receive(:ref).with(repo, sha)
|
|
127
|
+
api.get_ref(repo, sha)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
describe '#download' do
|
|
132
|
+
it 'downloads the file from the given branch' do
|
|
133
|
+
path = 'path/to/file.xyz'
|
|
134
|
+
|
|
135
|
+
expect(client).to receive(:ref).with(repo, branch).and_return(object: { sha: :branch_sha })
|
|
136
|
+
expect(client).to receive(:commit).with(repo, :branch_sha).and_return(commit: { tree: { sha: :base_tree_sha } })
|
|
137
|
+
expect(client).to receive(:tree).with(repo, :base_tree_sha, recursive: 1).and_return(
|
|
138
|
+
tree: [{ path: path, sha: :blob_sha }]
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
expect(client).to receive(:blob).with(repo, :blob_sha).and_return(
|
|
142
|
+
{ 'content' => :blob, 'encoding' => 'utf-8' }
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
expect(api.download(repo, path, branch)).to eq(:blob)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|