heracles-wrapper 0.0.1

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.
Files changed (34) hide show
  1. data/.gitignore +18 -0
  2. data/.rvmrc +55 -0
  3. data/.travis.yml +14 -0
  4. data/Gemfile +6 -0
  5. data/LICENSE.txt +14 -0
  6. data/README.md +74 -0
  7. data/Rakefile +11 -0
  8. data/heracles-wrapper.gemspec +34 -0
  9. data/lib/heracles-wrapper/config.rb +17 -0
  10. data/lib/heracles-wrapper/exceptions.rb +15 -0
  11. data/lib/heracles-wrapper/notification_response.rb +38 -0
  12. data/lib/heracles-wrapper/request/create_job.rb +63 -0
  13. data/lib/heracles-wrapper/request.rb +6 -0
  14. data/lib/heracles-wrapper/request_success.rb +25 -0
  15. data/lib/heracles-wrapper/test_helper.rb +67 -0
  16. data/lib/heracles-wrapper/version.rb +5 -0
  17. data/lib/heracles-wrapper.rb +38 -0
  18. data/lib/rails/generators/heracles/wrapper/install/USAGE +8 -0
  19. data/lib/rails/generators/heracles/wrapper/install/install_generator.rb +42 -0
  20. data/lib/rails/generators/heracles/wrapper/install/templates/config.rb +11 -0
  21. data/lib/rails/generators/heracles/wrapper/notification_response/USAGE +9 -0
  22. data/lib/rails/generators/heracles/wrapper/notification_response/notification_response_generator.rb +21 -0
  23. data/lib/rails/generators/heracles/wrapper/notification_response/templates/notification_response.rb.erb +17 -0
  24. data/lib/rails/generators/heracles/wrapper/notification_response/templates/notification_response_spec.rb.erb +31 -0
  25. data/spec/heracles-wrapper/config_spec.rb +24 -0
  26. data/spec/heracles-wrapper/exceptions_spec.rb +27 -0
  27. data/spec/heracles-wrapper/notification_response_spec.rb +93 -0
  28. data/spec/heracles-wrapper/request/create_job_spec.rb +163 -0
  29. data/spec/heracles-wrapper/request_success_spec.rb +49 -0
  30. data/spec/heracles-wrapper/test_helper_spec.rb +114 -0
  31. data/spec/heracles-wrapper_spec.rb +51 -0
  32. data/spec/rails/generators/heracles/wrapper/install/install_generator_spec.rb +31 -0
  33. data/spec/rails/generators/heracles/wrapper/notification_response/notification_response_generator_spec.rb +44 -0
  34. metadata +221 -0
@@ -0,0 +1,24 @@
1
+ require File.expand_path('../../../lib/heracles-wrapper/config', __FILE__)
2
+ require 'minitest/autorun'
3
+ describe 'Heracles::Wrapper::Config' do
4
+ subject {
5
+ Heracles::Wrapper::Config.new { |c|
6
+ c.api_key = expected_api_key
7
+ }
8
+ }
9
+ let(:expected_api_key) { '12345678901234567890123456789012'}
10
+ it 'should initialize with a block' do
11
+ subject.api_key.must_equal expected_api_key
12
+ end
13
+
14
+ it 'should have heracles base url' do
15
+ subject.heracles_base_url.must_equal Heracles::Wrapper::HERACLES_BASE_URL
16
+ end
17
+
18
+ it 'should allow heracles_base_url to be overwritten' do
19
+ expected_url = 'http://google.com'
20
+ subject.heracles_base_url.must_equal Heracles::Wrapper::HERACLES_BASE_URL
21
+ subject.heracles_base_url = expected_url
22
+ subject.heracles_base_url.must_equal expected_url
23
+ end
24
+ end
@@ -0,0 +1,27 @@
1
+ require File.expand_path('../../../lib/heracles-wrapper/exceptions', __FILE__)
2
+ require 'minitest/autorun'
3
+ require 'ostruct'
4
+ describe Heracles::Wrapper::RequestFailure do
5
+ describe 'well formed response' do
6
+ subject { Heracles::Wrapper::RequestFailure.new(response) }
7
+ let(:response) {
8
+ OpenStruct.new(:code => expected_code, :body => expected_body)
9
+ }
10
+ let(:expected_code) { 123 }
11
+ let(:expected_body) { 'Hello' }
12
+ it('has #code') { subject.code.must_equal expected_code }
13
+ it('has #messages') { subject.messages.must_equal expected_body }
14
+ it('has #response') { subject.response.must_equal response }
15
+ it('has #to_s') { subject.to_s.must_equal "code: #{expected_code}" }
16
+ end
17
+ describe 'poorly formed response as per a timeout' do
18
+ subject { Heracles::Wrapper::RequestFailure.new(response) }
19
+ let(:response) { Object.new }
20
+ let(:expected_code) { 500 }
21
+ let(:expected_body) { '' }
22
+ it('has #code') { subject.code.must_equal expected_code }
23
+ it('has #messages') { subject.messages.must_equal expected_body }
24
+ it('has #response') { subject.response.must_equal response }
25
+ it('has #to_s') { subject.to_s.must_equal "code: #{expected_code}" }
26
+ end
27
+ end
@@ -0,0 +1,93 @@
1
+ require File.expand_path(
2
+ '../../../lib/heracles-wrapper/notification_response', __FILE__
3
+ )
4
+ require 'minitest/autorun'
5
+
6
+ describe Heracles::Wrapper::NotificationResponse do
7
+ subject { Heracles::Wrapper::NotificationResponse.new(params) }
8
+ describe 'well formed response' do
9
+ let(:expected_job_status) { 'ok'}
10
+ let(:expected_job_id) { '1234' }
11
+ let(:params) {
12
+ {
13
+ :job_id => expected_job_id,
14
+ :job_status => expected_job_status,
15
+ :notification_payload => {
16
+ :hello => { :world => [{:foo => 1},{:foo => 2},{:bar => 3}]}
17
+ }
18
+ }
19
+ }
20
+
21
+ it 'should extract methods based on keys' do
22
+ subject.must_respond_to :fetch
23
+ subject.fetch(:hello).must_equal(
24
+ params.fetch(:notification_payload).fetch(:hello)
25
+ )
26
+ end
27
+
28
+ it 'should have #job_status' do
29
+ subject.job_status.must_equal expected_job_status.to_sym
30
+ end
31
+ it 'should have #job_id' do
32
+ subject.job_id.must_equal expected_job_id.to_i
33
+ end
34
+
35
+ it 'should have #one_time_key' do
36
+ subject.must_respond_to :one_time_notification_key
37
+ end
38
+
39
+ it 'should have #notification_payload' do
40
+ subject.must_respond_to :notification_payload
41
+ end
42
+ end
43
+
44
+ describe 'poorly formed response' do
45
+ let(:params) { {} }
46
+
47
+ it 'should raise error' do
48
+ lambda { subject }.must_raise ArgumentError
49
+ end
50
+
51
+ end
52
+ end
53
+
54
+ describe Heracles::Wrapper do
55
+
56
+ require 'delegate'
57
+ class DelegateResponse < DelegateClass(Heracles::Wrapper::NotificationResponse)
58
+ attr_reader :pid
59
+ def initialize(params)
60
+ super(Heracles::Wrapper::NotificationResponse.new(params))
61
+ @pid = fetch(:pid)
62
+ end
63
+ end
64
+
65
+ describe DelegateResponse do
66
+ subject {DelegateResponse.new(params)}
67
+ let(:params) {
68
+ {
69
+ :job_id => '1234',
70
+ :job_status => 'ok',
71
+ :notification_payload => {
72
+ :pid => 'abc'
73
+ }
74
+ }
75
+ }
76
+ describe 'well formed' do
77
+ it 'has a job_id and includes locally defined methods' do
78
+ subject.job_id.must_equal 1234
79
+ subject.job_status.must_equal :ok
80
+ subject.pid.must_equal 'abc'
81
+ end
82
+ end
83
+
84
+ describe 'poorly formed' do
85
+ let(:params) { {:notification_payload => {:pid => 'abc'}} }
86
+ it 'fails as an underlying pre-condition is not met' do
87
+ lambda {
88
+ subject
89
+ }.must_raise ArgumentError
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,163 @@
1
+ require File.expand_path(
2
+ '../../../../lib/heracles-wrapper/request/create_job', __FILE__
3
+ )
4
+ require File.expand_path('../../../../lib/heracles-wrapper/config', __FILE__)
5
+ require 'minitest/autorun'
6
+ require 'webmock/minitest'
7
+
8
+ describe 'Heracles::Wrapper::Request::CreateJob' do
9
+ require 'webmock/minitest'
10
+
11
+ before(:all) do
12
+ ::WebMock.disable_net_connect!
13
+ end
14
+ after(:all) do
15
+ ::WebMock.allow_net_connect!
16
+ end
17
+
18
+ subject { Heracles::Wrapper::Request::CreateJob.new(config, options) }
19
+ let(:config) {
20
+ Heracles::Wrapper::Config.new {|c|
21
+ c.api_key = expected_api_key
22
+ }
23
+ }
24
+ let(:expected_api_key) { '12345678901234567890123456789012' }
25
+ let(:expected_workflow_name) { 'RabbitWarren' }
26
+ let(:expected_parent_job_id) { nil }
27
+ let(:options) {
28
+ {
29
+ :workflow_name => expected_workflow_name,
30
+ :parent_job_id => expected_parent_job_id,
31
+ }
32
+ }
33
+
34
+ describe "#call" do
35
+ let(:expected_job_id) { 123 }
36
+ let(:expected_job_location) {
37
+ File.join(subject.url.to_s, expected_job_id.to_s)
38
+ }
39
+
40
+ it 'makes remote call and waits for response' do
41
+ stub_request(:post, subject.url.to_s).
42
+ to_return(
43
+ {
44
+ :body => %({"job_id" : "#{expected_job_id}"}),
45
+ :status => 201,
46
+ :headers => {
47
+ :content_type => 'application/json',
48
+ :location => expected_job_location
49
+ }
50
+ }
51
+ )
52
+ subject.call.code.must_equal 201
53
+ subject.call.job_id.must_equal expected_job_id.to_i
54
+ subject.call.location.must_equal expected_job_location
55
+ end
56
+
57
+ it 'handles timeout' do
58
+ stub_request(:post, subject.url.to_s).to_timeout
59
+ lambda {
60
+ subject.call
61
+ }.must_raise Heracles::Wrapper::RequestFailure
62
+ end
63
+
64
+ it 'handles redirection' do
65
+ stub_request(:post, subject.url.to_s).to_return(:status => 302)
66
+ lambda {
67
+ subject.call
68
+ }.must_raise Heracles::Wrapper::RequestFailure
69
+ end
70
+
71
+ it 'handles 404' do
72
+ stub_request(:post, subject.url.to_s).to_return(:status => 404)
73
+ lambda {
74
+ subject.call
75
+ }.must_raise Heracles::Wrapper::RequestFailure
76
+ end
77
+
78
+ it 'handles 401' do
79
+ stub_request(:post, subject.url.to_s).to_return(:status => 401)
80
+ lambda {
81
+ subject.call
82
+ }.must_raise Heracles::Wrapper::RequestFailure
83
+ end
84
+
85
+ it 'handles 500' do
86
+ stub_request(:post, subject.url.to_s).to_return(:status => 500)
87
+ lambda {
88
+ subject.call
89
+ }.must_raise Heracles::Wrapper::RequestFailure
90
+ end
91
+ end
92
+
93
+ it 'has a #url' do
94
+ subject.url.must_be_kind_of URI
95
+ end
96
+
97
+ it 'has #parameters' do
98
+ subject.parameters.must_be_kind_of Hash
99
+ end
100
+
101
+ it 'has #config' do
102
+ subject.must_respond_to :config
103
+ end
104
+
105
+ it 'has #parent_job_id' do
106
+ subject.must_respond_to :parent_job_id
107
+ end
108
+
109
+ it 'has #workflow_name' do
110
+ subject.must_respond_to :workflow_name
111
+ end
112
+
113
+ describe "#as_json" do
114
+ describe 'bare metal' do
115
+ it 'has #as_json' do
116
+ subject.as_json.must_equal(
117
+ {
118
+ :api_key => expected_api_key,
119
+ :workflow_name => expected_workflow_name,
120
+ :parameters => {}
121
+ }
122
+ )
123
+ end
124
+ end
125
+
126
+ describe 'with parent_job_id' do
127
+ let(:expected_parent_job_id) { '1234' }
128
+ it 'has #as_json' do
129
+ subject.as_json.must_equal(
130
+ {
131
+ :api_key => expected_api_key,
132
+ :workflow_name => expected_workflow_name,
133
+ :parent_job_id => expected_parent_job_id,
134
+ :parameters => {}
135
+ }
136
+ )
137
+ end
138
+ end
139
+
140
+ describe 'with additional parameters' do
141
+ let(:expected_callback_url) { 'my callback_url' }
142
+ let(:options) {
143
+ {
144
+ :workflow_name => expected_workflow_name,
145
+ :parent_job_id => expected_parent_job_id,
146
+ :parameters => {
147
+ :callback_url => expected_callback_url,
148
+ :system_number => '1234'
149
+ }
150
+ }
151
+ }
152
+ it 'has #as_json' do
153
+ subject.as_json.must_equal(
154
+ {
155
+ :api_key => expected_api_key,
156
+ :workflow_name => expected_workflow_name,
157
+ :parameters => options[:parameters]
158
+ }
159
+ )
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,49 @@
1
+ require File.expand_path(
2
+ '../../../lib/heracles-wrapper/request_success', __FILE__
3
+ )
4
+ require File.expand_path('../../../lib/heracles-wrapper/config', __FILE__)
5
+ require 'minitest/autorun'
6
+ require 'webmock/minitest'
7
+ require 'ostruct'
8
+
9
+ describe 'Heracles::Wrapper::RequestSuccess' do
10
+ subject { Heracles::Wrapper::RequestSuccess.new(http_response) }
11
+ let(:expected_job_id) { 1234 }
12
+ let(:expected_messages) { ['one message'] }
13
+ let(:expected_code) { 201 }
14
+ let(:expected_location) { 'http://somewhere.over/the/rainbown' }
15
+ let(:http_response) {
16
+ OpenStruct.new(
17
+ :body => %(
18
+ {
19
+ "job_id": #{expected_job_id},
20
+ "messages": #{expected_messages.inspect}
21
+ }
22
+ ),
23
+ :headers => { :location => expected_location },
24
+ :code => expected_code
25
+ ).tap { |obj|
26
+ def obj.foo_bar; 'Baz'; end
27
+ }
28
+ }
29
+
30
+ it 'has #code' do
31
+ subject.code.must_equal expected_code
32
+ end
33
+
34
+ it 'has #job_id' do
35
+ subject.job_id.must_equal expected_job_id
36
+ end
37
+
38
+ it 'has #location' do
39
+ subject.location.must_equal expected_location
40
+ end
41
+
42
+ it 'has #messages' do
43
+ subject.messages.must_equal expected_messages
44
+ end
45
+
46
+ it 'delegates everything else to the http_response' do
47
+ subject.foo_bar.must_equal 'Baz'
48
+ end
49
+ end
@@ -0,0 +1,114 @@
1
+ require File.expand_path('../../../lib/heracles-wrapper/test_helper', __FILE__)
2
+ require 'minitest/autorun'
3
+ require 'webmock/minitest'
4
+
5
+ describe Heracles::Wrapper::TestHelper do
6
+ include Heracles::Wrapper::TestHelper
7
+ require 'webmock/minitest'
8
+
9
+ before(:all) do
10
+ Heracles::Wrapper.configure { |c| c.api_key = 1234 }
11
+ ::WebMock.disable_net_connect!
12
+ end
13
+ after(:all) do
14
+ Heracles::Wrapper.clear_config!
15
+ ::WebMock.allow_net_connect!
16
+ end
17
+
18
+ let(:input_parameters) {
19
+ {
20
+ :workflow_name => 'TacoBuilder',
21
+ :parameters => {
22
+ :hello => 'world'
23
+ }
24
+ }
25
+ }
26
+
27
+ describe '#with_heracles_service_failure_stub' do
28
+ let(:expected_message) {'No Soup For You'}
29
+ it 'should require expected message' do
30
+ with_heracles_service_failure_stub(:create_job, [expected_message]) do
31
+ caller = Heracles::Wrapper.service(:create_job, input_parameters)
32
+ lambda {
33
+ value = caller.call
34
+ }.must_raise(Heracles::Wrapper::RequestFailure, /#{expected_message}/)
35
+ end
36
+ end
37
+ end
38
+
39
+ describe "#with_heracles_service_stub" do
40
+ it 'should return an object that adheres to request_success' do
41
+ with_heracles_service_stub(:create_job) do
42
+ @response = Heracles::Wrapper.service(
43
+ :create_job, input_parameters
44
+ ).call
45
+ @response.job_id.must_be_kind_of Fixnum
46
+ @response.messages.must_be_kind_of Array
47
+ @response.code.must_be_kind_of Fixnum
48
+ @response.location.must_be_kind_of String
49
+ end
50
+ end
51
+
52
+ it 'should be non destructive to the caller' do
53
+ @original_create_job_service = Heracles::Wrapper.create_job_service
54
+
55
+ with_heracles_service_stub(:create_job) do
56
+ @original_create_job_service.wont_be_same_as(
57
+ Heracles::Wrapper.create_job_service
58
+ )
59
+ end
60
+
61
+ Heracles::Wrapper.create_job_service.must_equal(
62
+ @original_create_job_service
63
+ )
64
+ end
65
+ let(:expected_job_id) { 1234 }
66
+ let(:expected_code) { 201 }
67
+ let(:expected_location) { "http://somewhere/jobs/#{expected_job_id}"}
68
+
69
+ def stub_live_http_request
70
+ stub_request(
71
+ :post,
72
+ File.join(Heracles::Wrapper.config.heracles_base_url, 'jobs')
73
+ ).
74
+ to_return(
75
+ {
76
+ :body => %({"job_id" : "#{expected_job_id}"}),
77
+ :status => expected_code,
78
+ :headers => {
79
+ :content_type => 'application/json',
80
+ :location => expected_location
81
+ }
82
+ }
83
+ )
84
+ end
85
+ let(:expected_response) {
86
+ {
87
+ :job_id => expected_job_id,
88
+ :code => expected_code,
89
+ :location => expected_location
90
+ }
91
+ }
92
+ it 'should respond like other systems' do
93
+ @stubbed_response = nil
94
+ with_heracles_service_stub(:create_job, expected_response) do
95
+ @stubbed_response = Heracles::Wrapper.service(
96
+ :create_job, input_parameters
97
+ )
98
+ end
99
+
100
+ stub_live_http_request
101
+ @full_response = Heracles::Wrapper.service(
102
+ :create_job, input_parameters
103
+ )
104
+
105
+ @stubbed_response.config.must_equal @full_response.config
106
+ @stubbed_response.workflow_name.must_equal @full_response.workflow_name
107
+ @stubbed_response.parent_job_id.must_equal @full_response.parent_job_id
108
+ @stubbed_response.parameters.must_equal @full_response.parameters
109
+
110
+ @stubbed_response.call.job_id.must_equal @full_response.call.job_id
111
+
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,51 @@
1
+ require File.expand_path('../../lib/heracles-wrapper', __FILE__)
2
+ require 'minitest/autorun'
3
+ describe 'Heracles::Wrapper' do
4
+ subject { Heracles::Wrapper }
5
+ before(:each) do
6
+ Heracles::Wrapper.clear_config!
7
+ end
8
+ before(:each) do
9
+ Heracles::Wrapper.clear_config!
10
+ end
11
+ describe 'improperly configured' do
12
+ it "should raise exception on .config if it wasn't set" do
13
+ lambda { subject.config }.must_raise(
14
+ Heracles::Wrapper::ConfigurationError,
15
+ /config not set/
16
+ )
17
+ end
18
+ it "should raise exception on .config if API Key is nil" do
19
+ subject.configure {|c| c.api_key = nil }
20
+ lambda { subject.config }.must_raise(
21
+ Heracles::Wrapper::ConfigurationError,
22
+ /api_key is invalid/
23
+ )
24
+ end
25
+ end
26
+ describe 'properly configured' do
27
+ before(:each) do
28
+ Heracles::Wrapper.configure do |c|
29
+ c.api_key = expected_api_key
30
+ end
31
+ end
32
+ let(:expected_api_key) { '12345678901234567890123456789012'}
33
+ it 'has .config' do
34
+ subject.config.must_be_kind_of Heracles::Wrapper::Config
35
+ end
36
+
37
+ let(:expected_workflow_name) { 'MyWorkflowName' }
38
+ let(:expected_request_parameters) { { :hello => 'World' } }
39
+ it 'has .build_request_for_create_job that responds to call' do
40
+ request = subject.service(
41
+ :create_job,
42
+ :workflow_name => expected_workflow_name,
43
+ :parameters => expected_request_parameters
44
+ )
45
+ request.parent_job_id.must_equal nil
46
+ request.workflow_name.must_equal expected_workflow_name
47
+ request.parameters.must_equal expected_request_parameters
48
+ request.must_respond_to :call
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,31 @@
1
+ gem 'rails'
2
+ require File.expand_path(
3
+ File.join(
4
+ '../../../../../../../',
5
+ 'lib/rails/generators/heracles/wrapper/install/install_generator'
6
+ ), __FILE__
7
+ )
8
+ class Heracles::Wrapper::InstallGeneratorTest < Rails::Generators::TestCase
9
+ tests Heracles::Wrapper::InstallGenerator
10
+ destination File.expand_path("../tmp", File.dirname(__FILE__))
11
+ setup :prepare_destination
12
+
13
+ test 'generates config when api key is provided' do
14
+ @api_key = 'generic-api-key'
15
+ run_generator(%w(-k generic-api-key))
16
+ assert_file 'config/initializers/heracles-wrapper.rb' do |config|
17
+ assert_match('Heracles::Wrapper.configure do |config|', config)
18
+ assert_match(
19
+ "config.api_key = #{@api_key.inspect}",
20
+ config
21
+ )
22
+ end
23
+
24
+ end
25
+
26
+ test 'does not generate config when api key is omitted' do
27
+ @api_key = 'generic-api-key'
28
+ run_generator
29
+ assert_no_file 'config/initializers/heracles-wrapper.rb'
30
+ end
31
+ end
@@ -0,0 +1,44 @@
1
+ gem 'rails'
2
+ TO_GEM_ROOT = "../../../../../../../"
3
+ require File.expand_path(
4
+ File.join(
5
+ TO_GEM_ROOT, 'lib/rails/generators/heracles/wrapper',
6
+ '/notification_response/notification_response_generator'
7
+ ), __FILE__
8
+ )
9
+ module Heracles::Wrapper
10
+ class NotificationResponseGeneratorTest < Rails::Generators::TestCase
11
+ tests Heracles::Wrapper::NotificationResponseGenerator
12
+ destination(
13
+ File.expand_path(File.join(TO_GEM_ROOT,"tmp", File.dirname(__FILE__)))
14
+ )
15
+ setup :prepare_destination
16
+
17
+ test 'should generate model with a name' do
18
+ run_generator(%w(MonkeyPatch))
19
+ assert_file 'app/models/monkey_patch.rb' do |file|
20
+ assert_match(
21
+ /^#{%(require 'heracles/wrapper/notification_response')}$/,
22
+ file
23
+ )
24
+ assert_match(
25
+ /^#{%(require 'delegate')}$/,
26
+ file
27
+ )
28
+ assert_match(
29
+ /^class\ MonkeyPatch\ \<\ DelegateClass\(Heracles::Wrapper::NotificationResponse\)$/,
30
+ file
31
+ )
32
+ assert_method(:initialize, file)
33
+ end
34
+ end
35
+
36
+ test 'should generate spec with a name' do
37
+ run_generator(%w(MonkeyPatch))
38
+ assert_file 'spec/models/monkey_patch_spec.rb' do |file|
39
+ assert_match(/^require 'spec_helper'/, file)
40
+ assert_match(/^describe MonkeyPatch do$/, file)
41
+ end
42
+ end
43
+ end
44
+ end