lolsoap 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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