savon 2.5.1 → 2.6.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.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/lib/savon/builder.rb +6 -1
- data/lib/savon/header.rb +2 -2
- data/lib/savon/options.rb +37 -11
- data/lib/savon/request.rb +7 -2
- data/lib/savon/version.rb +1 -1
- data/savon.gemspec +1 -1
- data/spec/fixtures/wsdl/no_message_tag.xml +1267 -0
- data/spec/integration/email_example_spec.rb +1 -1
- data/spec/integration/random_quote_spec.rb +2 -2
- data/spec/integration/stockquote_example_spec.rb +1 -1
- data/spec/integration/zipcode_example_spec.rb +1 -1
- data/spec/savon/builder_spec.rb +5 -0
- data/spec/savon/client_spec.rb +1 -1
- data/spec/savon/core_ext/string_spec.rb +9 -9
- data/spec/savon/http_error_spec.rb +2 -2
- data/spec/savon/options_spec.rb +235 -53
- data/spec/savon/request_spec.rb +2 -2
- data/spec/savon/response_spec.rb +33 -31
- data/spec/savon/soap_fault_spec.rb +5 -5
- metadata +5 -4
@@ -25,7 +25,7 @@ describe "Email example" do
|
|
25
25
|
pending "API limit exceeded"
|
26
26
|
else
|
27
27
|
# The expected result. We unfortunately don't have a license key for this service.
|
28
|
-
response_text.
|
28
|
+
expect(response_text).to eq("Email Domain Not Found")
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
@@ -15,8 +15,8 @@ describe 'rpc/encoded binding test' do
|
|
15
15
|
rescue Savon::SOAPFault => e
|
16
16
|
$stderr.puts e.to_hash.inspect
|
17
17
|
f_c = e.to_hash[:fault][:faultstring]
|
18
|
-
f_c.
|
19
|
-
f_c.
|
18
|
+
expect(f_c).not_to eq('No such operation \'getQuoteRequest\'')
|
19
|
+
expect(f_c).to eq('soapenv:Server.userException')
|
20
20
|
pending e
|
21
21
|
end
|
22
22
|
end
|
@@ -22,7 +22,7 @@ describe "Stockquote example" do
|
|
22
22
|
nori_options = { :convert_tags_to => lambda { |tag| tag.snakecase.to_sym } }
|
23
23
|
result = Nori.new(nori_options).parse(cdata)
|
24
24
|
|
25
|
-
result[:stock_quotes][:stock][:symbol].
|
25
|
+
expect(result[:stock_quotes][:stock][:symbol]).to eq("AAPL")
|
26
26
|
end
|
27
27
|
|
28
28
|
end
|
data/spec/savon/builder_spec.rb
CHANGED
data/spec/savon/client_spec.rb
CHANGED
@@ -4,33 +4,33 @@ describe String do
|
|
4
4
|
|
5
5
|
describe "snakecase" do
|
6
6
|
it "lowercases one word CamelCase" do
|
7
|
-
"Merb".snakecase.
|
7
|
+
expect("Merb".snakecase).to eq("merb")
|
8
8
|
end
|
9
9
|
|
10
10
|
it "makes one underscore snakecase two word CamelCase" do
|
11
|
-
"MerbCore".snakecase.
|
11
|
+
expect("MerbCore".snakecase).to eq("merb_core")
|
12
12
|
end
|
13
13
|
|
14
14
|
it "handles CamelCase with more than 2 words" do
|
15
|
-
"SoYouWantContributeToMerbCore".snakecase.
|
15
|
+
expect("SoYouWantContributeToMerbCore".snakecase).to eq("so_you_want_contribute_to_merb_core")
|
16
16
|
end
|
17
17
|
|
18
18
|
it "handles CamelCase with more than 2 capital letter in a row" do
|
19
|
-
"CNN".snakecase.
|
20
|
-
"CNNNews".snakecase.
|
21
|
-
"HeadlineCNNNews".snakecase.
|
19
|
+
expect("CNN".snakecase).to eq("cnn")
|
20
|
+
expect("CNNNews".snakecase).to eq("cnn_news")
|
21
|
+
expect("HeadlineCNNNews".snakecase).to eq("headline_cnn_news")
|
22
22
|
end
|
23
23
|
|
24
24
|
it "does NOT change one word lowercase" do
|
25
|
-
"merb".snakecase.
|
25
|
+
expect("merb".snakecase).to eq("merb")
|
26
26
|
end
|
27
27
|
|
28
28
|
it "leaves snake_case as is" do
|
29
|
-
"merb_core".snakecase.
|
29
|
+
expect("merb_core".snakecase).to eq("merb_core")
|
30
30
|
end
|
31
31
|
|
32
32
|
it "converts period characters to underscores" do
|
33
|
-
"User.GetEmail".snakecase.
|
33
|
+
expect("User.GetEmail".snakecase).to eq("user_get_email")
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
@@ -11,11 +11,11 @@ describe Savon::HTTPError do
|
|
11
11
|
describe ".present?" do
|
12
12
|
it "returns true if there was an HTTP error" do
|
13
13
|
http = new_response(:code => 404, :body => "Not Found")
|
14
|
-
expect(Savon::HTTPError.present? http).to
|
14
|
+
expect(Savon::HTTPError.present? http).to be_truthy
|
15
15
|
end
|
16
16
|
|
17
17
|
it "returns false unless there was an HTTP error" do
|
18
|
-
expect(Savon::HTTPError.present? new_response).to
|
18
|
+
expect(Savon::HTTPError.present? new_response).to be_falsey
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
data/spec/savon/options_spec.rb
CHANGED
@@ -45,6 +45,35 @@ describe "Options" do
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
+
context "global: :no_message_tag" do
|
49
|
+
it "omits the 'message tag' encapsulation step" do
|
50
|
+
client = new_client(:endpoint => @server.url(:repeat), :no_message_tag => true,
|
51
|
+
:wsdl => Fixture.wsdl(:no_message_tag))
|
52
|
+
msg = {'extLoginData' => {'Login' => 'test.user', 'Password' => 'secret', 'FacilityID' => 1,
|
53
|
+
'ThreePLKey' => '{XXXX-XXXX-XXXX-XXXX}', 'ThreePLID' => 1},
|
54
|
+
'Items' => ['Item' => {'SKU' => '001002003A', 'CustomerID' => 1,
|
55
|
+
'InventoryMethod' => 'FIFO', 'UPC' => '001002003A'}]}
|
56
|
+
response = client.call(:create_items, :message => msg)
|
57
|
+
|
58
|
+
expect(response.http.body.scan(/<tns:extLoginData>/).count).to eq(1)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "includes the 'message tag' encapsulation step" do
|
62
|
+
# This test is probably just exposing a bug while the previous
|
63
|
+
# test is using a workaround fix.
|
64
|
+
# That is just a guess though. I don't really have to properly debug the WSDL parser.
|
65
|
+
client = new_client(:endpoint => @server.url(:repeat), :no_message_tag => false,
|
66
|
+
:wsdl => Fixture.wsdl(:no_message_tag))
|
67
|
+
msg = {'extLoginData' => {'Login' => 'test.user', 'Password' => 'secret', 'FacilityID' => 1,
|
68
|
+
'ThreePLKey' => '{XXXX-XXXX-XXXX-XXXX}', 'ThreePLID' => 1},
|
69
|
+
'Items' => ['Item' => {'SKU' => '001002003A', 'CustomerID' => 1,
|
70
|
+
'InventoryMethod' => 'FIFO', 'UPC' => '001002003A'}]}
|
71
|
+
response = client.call(:create_items, :message => msg)
|
72
|
+
|
73
|
+
expect(response.http.body.scan(/<tns:extLoginData>/).count).to eq(2)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
48
77
|
context "global :namespaces" do
|
49
78
|
it "adds additional namespaces to the SOAP envelope" do
|
50
79
|
namespaces = { "xmlns:whatever" => "http://whatever.example.com" }
|
@@ -55,6 +84,24 @@ describe "Options" do
|
|
55
84
|
end
|
56
85
|
end
|
57
86
|
|
87
|
+
context 'global :follow_redirects' do
|
88
|
+
it 'sets whether or not request should follow redirects' do
|
89
|
+
client = new_client(:endpoint => @server.url, :follow_redirects => true)
|
90
|
+
|
91
|
+
HTTPI::Request.any_instance.expects(:follow_redirect=).with(true)
|
92
|
+
|
93
|
+
response = client.call(:authenticate)
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'defaults to false' do
|
97
|
+
client = new_client(:endpoint => @server.url)
|
98
|
+
|
99
|
+
HTTPI::Request.any_instance.expects(:follow_redirect=).with(false)
|
100
|
+
|
101
|
+
response = client.call(:authenticate)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
58
105
|
context "global :proxy" do
|
59
106
|
it "sets the proxy server to use" do
|
60
107
|
proxy_url = "http://example.com"
|
@@ -457,80 +504,215 @@ describe "Options" do
|
|
457
504
|
end
|
458
505
|
end
|
459
506
|
|
460
|
-
context "
|
461
|
-
|
462
|
-
|
463
|
-
|
507
|
+
context ":wsse_auth" do
|
508
|
+
let(:username) { "luke" }
|
509
|
+
let(:password) { "secret" }
|
510
|
+
let(:request) { response.http.body }
|
464
511
|
|
465
|
-
|
512
|
+
shared_examples "WSSE basic auth" do
|
513
|
+
it "adds WSSE basic auth information to the request" do
|
514
|
+
# the header and wsse security node
|
515
|
+
wsse_namespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
|
516
|
+
expect(request).to include("<env:Header><wsse:Security xmlns:wsse=\"#{wsse_namespace}\">")
|
517
|
+
|
518
|
+
# split up to prevent problems with unordered Hash attributes in 1.8 [dh, 2012-12-13]
|
519
|
+
expect(request).to include("<wsse:UsernameToken")
|
520
|
+
expect(request).to include("wsu:Id=\"UsernameToken-1\"")
|
521
|
+
expect(request).to include("xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\"")
|
522
|
+
|
523
|
+
# the username and password node with type attribute
|
524
|
+
expect(request).to include("<wsse:Username>#{username}</wsse:Username>")
|
525
|
+
password_text = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"
|
526
|
+
expect(request).to include("<wsse:Password Type=\"#{password_text}\">#{password}</wsse:Password>")
|
527
|
+
end
|
528
|
+
end
|
529
|
+
|
530
|
+
shared_examples "WSSE digest auth" do
|
531
|
+
it "adds WSSE digest auth information to the request" do
|
532
|
+
# the header and wsse security node
|
533
|
+
wsse_namespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
|
534
|
+
expect(request).to include("<env:Header><wsse:Security xmlns:wsse=\"#{wsse_namespace}\">")
|
535
|
+
|
536
|
+
# split up to prevent problems with unordered Hash attributes in 1.8 [dh, 2012-12-13]
|
537
|
+
expect(request).to include("<wsse:UsernameToken")
|
538
|
+
expect(request).to include("wsu:Id=\"UsernameToken-1\"")
|
539
|
+
expect(request).to include("xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\"")
|
540
|
+
|
541
|
+
# the username node
|
542
|
+
expect(request).to include("<wsse:Username>#{username}</wsse:Username>")
|
543
|
+
|
544
|
+
# the nonce node
|
545
|
+
expect(request).to match(/<wsse:Nonce.*>.+\n<\/wsse:Nonce>/)
|
466
546
|
|
467
|
-
|
468
|
-
|
469
|
-
expect(request).to include("<env:Header><wsse:Security xmlns:wsse=\"#{wsse_namespace}\">")
|
547
|
+
# the created node with a timestamp
|
548
|
+
expect(request).to match(/<wsu:Created>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.*<\/wsu:Created>/)
|
470
549
|
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
550
|
+
# the password node contains the encrypted value
|
551
|
+
password_digest = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest"
|
552
|
+
expect(request).to match(/<wsse:Password Type=\"#{password_digest}\">.+<\/wsse:Password>/)
|
553
|
+
expect(request).to_not include(password)
|
554
|
+
end
|
555
|
+
end
|
475
556
|
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
557
|
+
shared_examples "no WSSE auth" do
|
558
|
+
it "does not add WSSE auth to the request" do
|
559
|
+
expect(request).not_to include("<wsse:UsernameToken")
|
560
|
+
end
|
480
561
|
end
|
481
562
|
|
482
|
-
|
483
|
-
|
484
|
-
|
563
|
+
describe "global" do
|
564
|
+
context "enabled" do
|
565
|
+
context "without digest" do
|
566
|
+
let(:client) { new_client(:endpoint => @server.url(:repeat), :wsse_auth => [username, password]) }
|
567
|
+
let(:response) { client.call(:authenticate) }
|
568
|
+
include_examples "WSSE basic auth"
|
569
|
+
end
|
485
570
|
|
486
|
-
|
571
|
+
context "with digest" do
|
572
|
+
let(:client) { new_client(:endpoint => @server.url(:repeat), :wsse_auth => [username, password, :digest]) }
|
573
|
+
let(:response) { client.call(:authenticate) }
|
574
|
+
include_examples "WSSE digest auth"
|
575
|
+
end
|
487
576
|
|
488
|
-
|
489
|
-
|
490
|
-
|
577
|
+
context "local override" do
|
578
|
+
let(:client) { new_client(:endpoint => @server.url(:repeat), :wsse_auth => ["luke", "secret"]) }
|
579
|
+
|
580
|
+
context "enabled" do
|
581
|
+
let(:username) { "lea" }
|
582
|
+
let(:password) { "top-secret" }
|
583
|
+
|
584
|
+
context "without digest" do
|
585
|
+
let(:response) { client.call(:authenticate) {|locals| locals.wsse_auth(username, password)} }
|
586
|
+
include_examples "WSSE basic auth"
|
587
|
+
end
|
588
|
+
|
589
|
+
context "with digest" do
|
590
|
+
let(:response) { client.call(:authenticate) {|locals| locals.wsse_auth(username, password, :digest)} }
|
591
|
+
include_examples "WSSE digest auth"
|
592
|
+
end
|
593
|
+
end
|
594
|
+
|
595
|
+
context "disabled" do
|
596
|
+
let(:response) { client.call(:authenticate) {|locals| locals.wsse_auth(false)} }
|
597
|
+
include_examples "no WSSE auth"
|
598
|
+
end
|
599
|
+
|
600
|
+
context "set to nil" do
|
601
|
+
let(:response) { client.call(:authenticate) {|locals| locals.wsse_auth(nil)} }
|
602
|
+
include_examples "WSSE basic auth"
|
603
|
+
end
|
604
|
+
end
|
491
605
|
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
606
|
+
context "global" do
|
607
|
+
let(:client) { new_client(:endpoint => @server.url(:repeat), :wsse_auth => [username, password, :digest]) }
|
608
|
+
let(:response) { client.call(:authenticate) }
|
609
|
+
include_examples "WSSE digest auth"
|
610
|
+
end
|
611
|
+
end
|
496
612
|
|
497
|
-
|
498
|
-
|
613
|
+
context "not enabled" do
|
614
|
+
let(:client) { new_client(:endpoint => @server.url(:repeat)) }
|
499
615
|
|
500
|
-
|
501
|
-
|
616
|
+
describe "local" do
|
617
|
+
context "enabled" do
|
618
|
+
let(:response) { client.call(:authenticate) {|locals| locals.wsse_auth(username, password, :digest)} }
|
619
|
+
include_examples "WSSE digest auth"
|
620
|
+
end
|
502
621
|
|
503
|
-
|
504
|
-
|
622
|
+
context "disabled" do
|
623
|
+
let(:response) { client.call(:authenticate) { |locals| locals.wsse_auth(false)} }
|
624
|
+
include_examples "no WSSE auth"
|
625
|
+
end
|
505
626
|
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
627
|
+
context "set to nil" do
|
628
|
+
let(:response) { client.call(:authenticate) { |locals| locals.wsse_auth(nil)} }
|
629
|
+
include_examples "no WSSE auth"
|
630
|
+
end
|
631
|
+
end
|
632
|
+
end
|
510
633
|
end
|
511
634
|
end
|
512
635
|
|
513
|
-
context "
|
514
|
-
|
515
|
-
client = new_client(:endpoint => @server.url(:repeat), :wsse_timestamp => true)
|
516
|
-
response = client.call(:authenticate)
|
636
|
+
context ":wsse_timestamp" do
|
637
|
+
let(:request) { response.http.body }
|
517
638
|
|
518
|
-
|
639
|
+
shared_examples "WSSE timestamp" do
|
640
|
+
it "adds WSSE timestamp auth information to the request" do
|
641
|
+
# the header and wsse security node
|
642
|
+
wsse_namespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
|
643
|
+
expect(request).to include("<env:Header><wsse:Security xmlns:wsse=\"#{wsse_namespace}\">")
|
519
644
|
|
520
|
-
|
521
|
-
|
522
|
-
|
645
|
+
# split up to prevent problems with unordered Hash attributes in 1.8 [dh, 2012-12-13]
|
646
|
+
expect(request).to include("<wsu:Timestamp")
|
647
|
+
expect(request).to include("wsu:Id=\"Timestamp-1\"")
|
648
|
+
expect(request).to include("xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\"")
|
523
649
|
|
524
|
-
|
525
|
-
|
526
|
-
expect(request).to include("wsu:Id=\"Timestamp-1\"")
|
527
|
-
expect(request).to include("xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\"")
|
650
|
+
# the created node with a timestamp
|
651
|
+
expect(request).to match(/<wsu:Created>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.*<\/wsu:Created>/)
|
528
652
|
|
529
|
-
|
530
|
-
|
653
|
+
# the expires node with a timestamp
|
654
|
+
expect(request).to match(/<wsu:Expires>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.*<\/wsu:Expires>/)
|
655
|
+
end
|
656
|
+
end
|
657
|
+
|
658
|
+
shared_examples "no WSSE timestamp" do
|
659
|
+
it "does not add WSSE timestamp to the request" do
|
660
|
+
expect(request).not_to include("<wsu:Timestamp")
|
661
|
+
end
|
662
|
+
end
|
663
|
+
|
664
|
+
describe "global" do
|
665
|
+
context "enabled" do
|
666
|
+
context "through block without arguments" do
|
667
|
+
let(:client) do
|
668
|
+
new_client(:endpoint => @server.url(:repeat)) do |globals|
|
669
|
+
globals.wsse_timestamp
|
670
|
+
end
|
671
|
+
end
|
672
|
+
let(:response) { client.call(:authenticate) }
|
673
|
+
include_examples "WSSE timestamp"
|
674
|
+
end
|
531
675
|
|
532
|
-
|
533
|
-
|
676
|
+
context "through initializer options" do
|
677
|
+
let(:client) { new_client(:endpoint => @server.url(:repeat), :wsse_timestamp => true) }
|
678
|
+
let(:response) { client.call(:authenticate) }
|
679
|
+
include_examples "WSSE timestamp"
|
680
|
+
end
|
681
|
+
|
682
|
+
context "with local override" do
|
683
|
+
let(:client) { new_client(:endpoint => @server.url(:repeat), :wsse_timestamp => true) }
|
684
|
+
context "enabled" do
|
685
|
+
let(:response) { client.call(:authenticate) {|locals| locals.wsse_timestamp} }
|
686
|
+
include_examples "WSSE timestamp"
|
687
|
+
end
|
688
|
+
context "disabled" do
|
689
|
+
let(:response) { client.call(:authenticate) {|locals| locals.wsse_timestamp(false) } }
|
690
|
+
include_examples "no WSSE timestamp"
|
691
|
+
end
|
692
|
+
context "set to nil" do
|
693
|
+
let(:response) { client.call(:authenticate) {|locals| locals.wsse_timestamp(nil) } }
|
694
|
+
include_examples "WSSE timestamp"
|
695
|
+
end
|
696
|
+
end
|
697
|
+
end
|
698
|
+
|
699
|
+
context "not enabled" do
|
700
|
+
let(:client) { new_client(:endpoint => @server.url(:repeat)) }
|
701
|
+
describe "local" do
|
702
|
+
context "enabled" do
|
703
|
+
let(:response) { client.call(:authenticate) {|locals| locals.wsse_timestamp} }
|
704
|
+
include_examples "WSSE timestamp"
|
705
|
+
end
|
706
|
+
context "disabled" do
|
707
|
+
let(:response) { client.call(:authenticate) {|locals| locals.wsse_timestamp(false) } }
|
708
|
+
include_examples "no WSSE timestamp"
|
709
|
+
end
|
710
|
+
context "set to nil" do
|
711
|
+
let(:response) { client.call(:authenticate) {|locals| locals.wsse_timestamp(nil) } }
|
712
|
+
include_examples "no WSSE timestamp"
|
713
|
+
end
|
714
|
+
end
|
715
|
+
end
|
534
716
|
end
|
535
717
|
end
|
536
718
|
|
data/spec/savon/request_spec.rb
CHANGED
@@ -129,7 +129,7 @@ describe Savon::WSDLRequest do
|
|
129
129
|
|
130
130
|
new_wsdl_request.build
|
131
131
|
|
132
|
-
expect { http_request.auth.ssl.cert_key }.to raise_error
|
132
|
+
expect { http_request.auth.ssl.cert_key }.to raise_error
|
133
133
|
end
|
134
134
|
end
|
135
135
|
|
@@ -145,7 +145,7 @@ describe Savon::WSDLRequest do
|
|
145
145
|
|
146
146
|
new_wsdl_request.build
|
147
147
|
|
148
|
-
http_request.auth.ssl.cert_key.to_s.
|
148
|
+
expect(http_request.auth.ssl.cert_key.to_s).to match(/BEGIN RSA PRIVATE KEY/)
|
149
149
|
end
|
150
150
|
end
|
151
151
|
end
|