foreman_docker 3.0.0 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +22 -22
  3. data/app/assets/javascripts/foreman_docker/image_step.js +36 -6
  4. data/app/controllers/api/v2/containers_controller.rb +13 -11
  5. data/app/controllers/containers/steps_controller.rb +8 -2
  6. data/app/controllers/containers_controller.rb +4 -3
  7. data/app/controllers/image_search_controller.rb +36 -79
  8. data/app/helpers/container_steps_helper.rb +21 -0
  9. data/app/models/concerns/foreman_docker/parameter_validators.rb +27 -4
  10. data/app/models/container.rb +9 -10
  11. data/app/models/docker_container_wizard_state.rb +2 -0
  12. data/app/models/docker_container_wizard_states/dns.rb +2 -8
  13. data/app/models/docker_container_wizard_states/environment.rb +4 -14
  14. data/app/models/docker_container_wizard_states/environment_variable.rb +2 -2
  15. data/app/models/docker_container_wizard_states/exposed_port.rb +2 -8
  16. data/app/models/docker_container_wizard_states/image.rb +30 -0
  17. data/app/models/docker_container_wizard_states/preliminary.rb +1 -1
  18. data/app/models/docker_parameter.rb +20 -0
  19. data/app/models/docker_registry.rb +6 -4
  20. data/app/models/environment_variable.rb +3 -3
  21. data/app/models/exposed_port.rb +4 -10
  22. data/app/models/foreman_docker/compute_resource_extensions.rb +11 -0
  23. data/app/models/foreman_docker/dns.rb +3 -9
  24. data/app/models/foreman_docker/docker.rb +1 -5
  25. data/app/models/service/containers.rb +15 -11
  26. data/app/models/service/registry_api.rb +87 -15
  27. data/app/services/foreman_docker/image_search.rb +92 -0
  28. data/app/views/containers/index.html.erb +1 -3
  29. data/app/views/containers/show.html.erb +1 -1
  30. data/app/views/containers/steps/_image_hub_tab.html.erb +39 -29
  31. data/app/views/containers/steps/_title.html.erb +1 -1
  32. data/app/views/containers/steps/preliminary.html.erb +1 -1
  33. data/app/views/foreman_docker/common_parameters/_dns_entry.html.erb +1 -1
  34. data/app/views/foreman_docker/common_parameters/_environment_variable.html.erb +1 -1
  35. data/app/views/foreman_docker/common_parameters/_exposed_port.html.erb +1 -1
  36. data/app/views/image_search/_repository_search_results.html.erb +1 -1
  37. data/app/views/registries/index.html.erb +1 -3
  38. data/app/views/registries/new.html.erb +1 -1
  39. data/db/migrate/20160605133025_create_docker_parameters.rb +13 -0
  40. data/db/migrate/20160605134652_move_parameters_to_docker_parameters.rb +27 -0
  41. data/lib/foreman_docker/engine.rb +6 -7
  42. data/lib/foreman_docker/version.rb +1 -1
  43. data/lib/tasks/test.rake +35 -0
  44. data/test/functionals/api/v2/containers_controller_test.rb +21 -0
  45. data/test/functionals/api/v2/registries_controller_test.rb +4 -3
  46. data/test/functionals/containers_controller_test.rb +5 -0
  47. data/test/functionals/containers_steps_controller_test.rb +74 -36
  48. data/test/functionals/image_search_controller_test.rb +166 -12
  49. data/test/integration/container_test.rb +1 -1
  50. data/test/test_plugin_helper.rb +10 -0
  51. data/test/units/container_test.rb +1 -1
  52. data/test/units/containers_service_test.rb +31 -9
  53. data/test/units/docker_container_wizard_states/image_test.rb +84 -0
  54. data/test/units/docker_registry_test.rb +27 -10
  55. data/test/units/foreman_docker/compute_resource_extensions_test.rb +10 -0
  56. data/test/units/image_search_service_test.rb +188 -0
  57. data/test/units/registry_api_test.rb +206 -12
  58. metadata +55 -36
  59. data/lib/foreman_docker/tasks/test.rake +0 -45
  60. data/locale/de/foreman_docker.edit.po +0 -631
  61. data/locale/de/foreman_docker.po.time_stamp +0 -0
  62. data/locale/es/foreman_docker.edit.po +0 -631
  63. data/locale/es/foreman_docker.po.time_stamp +0 -0
  64. data/locale/fr/foreman_docker.edit.po +0 -632
  65. data/locale/fr/foreman_docker.po.time_stamp +0 -0
  66. data/locale/it/foreman_docker.edit.po +0 -630
  67. data/locale/it/foreman_docker.po.time_stamp +0 -0
  68. data/locale/ja/foreman_docker.edit.po +0 -634
  69. data/locale/ja/foreman_docker.po.time_stamp +0 -0
  70. data/locale/ko/foreman_docker.edit.po +0 -629
  71. data/locale/ko/foreman_docker.po.time_stamp +0 -0
  72. data/locale/pt_BR/foreman_docker.edit.po +0 -631
  73. data/locale/pt_BR/foreman_docker.po.time_stamp +0 -0
  74. data/locale/ru/foreman_docker.edit.po +0 -630
  75. data/locale/ru/foreman_docker.po.time_stamp +0 -0
  76. data/locale/zh_CN/foreman_docker.edit.po +0 -628
  77. data/locale/zh_CN/foreman_docker.po.time_stamp +0 -0
  78. data/locale/zh_TW/foreman_docker.edit.po +0 -628
  79. data/locale/zh_TW/foreman_docker.po.time_stamp +0 -0
@@ -0,0 +1,84 @@
1
+ require 'test_plugin_helper'
2
+
3
+ module DockerContainerWizardStates
4
+ class ImageTest < ActiveSupport::TestCase
5
+ let(:image) { 'centos' }
6
+ let(:tags) { ['latest', '5', '4.3'] }
7
+ let(:docker_hub) { Service::RegistryApi.new(url: 'https://nothub.com') }
8
+ let(:registry) { FactoryGirl.create(:docker_registry) }
9
+ let(:compute_resource) { FactoryGirl.create(:docker_cr) }
10
+ let(:image_search_service) { ForemanDocker::ImageSearch.new }
11
+ let(:wizard_state) do
12
+ DockerContainerWizardState.create
13
+ end
14
+ let(:preliminary) do
15
+ DockerContainerWizardStates::Preliminary.create(
16
+ compute_resource: compute_resource,
17
+ wizard_state: wizard_state
18
+ )
19
+ end
20
+
21
+ subject do
22
+ Image.new(
23
+ repository_name: image,
24
+ tag: tags.first,
25
+ wizard_state: wizard_state
26
+ )
27
+ end
28
+
29
+ setup do
30
+ ForemanDocker::ImageSearch.any_instance.unstub(:available?)
31
+ wizard_state.preliminary = preliminary
32
+ Service::RegistryApi.stubs(:docker_hub).returns(docker_hub)
33
+ end
34
+
35
+
36
+ describe 'it validates that the image is available' do
37
+ test 'validates via the image_search_service' do
38
+ DockerContainerWizardStates::Image.any_instance
39
+ .stubs(:image_search_service)
40
+ .returns(image_search_service)
41
+ available = [true, false].sample
42
+ image_search_service.expects(:available?)
43
+ .with("#{image}:#{tags.first}").at_least_once
44
+ .returns(available)
45
+ assert_equal available, subject.valid?
46
+ end
47
+
48
+ context 'when no registy is set' do
49
+ test 'it queries the compute_resource and docker_hub' do
50
+ compute_resource.expects(:image).with(image).at_least_once
51
+ .returns(image)
52
+ compute_resource.expects(:tags_for_local_image).at_least_once
53
+ .with(image, tags.first)
54
+ .returns([])
55
+ docker_hub.expects(:tags).at_least_once
56
+ .returns([])
57
+
58
+ subject.validate
59
+ end
60
+ end
61
+
62
+ context 'when a registy is set' do
63
+ setup do
64
+ subject.stubs(:registry_id).returns(registry.id)
65
+ DockerRegistry.expects(:find).with(registry.id)
66
+ .returns(registry)
67
+ end
68
+
69
+ test 'it queries the compute_resource and registry' do
70
+ compute_resource.expects(:image).with(image).at_least_once
71
+ .returns(image)
72
+ compute_resource.expects(:tags_for_local_image).at_least_once
73
+ .with(image, tags.first)
74
+ .returns([])
75
+ docker_hub.expects(:tags).never
76
+ registry.api.expects(:tags).with(image, tags.first).at_least_once
77
+ .returns([])
78
+
79
+ subject.validate
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -1,6 +1,8 @@
1
1
  require 'test_plugin_helper'
2
2
 
3
3
  class DockerRegistryTest < ActiveSupport::TestCase
4
+ subject { FactoryGirl.create(:docker_registry) }
5
+
4
6
  test 'used_location_ids should return correct location ids' do
5
7
  location = FactoryGirl.build(:location)
6
8
  r = as_admin do
@@ -29,21 +31,36 @@ class DockerRegistryTest < ActiveSupport::TestCase
29
31
  should validate_uniqueness_of(:name)
30
32
  should validate_uniqueness_of(:url)
31
33
 
32
- context 'attempt to login' do
34
+ describe 'registry validation' do
33
35
  setup do
34
- @registry = FactoryGirl.build(:docker_registry)
35
- @registry.unstub(:attempt_login)
36
+ subject.unstub(:attempt_login)
37
+ end
38
+
39
+ test 'is valid when the api is ok' do
40
+ subject.api.expects(:ok?).returns(true)
41
+ assert subject.valid?
36
42
  end
37
43
 
38
- test 'before creating a registry' do
39
- RestClient::Resource.any_instance.expects(:get)
40
- assert @registry.valid?
44
+ test 'is not valid when api is not ok' do
45
+ subject.api.expects(:ok?)
46
+ .raises(Docker::Error::AuthenticationError)
47
+ refute subject.valid?
41
48
  end
49
+ end
50
+
51
+ describe '#api' do
52
+ let(:api) { subject.api }
53
+
54
+ test 'returns a RegistryApi instance' do
55
+ assert_kind_of Service::RegistryApi, api
56
+ end
57
+ end
58
+
59
+ describe '#api' do
60
+ let(:api) { subject.api }
42
61
 
43
- test 'display errors in case authentication failed' do
44
- RestClient::Resource.any_instance.expects(:get).
45
- raises(Docker::Error::AuthenticationError)
46
- refute @registry.valid?
62
+ test 'returns a RegistryApi instance' do
63
+ assert_kind_of Service::RegistryApi, api
47
64
  end
48
65
  end
49
66
  end
@@ -0,0 +1,10 @@
1
+ require 'test_plugin_helper'
2
+
3
+ module ForemanDocker
4
+ class ComputeResourceExtensionsTest < ActiveSupport::TestCase
5
+ test 'ComputeResource::providers_requiring_url returns expected providers' do
6
+ expected_providers = "Docker, Libvirt, oVirt, OpenStack and Rackspace"
7
+ assert_equal expected_providers, ComputeResource.providers_requiring_url
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,188 @@
1
+ require 'test_plugin_helper'
2
+
3
+ class ImageSearchServiceTest < ActiveSupport::TestCase
4
+ let(:compute_resource) { FactoryGirl.create(:docker_cr) }
5
+ let(:registry) { FactoryGirl.create(:docker_registry).api }
6
+ let(:term) { 'centos' }
7
+ let(:query) { { term: term, tags: 'false' } }
8
+
9
+ subject { ForemanDocker::ImageSearch.new(compute_resource, registry) }
10
+
11
+ setup do
12
+ stub_registry_api
13
+ end
14
+
15
+ describe '#add_source' do
16
+ setup do
17
+ subject.instance_variable_set(:@sources, {})
18
+ end
19
+
20
+ test 'adds a compute resource to @sources[:compute_resource]' do
21
+ subject.add_source(compute_resource)
22
+ assert_equal compute_resource,
23
+ subject.instance_variable_get(:@sources)[:compute_resource].first
24
+ end
25
+
26
+ test 'adds a registry to @sources[:registry]' do
27
+ subject.add_source(registry)
28
+ assert_equal registry,
29
+ subject.instance_variable_get(:@sources)[:registry].first
30
+ end
31
+ end
32
+
33
+ describe '#remove_source' do
34
+ test 'removes a registry source from @sources' do
35
+ refute subject.instance_variable_get(:@sources)[:registry].empty?
36
+ subject.remove_source(registry)
37
+ assert subject.instance_variable_get(:@sources)[:registry].empty?
38
+ end
39
+
40
+ test 'removes a compute_resource source from @sources' do
41
+ refute subject.instance_variable_get(:@sources)[:compute_resource].empty?
42
+ subject.remove_source(compute_resource)
43
+ assert subject.instance_variable_get(:@sources)[:compute_resource].empty?
44
+ end
45
+ end
46
+
47
+ describe '#search' do
48
+ test 'returns {"name" => value } pairs' do
49
+ return_result = Hash.new
50
+ return_result.stubs(:info).returns({ 'RepoTags' => ["#{term}:latest"]})
51
+ compute_resource.stubs(:local_images).with(term)
52
+ .returns([return_result])
53
+ result = subject.search(query)
54
+ assert_equal({"name" => term}, result.first)
55
+ end
56
+
57
+ context 'tags is false' do
58
+ test 'calls #images with term as query' do
59
+ subject.expects(:images).with(term)
60
+ .returns([])
61
+ subject.search(query)
62
+ end
63
+ end
64
+
65
+ context 'tags is "true"' do
66
+ setup do
67
+ query[:tags] = 'true'
68
+ end
69
+
70
+ test 'calls #tags with term as query' do
71
+ subject.expects(:tags).with(term)
72
+ .returns([])
73
+ subject.search(query)
74
+ end
75
+ end
76
+ end
77
+
78
+ describe '#images' do
79
+ context 'a compute_resource set' do
80
+ test 'calls #search_compute_resource with term as query' do
81
+ subject.expects(:compute_resource_search).with(compute_resource, term)
82
+ .returns([])
83
+ subject.images(term)
84
+ end
85
+ end
86
+
87
+ context 'no compute_resource is set' do
88
+ setup do
89
+ subject.remove_source(compute_resource)
90
+ end
91
+
92
+ test 'does not call #search_compute_resource' do
93
+ subject.expects(:compute_resource_search).with(compute_resource, term)
94
+ .never
95
+ subject.images(term)
96
+ end
97
+ end
98
+
99
+ context 'a registry is set' do
100
+ test 'calls #search_registry' do
101
+ subject.expects(:registry_search).with(registry, term)
102
+ .returns([])
103
+ subject.images(term)
104
+ end
105
+ end
106
+
107
+ context 'no registry is set' do
108
+ setup do
109
+ subject.remove_source(registry)
110
+ end
111
+
112
+ test 'does not call #search_registry' do
113
+ subject.expects(:registry_search).with(registry, term)
114
+ .never
115
+ subject.images(term)
116
+ end
117
+ end
118
+ end
119
+
120
+ describe '#tags' do
121
+ let(:tag) { 'latest' }
122
+ let(:query) { "#{term}:#{tag}" }
123
+
124
+ context 'a compute_resource set' do
125
+ test 'calls #compute_resource with image name and tag' do
126
+ subject.expects(:compute_resource_tags).with(compute_resource, term, tag)
127
+ .returns([])
128
+ subject.tags(query)
129
+ end
130
+ end
131
+
132
+ context 'no compute_resource is set' do
133
+ setup do
134
+ subject.remove_source(compute_resource)
135
+ end
136
+
137
+ test 'does not call #search_compute_resource' do
138
+ subject.expects(:compute_resource_tags).with(compute_resource, term, tag)
139
+ .never
140
+ subject.tags(query)
141
+ end
142
+ end
143
+
144
+ context 'a registry is set' do
145
+ setup do
146
+ subject.remove_source(compute_resource)
147
+ end
148
+
149
+ test 'calls #registry_tags with image name and tag' do
150
+ subject.expects(:registry_tags).with(registry, term, tag)
151
+ .returns([])
152
+ subject.tags(query)
153
+ end
154
+ end
155
+
156
+ context 'no registry is set' do
157
+ setup do
158
+ subject.remove_source(registry)
159
+ end
160
+
161
+ test 'does not call #registry_tags' do
162
+ subject.expects(:registry_search).with(registry, term, tag)
163
+ .never
164
+ subject.images(query)
165
+ end
166
+ end
167
+ end
168
+
169
+ describe '#available?' do
170
+ test 'calls #tags with query' do
171
+ subject.expects(:tags).with(query).once
172
+ .returns([])
173
+ subject.available?(query)
174
+ end
175
+
176
+ test 'returns true if any matching image and tag is found' do
177
+ subject.stubs(:tags).with(query)
178
+ .returns([{ 'name' => "#{term}:latest" }])
179
+ subject.available?(query)
180
+ end
181
+
182
+ test 'returns false if none are found' do
183
+ subject.stubs(:tags).with(query)
184
+ .returns([])
185
+ subject.available?(query)
186
+ end
187
+ end
188
+ end
@@ -1,17 +1,211 @@
1
1
  require 'test_plugin_helper'
2
2
 
3
3
  class RegistryApiTest < ActiveSupport::TestCase
4
- test "initialize handles username password info correctly" do
5
- uname = "tardis"
6
- password = "boo"
7
- url = "http://docker-who.gov"
8
- reg = Service::RegistryApi.new(:url => url,
9
- :user => uname,
10
- :password => password)
11
- assert reg.config[:url].include?(uname)
12
- assert reg.config[:url].include?(password)
13
-
14
- reg = Service::RegistryApi.new(:url => url)
15
- assert_equal url, reg.config[:url]
4
+ let(:url) { 'http://dockerregistry.com:5000' }
5
+ subject { Service::RegistryApi.new(url: url) }
6
+
7
+ describe '#connection' do
8
+ test 'returns a Docker::Connection' do
9
+ assert_equal Docker::Connection, subject.connection.class
10
+ end
11
+
12
+ test 'the connection has the same url' do
13
+ assert_equal url, subject.connection.url
14
+ end
15
+
16
+ context 'authentication is set' do
17
+ let(:user) { 'username' }
18
+ let(:password) { 'secretpassword' }
19
+
20
+ subject do
21
+ Service::RegistryApi.new(
22
+ :url => url,
23
+ :password => password,
24
+ :user => user
25
+ )
26
+ end
27
+
28
+ test 'it sets the same user and password' do
29
+ assert_equal user, subject.connection.options[:user]
30
+ assert_equal password, subject.connection.options[:password]
31
+ end
32
+ end
33
+ end
34
+
35
+ describe '#get' do
36
+ let(:path) { '/v1/search' }
37
+ let(:json) { '{}' }
38
+
39
+ test 'calls get on #connection' do
40
+ subject.connection
41
+ .expects(:get).at_least_once
42
+ .returns(json)
43
+
44
+ subject.get(path)
45
+ end
46
+
47
+ test 'returns a parsed json' do
48
+ subject.connection.stubs(:get).returns(json)
49
+ assert_equal JSON.parse(json), subject.get(path)
50
+ end
51
+
52
+ # Docker::Connection is used and meant for the Docker,
53
+ # not the Registry API therefore it is required
54
+ # to override the path via options
55
+ test 'sets the path as an option not param for Docker::Connection#get' do
56
+ subject.connection.stubs(:get) do |path_param, _, options|
57
+ refute_equal path, path_param
58
+ assert_equal path, options[:path]
59
+ end.returns(json)
60
+
61
+ subject.get(path)
62
+ end
63
+
64
+ # Docker Hub will return a 503 when a 'Host' header includes a port.
65
+ # Omitting default ports (an Excon option) solves the issue
66
+ test 'sets omit_default_port to true' do
67
+ subject.connection.stubs(:get) do |_, _, options|
68
+ assert options[:omit_default_port]
69
+ end.returns(json)
70
+
71
+ subject.get(path)
72
+ end
73
+
74
+ test 'returns the response raw body if it is not JSON' do
75
+ response = 'This is not JSON'
76
+ subject.connection.stubs(:get)
77
+ .returns(response)
78
+ assert_equal response, subject.get('/v1/')
79
+ end
80
+ end
81
+
82
+ describe '#search' do
83
+ let(:path) { '/v1/search' }
84
+ let(:query) { 'centos' }
85
+
86
+ test "calls #get with path and query" do
87
+ subject.expects(:get).with(path, q: query) do |path_param, params|
88
+ assert_equal path, path_param
89
+ assert_equal query, params[:q]
90
+ end.returns({})
91
+
92
+ subject.search(query)
93
+ end
94
+
95
+ test "falls back to #catalog if #get fails" do
96
+ subject.expects(:catalog).with(query)
97
+
98
+ subject.expects(:get).with(path, q: query)
99
+ .raises('Error')
100
+
101
+ subject.search(query)
102
+ end
103
+ end
104
+
105
+ describe '#catalog' do
106
+ let(:path) { '/v2/_catalog' }
107
+ let(:query) { 'centos' }
108
+ let(:catalog) { { 'repositories' => ['centos', 'fedora'] } }
109
+
110
+ setup do
111
+ subject.stubs(:get).returns(catalog)
112
+ end
113
+
114
+ test "calls #get with path" do
115
+ subject.expects(:get).with(path)
116
+ .returns(catalog)
117
+
118
+ subject.catalog(query)
119
+ end
120
+
121
+ test 'returns {"name" => value} pairs' do
122
+ result = subject.catalog(query)
123
+ assert_equal({ "name" => query }, result.first)
124
+ end
125
+
126
+ test 'only give back matching results' do
127
+ result = subject.catalog('fedora')
128
+ assert_match(/^fedora/, result.first['name'])
129
+ end
130
+ end
131
+
132
+ describe '#tags' do
133
+ let(:query) { 'alpine' }
134
+ let(:path) { "/v1/repositories/#{query}/tags" }
135
+
136
+ test "calls #get with path" do
137
+ subject.expects(:get).with(path)
138
+ subject.tags(query)
139
+ end
140
+
141
+ test "falls back to #tags_v2 if #get fails" do
142
+ subject.expects(:get).with(path)
143
+ .raises('Error')
144
+
145
+ subject.expects(:tags_v2).with(query)
146
+ subject.tags(query)
147
+ end
148
+
149
+ # https://registry.access.redhat.com returns a hash not an array
150
+ test 'handles a hash response correctly' do
151
+ tags_hash = {
152
+ "7.0-21" => "e1f5733f050b2488a17b7630cb038bfbea8b7bdfa9bdfb99e63a33117e28d02f",
153
+ "7.0-23" => "bef54b8f8a2fdd221734f1da404d4c0a7d07ee9169b1443a338ab54236c8c91a",
154
+ "7.0-27" => "8e6704f39a3d4a0c82ec7262ad683a9d1d9a281e3c1ebbb64c045b9af39b3940"
155
+ }
156
+ subject.expects(:get).with(path)
157
+ .returns(tags_hash)
158
+ assert_equal '7.0-21', subject.tags(query).first['name']
159
+ end
160
+ end
161
+
162
+ describe '#tags for API v2' do
163
+ let(:query) { 'debian' }
164
+ let(:v1_path) { "/v1/repositories/#{query}/tags" }
165
+ let(:path) { "/v2/#{query}/tags/list" }
166
+ let(:tags) { { 'tags' => ['jessy', 'woody'] } }
167
+
168
+ setup do
169
+ subject.stubs(:get).with(v1_path)
170
+ .raises('404 Not found')
171
+ end
172
+
173
+ test 'calls #get with path' do
174
+ subject.expects(:get).with(path)
175
+ .returns(tags)
176
+ subject.tags(query)
177
+ end
178
+
179
+ test 'returns {"name" => value } pairs ' do
180
+ subject.stubs(:get).with(path).returns(tags)
181
+ result = subject.tags(query)
182
+ assert_equal tags['tags'].first, result.first['name']
183
+ end
184
+ end
185
+
186
+ describe '#ok?' do
187
+ test 'calls the API via #get with /v1/' do
188
+ subject.connection.expects(:get)
189
+ .with('/', nil, Service::RegistryApi::DEFAULTS[:connection].merge(path: '/v1/'))
190
+ .returns('Docker Registry API')
191
+ assert subject.ok?
192
+ end
193
+
194
+ test 'calls #get with /v2/ if /v1/fails' do
195
+ subject.stubs(:get).with('/v1/')
196
+ .raises('404 page not found')
197
+ subject.expects(:get).with('/v2/')
198
+ .returns({})
199
+ assert subject.ok?
200
+ end
201
+ end
202
+
203
+ describe '.docker_hub' do
204
+ subject { Service::RegistryApi }
205
+
206
+ test 'returns an instance for Docker Hub' do
207
+ result = subject.docker_hub
208
+ assert_equal Service::RegistryApi::DOCKER_HUB, result.url
209
+ end
16
210
  end
17
211
  end