diff options
Diffstat (limited to 'qa/spec/resource')
-rw-r--r-- | qa/spec/resource/api_fabricator_spec.rb | 161 | ||||
-rw-r--r-- | qa/spec/resource/base_spec.rb | 246 | ||||
-rw-r--r-- | qa/spec/resource/repository/push_spec.rb | 26 |
3 files changed, 433 insertions, 0 deletions
diff --git a/qa/spec/resource/api_fabricator_spec.rb b/qa/spec/resource/api_fabricator_spec.rb new file mode 100644 index 00000000000..a5ed4422f6e --- /dev/null +++ b/qa/spec/resource/api_fabricator_spec.rb @@ -0,0 +1,161 @@ +# frozen_string_literal: true + +describe QA::Resource::ApiFabricator do + let(:resource_without_api_support) do + Class.new do + def self.name + 'FooBarResource' + end + end + end + + let(:resource_with_api_support) do + Class.new do + def self.name + 'FooBarResource' + end + + def api_get_path + '/foo' + end + + def api_post_path + '/bar' + end + + def api_post_body + { name: 'John Doe' } + end + end + end + + before do + allow(subject).to receive(:current_url).and_return('') + end + + subject { resource.tap { |f| f.include(described_class) }.new } + + describe '#api_support?' do + let(:api_client) { spy('Runtime::API::Client') } + let(:api_client_instance) { double('API Client') } + + context 'when resource does not support fabrication via the API' do + let(:resource) { resource_without_api_support } + + it 'returns false' do + expect(subject).not_to be_api_support + end + end + + context 'when resource supports fabrication via the API' do + let(:resource) { resource_with_api_support } + + it 'returns false' do + expect(subject).to be_api_support + end + end + end + + describe '#fabricate_via_api!' do + let(:api_client) { spy('Runtime::API::Client') } + let(:api_client_instance) { double('API Client') } + + before do + stub_const('QA::Runtime::API::Client', api_client) + + allow(api_client).to receive(:new).and_return(api_client_instance) + allow(api_client_instance).to receive(:personal_access_token).and_return('foo') + end + + context 'when resource does not support fabrication via the API' do + let(:resource) { resource_without_api_support } + + it 'raises a NotImplementedError exception' do + expect { subject.fabricate_via_api! }.to raise_error(NotImplementedError, "Resource FooBarResource does not support fabrication via the API!") + end + end + + context 'when resource supports fabrication via the API' do + let(:resource) { resource_with_api_support } + let(:api_request) { spy('Runtime::API::Request') } + let(:resource_web_url) { 'http://example.org/api/v4/foo' } + let(:response) { { id: 1, name: 'John Doe', web_url: resource_web_url } } + let(:raw_post) { double('Raw POST response', code: 201, body: response.to_json) } + + before do + stub_const('QA::Runtime::API::Request', api_request) + + allow(api_request).to receive(:new).and_return(double(url: resource_web_url)) + end + + context 'when creating a resource' do + before do + allow(subject).to receive(:post).with(resource_web_url, subject.api_post_body).and_return(raw_post) + end + + it 'returns the resource URL' do + expect(api_request).to receive(:new).with(api_client_instance, subject.api_post_path).and_return(double(url: resource_web_url)) + expect(subject).to receive(:post).with(resource_web_url, subject.api_post_body).and_return(raw_post) + + expect(subject.fabricate_via_api!).to eq(resource_web_url) + end + + it 'populates api_resource with the resource' do + subject.fabricate_via_api! + + expect(subject.api_resource).to eq(response) + end + + context 'when the POST fails' do + let(:post_response) { { error: "Name already taken." } } + let(:raw_post) { double('Raw POST response', code: 400, body: post_response.to_json) } + + it 'raises a ResourceFabricationFailedError exception' do + expect(api_request).to receive(:new).with(api_client_instance, subject.api_post_path).and_return(double(url: resource_web_url)) + expect(subject).to receive(:post).with(resource_web_url, subject.api_post_body).and_return(raw_post) + + expect { subject.fabricate_via_api! }.to raise_error(described_class::ResourceFabricationFailedError, "Fabrication of FooBarResource using the API failed (400) with `#{raw_post}`.") + expect(subject.api_resource).to be_nil + end + end + end + + context '#transform_api_resource' do + let(:resource) do + Class.new do + def self.name + 'FooBarResource' + end + + def api_get_path + '/foo' + end + + def api_post_path + '/bar' + end + + def api_post_body + { name: 'John Doe' } + end + + def transform_api_resource(resource) + resource[:new] = 'foobar' + resource + end + end + end + + let(:response) { { existing: 'foo', web_url: resource_web_url } } + let(:transformed_resource) { { existing: 'foo', new: 'foobar', web_url: resource_web_url } } + + it 'transforms the resource' do + expect(subject).to receive(:post).with(resource_web_url, subject.api_post_body).and_return(raw_post) + expect(subject).to receive(:transform_api_resource).with(response).and_return(transformed_resource) + + subject.fabricate_via_api! + end + end + end + end +end diff --git a/qa/spec/resource/base_spec.rb b/qa/spec/resource/base_spec.rb new file mode 100644 index 00000000000..dc9e16792d3 --- /dev/null +++ b/qa/spec/resource/base_spec.rb @@ -0,0 +1,246 @@ +# frozen_string_literal: true + +describe QA::Resource::Base do + include Support::StubENV + + let(:resource) { spy('resource') } + let(:location) { 'http://location' } + + shared_context 'fabrication context' do + subject do + Class.new(described_class) do + def self.name + 'MyResource' + end + end + end + + before do + allow(subject).to receive(:current_url).and_return(location) + allow(subject).to receive(:new).and_return(resource) + end + end + + shared_examples 'fabrication method' do |fabrication_method_called, actual_fabrication_method = nil| + let(:fabrication_method_used) { actual_fabrication_method || fabrication_method_called } + + it 'yields resource before calling resource method' do + expect(resource).to receive(:something!).ordered + expect(resource).to receive(fabrication_method_used).ordered.and_return(location) + + subject.public_send(fabrication_method_called, resource: resource) do |resource| + resource.something! + end + end + + it 'does not log the resource and build method when QA_DEBUG=false' do + stub_env('QA_DEBUG', 'false') + expect(resource).to receive(fabrication_method_used).and_return(location) + + expect { subject.public_send(fabrication_method_called, 'something', resource: resource) } + .not_to output.to_stdout + end + end + + describe '.fabricate!' do + context 'when resource does not support fabrication via the API' do + before do + expect(described_class).to receive(:fabricate_via_api!).and_raise(NotImplementedError) + end + + it 'calls .fabricate_via_browser_ui!' do + expect(described_class).to receive(:fabricate_via_browser_ui!) + + described_class.fabricate! + end + end + + context 'when resource supports fabrication via the API' do + it 'calls .fabricate_via_browser_ui!' do + expect(described_class).to receive(:fabricate_via_api!) + + described_class.fabricate! + end + end + end + + describe '.fabricate_via_api!' do + include_context 'fabrication context' + + it_behaves_like 'fabrication method', :fabricate_via_api! + + it 'instantiates the resource, calls resource method returns the resource' do + expect(resource).to receive(:fabricate_via_api!).and_return(location) + + result = subject.fabricate_via_api!(resource: resource, parents: []) + + expect(result).to eq(resource) + end + + it 'logs the resource and build method when QA_DEBUG=true' do + stub_env('QA_DEBUG', 'true') + expect(resource).to receive(:fabricate_via_api!).and_return(location) + + expect { subject.fabricate_via_api!('something', resource: resource, parents: []) } + .to output(/==> Built a MyResource via api in [\d\.\-e]+ seconds+/) + .to_stdout + end + end + + describe '.fabricate_via_browser_ui!' do + include_context 'fabrication context' + + it_behaves_like 'fabrication method', :fabricate_via_browser_ui!, :fabricate! + + it 'instantiates the resource and calls resource method' do + subject.fabricate_via_browser_ui!('something', resource: resource, parents: []) + + expect(resource).to have_received(:fabricate!).with('something') + end + + it 'returns fabrication resource' do + result = subject.fabricate_via_browser_ui!('something', resource: resource, parents: []) + + expect(result).to eq(resource) + end + + it 'logs the resource and build method when QA_DEBUG=true' do + stub_env('QA_DEBUG', 'true') + + expect { subject.fabricate_via_browser_ui!('something', resource: resource, parents: []) } + .to output(/==> Built a MyResource via browser_ui in [\d\.\-e]+ seconds+/) + .to_stdout + end + end + + shared_context 'simple resource' do + subject do + Class.new(QA::Resource::Base) do + attribute :test do + 'block' + end + + attribute :no_block + + def fabricate! + 'any' + end + + def self.current_url + 'http://stub' + end + end + end + + let(:resource) { subject.new } + end + + describe '.attribute' do + include_context 'simple resource' + + it 'appends new attribute' do + expect(subject.attributes_names).to eq([:no_block, :test, :web_url]) + end + + context 'when the attribute is populated via a block' do + it 'returns value from the block' do + result = subject.fabricate!(resource: resource) + + expect(result).to be_a(described_class) + expect(result.test).to eq('block') + end + end + + context 'when the attribute is populated via the api' do + let(:api_resource) { { no_block: 'api' } } + + before do + expect(resource).to receive(:api_resource).and_return(api_resource) + end + + it 'returns value from api' do + result = subject.fabricate!(resource: resource) + + expect(result).to be_a(described_class) + expect(result.no_block).to eq('api') + end + + context 'when the attribute also has a block' do + let(:api_resource) { { test: 'api_with_block' } } + + before do + allow(QA::Runtime::Logger).to receive(:info) + end + + it 'returns value from api and emits an INFO log entry' do + result = subject.fabricate!(resource: resource) + + expect(result).to be_a(described_class) + expect(result.test).to eq('api_with_block') + expect(QA::Runtime::Logger) + .to have_received(:info).with(/api_with_block/) + end + end + end + + context 'when the attribute is populated via direct assignment' do + before do + resource.test = 'value' + end + + it 'returns value from the assignment' do + result = subject.fabricate!(resource: resource) + + expect(result).to be_a(described_class) + expect(result.test).to eq('value') + end + + context 'when the api also has such response' do + before do + allow(resource).to receive(:api_resource).and_return({ test: 'api' }) + end + + it 'returns value from the assignment' do + result = subject.fabricate!(resource: resource) + + expect(result).to be_a(described_class) + expect(result.test).to eq('value') + end + end + end + + context 'when the attribute has no value' do + it 'raises an error because no values could be found' do + result = subject.fabricate!(resource: resource) + + expect { result.no_block } + .to raise_error(described_class::NoValueError, "No value was computed for no_block of #{resource.class.name}.") + end + end + end + + describe '#web_url' do + include_context 'simple resource' + + it 'sets #web_url to #current_url after fabrication' do + subject.fabricate!(resource: resource) + + expect(resource.web_url).to eq(subject.current_url) + end + end + + describe '#visit!' do + include_context 'simple resource' + + before do + allow(resource).to receive(:visit) + end + + it 'calls #visit with the underlying #web_url' do + resource.web_url = subject.current_url + resource.visit! + + expect(resource).to have_received(:visit).with(subject.current_url) + end + end +end diff --git a/qa/spec/resource/repository/push_spec.rb b/qa/spec/resource/repository/push_spec.rb new file mode 100644 index 00000000000..bf3ebce0cfe --- /dev/null +++ b/qa/spec/resource/repository/push_spec.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +describe QA::Resource::Repository::Push do + describe '.files=' do + let(:files) do + [ + { + name: 'file.txt', + content: 'foo' + } + ] + end + + it 'raises an error if files is not an array' do + expect { subject.files = '' }.to raise_error(ArgumentError) + end + + it 'raises an error if files is an empty array' do + expect { subject.files = [] }.to raise_error(ArgumentError) + end + + it 'does not raise if files is an array' do + expect { subject.files = files }.not_to raise_error + end + end +end |