lolsoap 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.travis.yml +7 -0
- data/.yardopts +1 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +22 -0
- data/LICENSE.txt +20 -0
- data/README.md +124 -0
- data/Rakefile +29 -0
- data/VERSION +1 -0
- data/lib/lolsoap.rb +11 -0
- data/lib/lolsoap/builder.rb +93 -0
- data/lib/lolsoap/client.rb +25 -0
- data/lib/lolsoap/envelope.rb +94 -0
- data/lib/lolsoap/errors.rb +15 -0
- data/lib/lolsoap/fault.rb +26 -0
- data/lib/lolsoap/hash_builder.rb +48 -0
- data/lib/lolsoap/request.rb +54 -0
- data/lib/lolsoap/response.rb +50 -0
- data/lib/lolsoap/wsdl.rb +98 -0
- data/lib/lolsoap/wsdl/element.rb +28 -0
- data/lib/lolsoap/wsdl/null_element.rb +15 -0
- data/lib/lolsoap/wsdl/null_type.rb +19 -0
- data/lib/lolsoap/wsdl/operation.rb +18 -0
- data/lib/lolsoap/wsdl/type.rb +38 -0
- data/lib/lolsoap/wsdl_parser.rb +121 -0
- data/lolsoap.gemspec +97 -0
- data/test/fixtures/stock_quote.wsdl +74 -0
- data/test/fixtures/stock_quote_fault.xml +16 -0
- data/test/fixtures/stock_quote_response.xml +8 -0
- data/test/helper.rb +14 -0
- data/test/integration/test_client.rb +20 -0
- data/test/integration/test_envelope.rb +45 -0
- data/test/integration/test_request.rb +19 -0
- data/test/integration/test_response.rb +15 -0
- data/test/integration/test_wsdl.rb +28 -0
- data/test/unit/test_builder.rb +95 -0
- data/test/unit/test_client.rb +12 -0
- data/test/unit/test_envelope.rb +112 -0
- data/test/unit/test_fault.rb +33 -0
- data/test/unit/test_hash_builder.rb +127 -0
- data/test/unit/test_request.rb +48 -0
- data/test/unit/test_response.rb +39 -0
- data/test/unit/test_wsdl.rb +143 -0
- data/test/unit/test_wsdl_parser.rb +105 -0
- data/test/unit/wsdl/test_element.rb +31 -0
- data/test/unit/wsdl/test_type.rb +44 -0
- metadata +152 -0
@@ -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
|