lolsoap 0.1.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 (47) hide show
  1. data/.document +5 -0
  2. data/.travis.yml +7 -0
  3. data/.yardopts +1 -0
  4. data/Gemfile +10 -0
  5. data/Gemfile.lock +22 -0
  6. data/LICENSE.txt +20 -0
  7. data/README.md +124 -0
  8. data/Rakefile +29 -0
  9. data/VERSION +1 -0
  10. data/lib/lolsoap.rb +11 -0
  11. data/lib/lolsoap/builder.rb +93 -0
  12. data/lib/lolsoap/client.rb +25 -0
  13. data/lib/lolsoap/envelope.rb +94 -0
  14. data/lib/lolsoap/errors.rb +15 -0
  15. data/lib/lolsoap/fault.rb +26 -0
  16. data/lib/lolsoap/hash_builder.rb +48 -0
  17. data/lib/lolsoap/request.rb +54 -0
  18. data/lib/lolsoap/response.rb +50 -0
  19. data/lib/lolsoap/wsdl.rb +98 -0
  20. data/lib/lolsoap/wsdl/element.rb +28 -0
  21. data/lib/lolsoap/wsdl/null_element.rb +15 -0
  22. data/lib/lolsoap/wsdl/null_type.rb +19 -0
  23. data/lib/lolsoap/wsdl/operation.rb +18 -0
  24. data/lib/lolsoap/wsdl/type.rb +38 -0
  25. data/lib/lolsoap/wsdl_parser.rb +121 -0
  26. data/lolsoap.gemspec +97 -0
  27. data/test/fixtures/stock_quote.wsdl +74 -0
  28. data/test/fixtures/stock_quote_fault.xml +16 -0
  29. data/test/fixtures/stock_quote_response.xml +8 -0
  30. data/test/helper.rb +14 -0
  31. data/test/integration/test_client.rb +20 -0
  32. data/test/integration/test_envelope.rb +45 -0
  33. data/test/integration/test_request.rb +19 -0
  34. data/test/integration/test_response.rb +15 -0
  35. data/test/integration/test_wsdl.rb +28 -0
  36. data/test/unit/test_builder.rb +95 -0
  37. data/test/unit/test_client.rb +12 -0
  38. data/test/unit/test_envelope.rb +112 -0
  39. data/test/unit/test_fault.rb +33 -0
  40. data/test/unit/test_hash_builder.rb +127 -0
  41. data/test/unit/test_request.rb +48 -0
  42. data/test/unit/test_response.rb +39 -0
  43. data/test/unit/test_wsdl.rb +143 -0
  44. data/test/unit/test_wsdl_parser.rb +105 -0
  45. data/test/unit/wsdl/test_element.rb +31 -0
  46. data/test/unit/wsdl/test_type.rb +44 -0
  47. metadata +152 -0
@@ -0,0 +1,12 @@
1
+ require 'helper'
2
+ require 'lolsoap/client'
3
+
4
+ module LolSoap
5
+ describe Client do
6
+ it 'can be instantiated with an already-parsed WSDL object' do
7
+ wsdl = Object.new
8
+ client = Client.new(wsdl)
9
+ client.wsdl.must_equal wsdl
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,112 @@
1
+ require 'helper'
2
+ require 'lolsoap/envelope'
3
+
4
+ module LolSoap
5
+ describe Envelope do
6
+ let(:wsdl) { OpenStruct.new(:type_namespaces => { 'foo' => 'http://example.com/foo' }) }
7
+ let(:operation) do
8
+ OpenStruct.new(:input => OpenStruct.new(:prefix => 'foo', :name => 'WashHandsRequest'))
9
+ end
10
+
11
+ subject { Envelope.new(wsdl, operation) }
12
+
13
+ let(:doc) { subject.doc }
14
+ let(:header) { doc.at_xpath('/soap:Envelope/soap:Header', doc.namespaces) }
15
+ let(:input) { doc.at_xpath('/soap:Envelope/soap:Body/foo:WashHandsRequest', doc.namespaces) }
16
+
17
+ it 'has a skeleton SOAP envelope structure when first created' do
18
+ doc.namespaces.must_equal(
19
+ 'xmlns:soap' => Envelope::SOAP_NAMESPACE,
20
+ 'xmlns:foo' => 'http://example.com/foo'
21
+ )
22
+
23
+ header.wont_equal nil
24
+ header.children.length.must_equal 0
25
+
26
+ input.wont_equal nil
27
+ input.children.length.must_equal 0
28
+ end
29
+
30
+ describe '#body' do
31
+ it 'yields and returns a builder object for the body' do
32
+ builder = Object.new
33
+
34
+ builder_klass = MiniTest::Mock.new
35
+ builder_klass.expect(:new, builder, [input, operation.input])
36
+
37
+ block = nil
38
+ ret = subject.body(builder_klass) { |b| block = b }
39
+
40
+ ret.must_equal builder
41
+ block.must_equal builder
42
+ end
43
+
44
+ it "doesn't require a block" do
45
+ builder = Object.new
46
+
47
+ builder_klass = MiniTest::Mock.new
48
+ builder_klass.expect(:new, builder, [input, operation.input])
49
+
50
+ subject.body(builder_klass).must_equal builder
51
+ end
52
+ end
53
+
54
+ describe '#header' do
55
+ it 'yields and returns the xml builder object for the header' do
56
+ builder = Object.new
57
+
58
+ builder_klass = MiniTest::Mock.new
59
+ builder_klass.expect(:new, builder, [header])
60
+
61
+ block = nil
62
+ ret = subject.header(builder_klass) { |b| block = b }
63
+
64
+ ret.must_equal builder
65
+ block.must_equal builder
66
+ end
67
+
68
+ it "doesn't require a block" do
69
+ builder = Object.new
70
+
71
+ builder_klass = MiniTest::Mock.new
72
+ builder_klass.expect(:new, builder, [header])
73
+
74
+ subject.header(builder_klass).must_equal builder
75
+ end
76
+ end
77
+
78
+ describe '#endpoint' do
79
+ it 'delegates to wsdl' do
80
+ wsdl.endpoint = 'lol'
81
+ subject.endpoint.must_equal 'lol'
82
+ end
83
+ end
84
+
85
+ describe '#to_xml' do
86
+ it 'returns the xml of the doc' do
87
+ def subject.doc; OpenStruct.new(:to_xml => '<lol>'); end
88
+ subject.to_xml.must_equal '<lol>'
89
+ end
90
+ end
91
+
92
+ describe '#action' do
93
+ it "returns the operation's action" do
94
+ operation.action = 'lol'
95
+ subject.action.must_equal 'lol'
96
+ end
97
+ end
98
+
99
+ describe '#input_type' do
100
+ it "returns the operation's input" do
101
+ subject.input_type.must_equal operation.input
102
+ end
103
+ end
104
+
105
+ describe '#output' do
106
+ it "returns the operation's output" do
107
+ operation.output = 'lol'
108
+ subject.output_type.must_equal 'lol'
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,33 @@
1
+ require 'helper'
2
+ require 'lolsoap/envelope'
3
+ require 'lolsoap/fault'
4
+
5
+ module LolSoap
6
+ describe Fault do
7
+ let(:request) { OpenStruct.new(:soap_namespace => Envelope::SOAP_NAMESPACE) }
8
+ let(:node) do
9
+ doc = Nokogiri::XML(File.read(TEST_ROOT + '/fixtures/stock_quote_fault.xml'))
10
+ doc.at_xpath('//soap:Fault', 'soap' => Envelope::SOAP_NAMESPACE)
11
+ end
12
+
13
+ subject { Fault.new(request, node) }
14
+
15
+ describe '#code' do
16
+ it 'returns the code' do
17
+ subject.code.must_equal 'soap:Sender'
18
+ end
19
+ end
20
+
21
+ describe '#reason' do
22
+ it 'returns the reason' do
23
+ subject.reason.must_match(/^Omg.*crashed!$/)
24
+ end
25
+ end
26
+
27
+ describe '#detail' do
28
+ it 'returns the detail' do
29
+ subject.detail.must_equal '<Foo>Some detail</Foo>'
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,127 @@
1
+ require 'helper'
2
+ require 'lolsoap/wsdl'
3
+ require 'lolsoap/hash_builder'
4
+ require 'nokogiri'
5
+
6
+ module LolTypes
7
+ class Type
8
+ def element(name)
9
+ elements.fetch(name) { LolSoap::WSDL::NullElement.new }
10
+ end
11
+ end
12
+
13
+ class Person < Type
14
+ def elements
15
+ @elements ||= {
16
+ 'name' => OpenStruct.new(:type => LolTypes.name, :singular? => true),
17
+ 'age' => OpenStruct.new(:type => LolSoap::WSDL::NullType.new, :singular? => true),
18
+ 'friends' => OpenStruct.new(:type => LolTypes.person, :singular? => false)
19
+ }
20
+ end
21
+ end
22
+
23
+ class Name < Type
24
+ def elements
25
+ @elements ||= {
26
+ 'firstName' => OpenStruct.new(:type => LolSoap::WSDL::NullType.new, :singular? => true),
27
+ 'lastName' => OpenStruct.new(:type => LolSoap::WSDL::NullType.new, :singular? => true)
28
+ }
29
+ end
30
+ end
31
+
32
+ def self.person; @person ||= Person.new; end
33
+ def self.name; @name ||= Name.new; end
34
+ end
35
+
36
+ module LolSoap
37
+ describe HashBuilder do
38
+ it 'converts an XML node to a hash using the type' do
39
+ xml = Nokogiri::XML::Builder.new do
40
+ person do
41
+ name do
42
+ firstName 'Jon'
43
+ lastName 'Leighton'
44
+ end
45
+ age '22'
46
+ end
47
+ end
48
+ node = xml.doc.root
49
+
50
+ builder = HashBuilder.new(node, LolTypes.person)
51
+ builder.output.must_equal({
52
+ 'name' => {
53
+ 'firstName' => 'Jon',
54
+ 'lastName' => 'Leighton'
55
+ },
56
+ 'age' => '22'
57
+ })
58
+ end
59
+
60
+ it 'converts nodes that have an unknown type' do
61
+ xml = Nokogiri::XML::Builder.new { person { foo 'bar' } }
62
+ node = xml.doc.root
63
+
64
+ builder = HashBuilder.new(node, LolTypes.person)
65
+ builder.output.must_equal({ 'foo' => 'bar' })
66
+ end
67
+
68
+ it 'converts fields which can occur multiple times into arrays' do
69
+ xml = Nokogiri::XML::Builder.new do
70
+ person do
71
+ friends { age '20' }
72
+ end
73
+ end
74
+ node = xml.doc.root
75
+
76
+ builder = HashBuilder.new(node, LolTypes.person)
77
+ builder.output.must_equal({
78
+ 'friends' => [
79
+ { 'age' => '20' }
80
+ ]
81
+ })
82
+
83
+ xml = Nokogiri::XML::Builder.new do
84
+ person do
85
+ friends { age '20' }
86
+ friends { age '50' }
87
+ friends { age '30' }
88
+ end
89
+ end
90
+ node = xml.doc.root
91
+
92
+ builder = HashBuilder.new(node, LolTypes.person)
93
+ builder.output.must_equal({
94
+ 'friends' => [
95
+ { 'age' => '20' },
96
+ { 'age' => '50' },
97
+ { 'age' => '30' }
98
+ ]
99
+ })
100
+ end
101
+
102
+ it 'converts fields which occur multiple times, even if their element says they shouldnt, into arrays' do
103
+ xml = Nokogiri::XML::Builder.new do
104
+ person do
105
+ age '20'
106
+ age '30'
107
+ end
108
+ end
109
+ node = xml.doc.root
110
+
111
+ builder = HashBuilder.new(node, LolTypes.person)
112
+ builder.output.must_equal({ 'age' => ['20', '30'] })
113
+
114
+ xml = Nokogiri::XML::Builder.new do
115
+ person do
116
+ age '20'
117
+ age '30'
118
+ age '40'
119
+ end
120
+ end
121
+ node = xml.doc.root
122
+
123
+ builder = HashBuilder.new(node, LolTypes.person)
124
+ builder.output.must_equal({ 'age' => ['20', '30', '40'] })
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,48 @@
1
+ require 'helper'
2
+ require 'lolsoap/request'
3
+
4
+ module LolSoap
5
+ describe Request do
6
+ let(:envelope) { OpenStruct.new }
7
+ subject { Request.new(envelope) }
8
+
9
+ [:header, :body, :soap_namespace, :input_type, :output_type].each do |method|
10
+ describe "##{method}" do
11
+ let(:envelope) { MiniTest::Mock.new }
12
+
13
+ it 'delegates to the envelope' do
14
+ ret = Object.new
15
+ envelope.expect(method, ret)
16
+ subject.send(method).must_equal ret
17
+ end
18
+ end
19
+ end
20
+
21
+ describe '#url' do
22
+ it 'returns the envelope endpoint' do
23
+ envelope.endpoint = 'lol'
24
+ subject.url.must_equal 'lol'
25
+ end
26
+ end
27
+
28
+ describe '#headers' do
29
+ it 'returns the necessary headers' do
30
+ envelope.to_xml = '<lol>'
31
+ envelope.action = 'http://example.com/LolOutLoud'
32
+
33
+ subject.headers.must_equal({
34
+ 'Content-Type' => 'application/soap+xml;charset=UTF-8',
35
+ 'Content-Length' => '5',
36
+ 'SOAPAction' => 'http://example.com/LolOutLoud'
37
+ })
38
+ end
39
+ end
40
+
41
+ describe '#content' do
42
+ it 'returns the envelope as an xml string' do
43
+ envelope.to_xml = '<lol>'
44
+ subject.content.must_equal '<lol>'
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,39 @@
1
+ require 'helper'
2
+ require 'lolsoap/envelope'
3
+ require 'lolsoap/response'
4
+
5
+ module LolSoap
6
+ describe Response do
7
+ let(:request) { OpenStruct.new(:soap_namespace => Envelope::SOAP_NAMESPACE, :output => Object.new) }
8
+ let(:doc) { Nokogiri::XML(File.read(TEST_ROOT + '/fixtures/stock_quote_response.xml')) }
9
+
10
+ subject { Response.new(request, doc) }
11
+
12
+ describe '#body' do
13
+ it 'returns the first node under the envelope body' do
14
+ subject.body.must_equal doc.at_xpath('/soap:Envelope/soap:Body/m:GetStockPriceResponse')
15
+ end
16
+ end
17
+
18
+ describe '#body_hash' do
19
+ it 'builds a hash from the body node' do
20
+ builder = OpenStruct.new(:output => Object.new)
21
+ builder_klass = MiniTest::Mock.new
22
+ builder_klass.expect(:new, builder, [subject.body, request.output])
23
+
24
+ subject.body_hash(builder_klass).must_equal builder.output
25
+ end
26
+ end
27
+
28
+ describe '#header' do
29
+ it 'returns the header element' do
30
+ subject.header.must_equal doc.at_xpath('/soap:Envelope/soap:Header')
31
+ end
32
+ end
33
+
34
+ it 'should raise a FaultRaised error when initialized, if there is a SOAP fault' do
35
+ lambda { Response.new(request, Nokogiri::XML(File.read(TEST_ROOT + '/fixtures/stock_quote_fault.xml'))) }.
36
+ must_raise FaultRaised
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,143 @@
1
+ require 'helper'
2
+ require 'lolsoap/wsdl'
3
+
4
+ module LolSoap
5
+ describe WSDL do
6
+ describe 'with a doc that can be parsed' do
7
+ let(:namespace) { 'http://lolsoap.api/bla' }
8
+ let(:parser) { OpenStruct.new(:namespaces => { 'bla' => namespace }) }
9
+
10
+ subject { WSDL.new(parser) }
11
+
12
+ describe 'with operations' do
13
+ before do
14
+ def subject.type(n)
15
+ @types ||= { 'WashHandsRequest' => Object.new, 'WashHandsResponse' => Object.new }
16
+ @types[n]
17
+ end
18
+
19
+ parser.operations = {
20
+ 'washHands' => {
21
+ :action => 'urn:washHands',
22
+ :input => { :name => 'WashHandsRequest' },
23
+ :output => { :name => 'WashHandsResponse' }
24
+ }
25
+ }
26
+ end
27
+
28
+ describe '#operations' do
29
+ it 'returns a hash of operations' do
30
+ subject.operations.length.must_equal 1
31
+ subject.operations['washHands'].tap do |op|
32
+ op.wsdl.must_equal subject
33
+ op.action.must_equal "urn:washHands"
34
+ op.input.must_equal subject.types['WashHandsRequest']
35
+ op.output.must_equal subject.types['WashHandsResponse']
36
+ end
37
+ end
38
+ end
39
+
40
+ describe '#operation' do
41
+ it 'returns a single operation' do
42
+ subject.operation('washHands').must_equal subject.operations['washHands']
43
+ end
44
+ end
45
+ end
46
+
47
+ describe '#endpoint' do
48
+ it 'returns the endpoint' do
49
+ parser.endpoint = 'http://lolsoap.api/v1'
50
+ subject.endpoint.must_equal 'http://lolsoap.api/v1'
51
+ end
52
+ end
53
+
54
+ describe '#namespaces' do
55
+ it 'returns a namespaces hash' do
56
+ subject.namespaces.must_equal({ 'bla' => namespace })
57
+ end
58
+ end
59
+
60
+ describe '#prefixes' do
61
+ it 'returns the prefixes-to-namespace mapping' do
62
+ subject.prefixes.must_equal({ namespace => 'bla' })
63
+ end
64
+ end
65
+
66
+ describe 'with types' do
67
+ before do
68
+ parser.types = {
69
+ 'Brush' => {
70
+ :elements => {
71
+ 'handleColor' => {
72
+ :name => 'handleColor',
73
+ :type => 'bla:Color',
74
+ :singular => true
75
+ },
76
+ 'age' => {
77
+ :name => 'age',
78
+ :type => 'xs:int',
79
+ :singular => false
80
+ }
81
+ },
82
+ :namespace => namespace
83
+ },
84
+ 'Color' => {
85
+ :elements => {
86
+ 'name' => {
87
+ :name => 'name',
88
+ :type => 'xs:string',
89
+ :singular => true
90
+ },
91
+ 'hex' => {
92
+ :name => 'hex',
93
+ :type => 'xs:string',
94
+ :singular => true
95
+ }
96
+ },
97
+ :namespace => namespace
98
+ }
99
+ }
100
+ end
101
+
102
+ describe '#types' do
103
+ it 'returns a hash of types' do
104
+ subject.types.length.must_equal 2
105
+
106
+ subject.types['Brush'].tap do |t|
107
+ t.namespace.must_equal namespace
108
+ t.elements.length.must_equal 2
109
+ t.element('handleColor').type.must_equal subject.types['Color']
110
+ t.element('handleColor').singular?.must_equal true
111
+ t.element('age').type.must_equal WSDL::NullType.new
112
+ t.element('age').singular?.must_equal false
113
+ end
114
+
115
+ subject.types['Color'].tap do |t|
116
+ t.namespace.must_equal namespace
117
+ t.elements.length.must_equal 2
118
+ t.element('name').type.must_equal WSDL::NullType.new
119
+ t.element('hex').type.must_equal WSDL::NullType.new
120
+ end
121
+ end
122
+ end
123
+
124
+ describe '#type' do
125
+ it 'returns a single type' do
126
+ subject.type('Color').must_equal subject.types['Color']
127
+ end
128
+
129
+ it 'returns a null object if a type is missing' do
130
+ subject.type('FooBar').must_equal WSDL::NullType.new
131
+ end
132
+ end
133
+
134
+ describe '#type_namespaces' do
135
+ it 'returns only the namespaces that used by types' do
136
+ parser.namespaces['foo'] = 'bar'
137
+ subject.type_namespaces.must_equal 'bla' => namespace
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end