stockboy 0.5.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.
Files changed (112) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +5 -0
  4. data/.yardopts +7 -0
  5. data/CHANGELOG.md +24 -0
  6. data/Gemfile +12 -0
  7. data/Guardfile +10 -0
  8. data/LICENSE +21 -0
  9. data/README.md +293 -0
  10. data/Rakefile +30 -0
  11. data/lib/stockboy.rb +80 -0
  12. data/lib/stockboy/attribute.rb +11 -0
  13. data/lib/stockboy/attribute_map.rb +74 -0
  14. data/lib/stockboy/candidate_record.rb +130 -0
  15. data/lib/stockboy/configuration.rb +62 -0
  16. data/lib/stockboy/configurator.rb +176 -0
  17. data/lib/stockboy/dsl.rb +68 -0
  18. data/lib/stockboy/exceptions.rb +3 -0
  19. data/lib/stockboy/filter.rb +58 -0
  20. data/lib/stockboy/filter_chain.rb +41 -0
  21. data/lib/stockboy/filters.rb +11 -0
  22. data/lib/stockboy/filters/missing_email.rb +37 -0
  23. data/lib/stockboy/job.rb +241 -0
  24. data/lib/stockboy/mapped_record.rb +59 -0
  25. data/lib/stockboy/provider.rb +238 -0
  26. data/lib/stockboy/providers.rb +11 -0
  27. data/lib/stockboy/providers/file.rb +135 -0
  28. data/lib/stockboy/providers/ftp.rb +205 -0
  29. data/lib/stockboy/providers/http.rb +123 -0
  30. data/lib/stockboy/providers/imap.rb +290 -0
  31. data/lib/stockboy/providers/soap.rb +120 -0
  32. data/lib/stockboy/railtie.rb +28 -0
  33. data/lib/stockboy/reader.rb +59 -0
  34. data/lib/stockboy/readers.rb +11 -0
  35. data/lib/stockboy/readers/csv.rb +115 -0
  36. data/lib/stockboy/readers/fixed_width.rb +121 -0
  37. data/lib/stockboy/readers/spreadsheet.rb +144 -0
  38. data/lib/stockboy/readers/xml.rb +155 -0
  39. data/lib/stockboy/registry.rb +42 -0
  40. data/lib/stockboy/source_record.rb +43 -0
  41. data/lib/stockboy/string_pool.rb +35 -0
  42. data/lib/stockboy/template_file.rb +44 -0
  43. data/lib/stockboy/translations.rb +70 -0
  44. data/lib/stockboy/translations/boolean.rb +58 -0
  45. data/lib/stockboy/translations/date.rb +41 -0
  46. data/lib/stockboy/translations/decimal.rb +33 -0
  47. data/lib/stockboy/translations/default_empty_string.rb +38 -0
  48. data/lib/stockboy/translations/default_false.rb +41 -0
  49. data/lib/stockboy/translations/default_nil.rb +38 -0
  50. data/lib/stockboy/translations/default_true.rb +41 -0
  51. data/lib/stockboy/translations/default_zero.rb +41 -0
  52. data/lib/stockboy/translations/integer.rb +33 -0
  53. data/lib/stockboy/translations/string.rb +33 -0
  54. data/lib/stockboy/translations/time.rb +41 -0
  55. data/lib/stockboy/translations/uk_date.rb +51 -0
  56. data/lib/stockboy/translations/us_date.rb +51 -0
  57. data/lib/stockboy/translator.rb +66 -0
  58. data/lib/stockboy/version.rb +3 -0
  59. data/spec/fixtures/.gitkeep +0 -0
  60. data/spec/fixtures/files/a_garbage.csv +1 -0
  61. data/spec/fixtures/files/test_data-20120101.csv +1 -0
  62. data/spec/fixtures/files/test_data-20120202.csv +1 -0
  63. data/spec/fixtures/files/z_garbage.csv +1 -0
  64. data/spec/fixtures/jobs/test_job.rb +1 -0
  65. data/spec/fixtures/soap/get_list/fault.xml +8 -0
  66. data/spec/fixtures/soap/get_list/success.xml +18 -0
  67. data/spec/fixtures/spreadsheets/test_data.xls +0 -0
  68. data/spec/fixtures/spreadsheets/test_row_options.xls +0 -0
  69. data/spec/fixtures/xml/body.xml +14 -0
  70. data/spec/spec_helper.rb +28 -0
  71. data/spec/stockboy/attribute_map_spec.rb +59 -0
  72. data/spec/stockboy/attribute_spec.rb +11 -0
  73. data/spec/stockboy/candidate_record_spec.rb +150 -0
  74. data/spec/stockboy/configuration_spec.rb +28 -0
  75. data/spec/stockboy/configurator_spec.rb +127 -0
  76. data/spec/stockboy/filter_chain_spec.rb +40 -0
  77. data/spec/stockboy/filter_spec.rb +41 -0
  78. data/spec/stockboy/filters/missing_email_spec.rb +26 -0
  79. data/spec/stockboy/filters_spec.rb +38 -0
  80. data/spec/stockboy/job_spec.rb +238 -0
  81. data/spec/stockboy/mapped_record_spec.rb +30 -0
  82. data/spec/stockboy/provider_spec.rb +34 -0
  83. data/spec/stockboy/providers/file_spec.rb +116 -0
  84. data/spec/stockboy/providers/ftp_spec.rb +143 -0
  85. data/spec/stockboy/providers/http_spec.rb +94 -0
  86. data/spec/stockboy/providers/imap_spec.rb +76 -0
  87. data/spec/stockboy/providers/soap_spec.rb +107 -0
  88. data/spec/stockboy/providers_spec.rb +38 -0
  89. data/spec/stockboy/readers/csv_spec.rb +68 -0
  90. data/spec/stockboy/readers/fixed_width_spec.rb +52 -0
  91. data/spec/stockboy/readers/spreadsheet_spec.rb +121 -0
  92. data/spec/stockboy/readers/xml_spec.rb +94 -0
  93. data/spec/stockboy/readers_spec.rb +30 -0
  94. data/spec/stockboy/source_record_spec.rb +19 -0
  95. data/spec/stockboy/template_file_spec.rb +30 -0
  96. data/spec/stockboy/translations/boolean_spec.rb +48 -0
  97. data/spec/stockboy/translations/date_spec.rb +38 -0
  98. data/spec/stockboy/translations/decimal_spec.rb +23 -0
  99. data/spec/stockboy/translations/default_empty_string_spec.rb +32 -0
  100. data/spec/stockboy/translations/default_false_spec.rb +25 -0
  101. data/spec/stockboy/translations/default_nil_spec.rb +32 -0
  102. data/spec/stockboy/translations/default_true_spec.rb +25 -0
  103. data/spec/stockboy/translations/default_zero_spec.rb +32 -0
  104. data/spec/stockboy/translations/integer_spec.rb +22 -0
  105. data/spec/stockboy/translations/string_spec.rb +22 -0
  106. data/spec/stockboy/translations/time_spec.rb +27 -0
  107. data/spec/stockboy/translations/uk_date_spec.rb +37 -0
  108. data/spec/stockboy/translations/us_date_spec.rb +37 -0
  109. data/spec/stockboy/translations_spec.rb +55 -0
  110. data/spec/stockboy/translator_spec.rb +27 -0
  111. data/stockboy.gemspec +32 -0
  112. metadata +305 -0
@@ -0,0 +1,116 @@
1
+ require 'spec_helper'
2
+ require 'stockboy/providers/file'
3
+
4
+ module Stockboy
5
+ describe Providers::File do
6
+ subject(:provider) { Stockboy::Providers::File.new }
7
+
8
+ it "should assign parameters" do
9
+ provider.file_dir = "fixtures/files"
10
+ provider.file_name = %r{import_20[1-9][0-9]-(0[1-9]|1[0-2])-([0-2][1-9]|3[0-1]).csv}
11
+ provider.file_newer = Date.today
12
+ provider.file_smaller = 1024**2
13
+ provider.file_larger = 1024
14
+ provider.pick = :first
15
+
16
+ provider.file_dir.should == "fixtures/files"
17
+ provider.file_name.should == %r{import_20[1-9][0-9]-(0[1-9]|1[0-2])-([0-2][1-9]|3[0-1]).csv}
18
+ provider.file_newer.should == Date.today
19
+ provider.file_smaller.should == 1024**2
20
+ provider.file_larger.should == 1024
21
+ provider.pick.should == :first
22
+ end
23
+
24
+ describe ".new" do
25
+ it "has no errors" do
26
+ provider.errors.messages.should be_empty
27
+ end
28
+
29
+ it "accepts block initialization" do
30
+ provider = Providers::File.new{ |f| f.file_dir 'fixtures/files' }
31
+ provider.file_dir.should == 'fixtures/files'
32
+ end
33
+ end
34
+
35
+ describe "#matching_file" do
36
+ subject(:provider) do
37
+ Providers::File.new do |f|
38
+ f.file_dir = RSpec.configuration.fixture_path.join("files")
39
+ end
40
+ end
41
+
42
+ it "returns the full path to the matching file name" do
43
+ provider.file_name = "test_data-*"
44
+ provider.matching_file.should end_with "fixtures/files/test_data-20120202.csv"
45
+ end
46
+ end
47
+
48
+ describe "#data" do
49
+ subject(:provider) do
50
+ Providers::File.new do |f|
51
+ f.file_dir = RSpec.configuration.fixture_path.join("files")
52
+ end
53
+ end
54
+
55
+ it "fails with an error if the file doesn't exist" do
56
+ provider.file_name = "missing-file.csv"
57
+ provider.data.should be_nil
58
+ provider.valid?.should == false
59
+ provider.errors[:base].should_not be_empty
60
+ end
61
+
62
+ it "finds last matching file from string glob" do
63
+ provider.file_name = "test_data-*.csv"
64
+ provider.data.should == "2012-02-02\n"
65
+ end
66
+
67
+ it "finds first matching file from string glob" do
68
+ provider.file_name = "test_data-*.csv"
69
+ provider.pick = :first
70
+ provider.data.should == "2012-01-01\n"
71
+ end
72
+
73
+ it "finds last matching file from regex" do
74
+ provider.file_name = /test_data/
75
+ provider.data.should == "2012-02-02\n"
76
+ end
77
+
78
+ context "with :since validation" do
79
+ let(:recently) { Time.now - 60 }
80
+
81
+ it "skips old files" do
82
+ expect_any_instance_of(::File).to receive(:mtime).and_return Time.now - 86400
83
+ provider.file_dir = RSpec.configuration.fixture_path.join("files")
84
+ provider.file_name = '*.csv'
85
+ provider.since = recently
86
+
87
+ provider.data.should be_nil
88
+ provider.errors[:response].should include "No new files since #{recently}"
89
+ end
90
+ end
91
+ end
92
+
93
+ describe ".delete_data" do
94
+ let(:target) { ::Tempfile.new(['delete', '.csv']) }
95
+ let(:target_dir) { File.dirname(target) }
96
+ subject(:provider) { Providers::File.new(file_name: 'delete*.csv', file_dir: target_dir) }
97
+
98
+ after do
99
+ target.unlink
100
+ end
101
+
102
+ it "should raise an error when called blindly" do
103
+ expect_any_instance_of(::File).to_not receive(:delete)
104
+ expect { provider.delete_data }.to raise_error Stockboy::OutOfSequence
105
+ end
106
+
107
+ it "should call delete on the matched file" do
108
+ provider.matching_file
109
+
110
+ expect(::File).to receive(:delete).with(target.path)
111
+ provider.delete_data
112
+ end
113
+ end
114
+
115
+ end
116
+ end
@@ -0,0 +1,143 @@
1
+ require 'spec_helper'
2
+ require 'stockboy/providers/ftp'
3
+
4
+ module Stockboy
5
+ describe Providers::FTP do
6
+
7
+ subject(:provider) do
8
+ Stockboy::Providers::FTP.new do |ftp|
9
+ ftp.host = "localhost.test"
10
+ ftp.username = "a"
11
+ ftp.password = "b"
12
+ ftp.binary = true
13
+ ftp.passive = true
14
+ ftp.file_name = '*.csv'
15
+ end
16
+ end
17
+
18
+ it "should assign parameters" do
19
+ ftp = Stockboy::Providers::FTP.new
20
+ ftp.host = "localhost.test"
21
+ ftp.username = "uuu"
22
+ ftp.password = "ppp"
23
+ ftp.file_dir = "files/here"
24
+ ftp.file_name = %r{import_20[1-9][0-9]-(0[1-9]|1[0-2])-([0-2][1-9]|3[0-1]).csv}
25
+
26
+ ftp.host.should == "localhost.test"
27
+ ftp.username.should == "uuu"
28
+ ftp.password.should == "ppp"
29
+ ftp.file_dir.should == "files/here"
30
+ ftp.file_name.should == %r{import_20[1-9][0-9]-(0[1-9]|1[0-2])-([0-2][1-9]|3[0-1]).csv}
31
+ end
32
+
33
+ describe ".new" do
34
+ it "has no errors" do
35
+ subject.errors.messages.should be_empty
36
+ end
37
+
38
+ it "accepts block initialization" do
39
+ ftp = Providers::FTP.new{ |f| f.host 'test2.local' }
40
+ ftp.host.should == 'test2.local'
41
+ end
42
+ end
43
+
44
+ describe "#client" do
45
+ it "should open connection to host with username and password" do
46
+ expect_connection
47
+
48
+ connection = false
49
+ provider.client { |f| connection = f }
50
+
51
+ connection.should be_a Net::FTP
52
+ connection.binary.should be_true
53
+ connection.passive.should be_true
54
+ end
55
+
56
+ it "should return yielded result" do
57
+ expect_connection
58
+
59
+ result = provider.client { |_| "a_file_name.csv" }
60
+
61
+ result.should == "a_file_name.csv"
62
+ end
63
+ end
64
+
65
+ describe "#data" do
66
+ it "adds an error on missing host" do
67
+ provider.host = nil
68
+ provider.data
69
+
70
+ provider.errors.include?(:host).should be_true
71
+ end
72
+
73
+ it "adds an error on missing file_name" do
74
+ provider.file_name = nil
75
+ provider.data
76
+
77
+ provider.errors.include?(:file_name).should be_true
78
+ end
79
+
80
+ it "downloads the last matching file" do
81
+ net_ftp = expect_connection
82
+ expect(net_ftp).to receive(:nlst).and_return ['20120101.csv', '20120102.csv']
83
+ expect(net_ftp).to receive(:get).with('20120102.csv', nil).and_return "DATA"
84
+ expect(provider).to receive(:validate_file).and_return true
85
+
86
+ provider.data.should == "DATA"
87
+ end
88
+
89
+ it "skips old files" do
90
+ net_ftp = expect_connection
91
+ expect(net_ftp).to receive(:nlst).and_return ['20120101.csv', '20120102.csv']
92
+ expect(net_ftp).to receive(:mtime).with('20120102.csv').and_return(Time.new(2009,01,01))
93
+ expect(net_ftp).to_not receive(:get)
94
+
95
+ provider.file_newer = Time.new(2010,1,1)
96
+
97
+ provider.data.should be_nil
98
+ end
99
+ end
100
+
101
+ describe "#matching_file" do
102
+ it "does not change until cleared" do
103
+ net_ftp = expect_connection
104
+ expect(net_ftp).to receive(:nlst).and_return ["1.csv", "2.csv"]
105
+
106
+ provider.matching_file.should == "2.csv"
107
+
108
+ net_ftp = expect_connection
109
+ expect(net_ftp).to receive(:nlst).and_return ["1.csv", "2.csv", "3.csv"]
110
+
111
+ provider.matching_file.should == "2.csv"
112
+ provider.clear
113
+ provider.matching_file.should == "3.csv"
114
+ end
115
+ end
116
+
117
+ describe "#delete_data" do
118
+ it "should raise an error when called blindly" do
119
+ expect_any_instance_of(Net::FTP).to_not receive(:delete)
120
+ expect { provider.delete_data }.to raise_error Stockboy::OutOfSequence
121
+ end
122
+
123
+ it "should delete the matching file" do
124
+ net_ftp = expect_connection
125
+ expect(net_ftp).to receive(:nlst).and_return ["1.csv", "2.csv"]
126
+
127
+ provider.matching_file.should == "2.csv"
128
+
129
+ net_ftp = expect_connection
130
+ expect(net_ftp).to receive(:delete).with("2.csv")
131
+
132
+ provider.delete_data.should == "2.csv"
133
+ end
134
+ end
135
+
136
+ def expect_connection(host="localhost.test", user="a", pass="b")
137
+ net_ftp = Net::FTP.new
138
+ expect(Net::FTP).to receive(:open).with(host, user, pass).and_yield(net_ftp)
139
+ net_ftp
140
+ end
141
+
142
+ end
143
+ end
@@ -0,0 +1,94 @@
1
+ require 'spec_helper'
2
+ require 'stockboy/providers/http'
3
+
4
+ module Stockboy
5
+ describe Providers::HTTP do
6
+ subject(:provider) { Stockboy::Providers::HTTP.new }
7
+
8
+ it "should assign parameters from :uri option" do
9
+ provider.uri = "http://www.example.com/"
10
+ provider.query = {user: 'u'}
11
+ provider.method = :get
12
+
13
+ provider.uri.should == URI("http://www.example.com/?user=u")
14
+ provider.query.should == {user: 'u'}
15
+ provider.method.should == :get
16
+ end
17
+
18
+ it "should assign parameters from :get" do
19
+ provider.get = "http://www.example.com/"
20
+ provider.query = {user: 'u'}
21
+
22
+ provider.uri.should == URI("http://www.example.com/?user=u")
23
+ provider.query.should == {user: 'u'}
24
+ provider.method.should == :get
25
+ end
26
+
27
+ it "should assign parameters from :post" do
28
+ provider.post = "http://www.example.com/"
29
+ provider.query = {user: 'u'}
30
+
31
+ provider.uri.should == URI("http://www.example.com/?user=u")
32
+ provider.query.should == {user: 'u'}
33
+ provider.method.should == :post
34
+ end
35
+
36
+ describe ".new" do
37
+ its(:errors) { should be_empty }
38
+
39
+ it "accepts block DSL initialization" do
40
+ provider = Providers::HTTP.new do
41
+ get "http://www.example.com/"
42
+ query user: 'u'
43
+ end
44
+
45
+ provider.uri.should == URI("http://www.example.com/?user=u")
46
+ provider.query.should == { user: 'u' }
47
+ provider.method.should == :get
48
+ end
49
+ end
50
+
51
+ describe "validation" do
52
+ it "should not be valid without a method" do
53
+ provider.method = nil
54
+ provider.should_not be_valid
55
+ provider.errors.keys.should include(:method)
56
+ end
57
+
58
+ it "should not be valid without a uri" do
59
+ provider.uri = ""
60
+ provider.should_not be_valid
61
+ provider.errors.keys.should include(:uri)
62
+ end
63
+ end
64
+
65
+ describe "#data" do
66
+ let(:response) { HTTPI::Response.new(200, {}, '{"success":true}') }
67
+
68
+ subject(:provider) do
69
+ Providers::HTTP.new do |s|
70
+ s.uri = "http://www.example.com/"
71
+ s.query = {username: "user"}
72
+ s.method = :get
73
+ end
74
+ end
75
+
76
+ it "returns string body on success" do
77
+ expect(HTTPI).to receive(:request) { response }
78
+
79
+ provider.data.should == '{"success":true}'
80
+ end
81
+ end
82
+
83
+ describe "#client" do
84
+ it "yields a generic HTTP interface" do
85
+ provider.client do |http|
86
+ http.should respond_to :get
87
+ http.should respond_to :post
88
+ end
89
+ end
90
+ end
91
+
92
+ end
93
+ end
94
+
@@ -0,0 +1,76 @@
1
+ require 'spec_helper'
2
+ require 'stockboy/providers/imap'
3
+
4
+ module Stockboy
5
+ describe Providers::IMAP do
6
+ subject(:provider) { Stockboy::Providers::IMAP.new }
7
+
8
+ it "should assign parameters" do
9
+ provider.host = "mail.localhost.test"
10
+ provider.username = "uuu"
11
+ provider.password = "ppp"
12
+ provider.mailbox = "INBOX/Data"
13
+ provider.subject = %r{New Records 20[1-9][0-9]-(0[1-9]|1[0-2])-([0-2][1-9]|3[0-1])}
14
+ provider.since = Date.new(2012,12,1)
15
+ provider.attachment = %r{data-[0-9]+\.csv}
16
+
17
+ provider.host.should == "mail.localhost.test"
18
+ provider.username.should == "uuu"
19
+ provider.password.should == "ppp"
20
+ provider.mailbox.should == "INBOX/Data"
21
+ provider.subject.should == %r{New Records 20[1-9][0-9]-(0[1-9]|1[0-2])-([0-2][1-9]|3[0-1])}
22
+ provider.attachment.should == %r{data-[0-9]+\.csv}
23
+ provider.since.should == Date.new(2012,12,1)
24
+ end
25
+
26
+ describe "deprecated options", pending: "implement deprecated_alias" do
27
+ it "promotes since instead of newer_than" do
28
+ provider = Providers::IMAP.new{ |f| f.newer_than Date.new(2012,12,1) }
29
+ provider.since.should == Date.new(2012,12,1)
30
+ end
31
+ end
32
+
33
+ describe ".new" do
34
+ it "has no errors" do
35
+ provider.errors.messages.should be_empty
36
+ end
37
+
38
+ it "accepts block initialization" do
39
+ provider = Providers::IMAP.new{ |f| f.host 'mail.test2.local' }
40
+ provider.host.should == 'mail.test2.local'
41
+ end
42
+ end
43
+
44
+ describe "#data" do
45
+
46
+ context "with no messages found" do
47
+ it "should be nil" do
48
+ allow(provider).to receive(:fetch_imap_message_keys).and_return []
49
+ provider.data.should be_nil
50
+ end
51
+ end
52
+
53
+ end
54
+
55
+ describe "#delete_data" do
56
+ let(:imap) { double(:imap) }
57
+
58
+ it "should raise an error when called blindly" do
59
+ expect { provider.delete_data }.to raise_error Stockboy::OutOfSequence
60
+ end
61
+
62
+ it "should call delete on the matching message" do
63
+ allow(provider).to receive(:client).and_yield(imap)
64
+ allow(provider).to receive(:fetch_imap_message_keys) { [5] }
65
+
66
+ provider.matching_message
67
+
68
+ expect(imap).to receive(:uid_store).with(5, "+FLAGS", [:Deleted])
69
+ expect(imap).to receive(:expunge)
70
+
71
+ provider.delete_data
72
+ end
73
+ end
74
+
75
+ end
76
+ end
@@ -0,0 +1,107 @@
1
+ require 'spec_helper'
2
+ require 'stockboy/providers/soap'
3
+
4
+ module Stockboy
5
+ describe Providers::SOAP do
6
+ before(:all) { savon.mock! }
7
+ after(:all) { savon.unmock! }
8
+ subject(:provider) { Stockboy::Providers::SOAP.new }
9
+
10
+ it "should assign parameters" do
11
+ provider.wsdl = "http://api.example.com/?wsdl"
12
+ provider.request = :get_user
13
+ provider.namespace = "http://api.example.com/"
14
+ provider.message = {user: 'u', pass: 'p'}
15
+ provider.headers = {key: 'k'}
16
+
17
+ provider.wsdl.should == "http://api.example.com/?wsdl"
18
+ provider.request.should == :get_user
19
+ provider.namespace.should == "http://api.example.com/"
20
+ provider.message.should == {user: 'u', pass: 'p'}
21
+ provider.headers.should == {key: 'k'}
22
+ end
23
+
24
+ describe ".new" do
25
+ its(:errors) { should be_empty }
26
+
27
+ it "accepts block initialization" do
28
+ provider = Providers::SOAP.new do |p|
29
+ p.request = :get_user
30
+ p.endpoint = "http://api.example.com/v1"
31
+ p.namespace = "http://api.example.com/namespace"
32
+ p.wsdl = "http://api.example.com/?wsdl"
33
+ p.message = {user: 'u', pass: 'p'}
34
+ p.headers = {key: 'k'}
35
+ end
36
+
37
+ provider.request.should == :get_user
38
+ provider.endpoint.should == "http://api.example.com/v1"
39
+ provider.wsdl.should == "http://api.example.com/?wsdl"
40
+ provider.namespace.should == "http://api.example.com/namespace"
41
+ provider.message.should == {user: 'u', pass: 'p'}
42
+ provider.headers.should == {key: 'k'}
43
+ end
44
+ end
45
+
46
+ describe "validation" do
47
+ context "with a WSDL document" do
48
+ before { provider.wsdl = "http://api.example.com/?wsdl" }
49
+ it { should be_valid }
50
+ end
51
+
52
+ context "without a WSDL document" do
53
+ it "has error for blank endpoint & WSDL namespace" do
54
+ provider.valid?
55
+ provider.errors.keys.should include(:endpoint)
56
+ end
57
+ end
58
+ end
59
+
60
+ describe "#client" do
61
+ it "yields a Savon client" do
62
+ provider.endpoint = "http://api.example.com/v1"
63
+ provider.namespace = ''
64
+ provider.client do |soap|
65
+ soap.should be_a Savon::Client
66
+ end
67
+ end
68
+ end
69
+
70
+ describe "#data" do
71
+ let(:xml_success_fixture) do
72
+ File.read(RSpec.configuration.fixture_path.join "soap/get_list/success.xml")
73
+ end
74
+
75
+ let(:provider) do
76
+ Providers::SOAP.new do |s|
77
+ s.endpoint = "http://api.example.com/v1"
78
+ s.namespace = ''
79
+ s.request = :get_list
80
+ s.message = {username: "user", password: "pass"}
81
+ end
82
+ end
83
+
84
+ subject(:response) do
85
+ savon.expects(:get_list)
86
+ .with(message: {username: 'user', password: 'pass'})
87
+ .returns(xml_success_fixture)
88
+ provider.data
89
+ end
90
+
91
+ it "returns hash data on success" do
92
+ should be_a Hash
93
+ end
94
+
95
+ it "uses string keys" do
96
+ response.keys.each { |k| k.should be_a String }
97
+ end
98
+
99
+ it "shares key string objects from a common pool" do
100
+ cases = response["MultiNamespacedEntryResponse"]["history"]["case"]
101
+ text_keys = cases.map { |c| c.keys[c.keys.index("logText")] }
102
+ text_keys[0].should be text_keys[1]
103
+ end
104
+
105
+ end
106
+ end
107
+ end