stash-sword 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +42 -0
- data/.rubocop.yml +25 -0
- data/.ruby-version +1 -0
- data/.travis.yml +2 -0
- data/.yardopts +3 -0
- data/Gemfile +6 -0
- data/LICENSE.md +22 -0
- data/README.md +4 -0
- data/Rakefile +49 -0
- data/examples/example.rb +26 -0
- data/examples/uploads/example.zip +0 -0
- data/examples/uploads/example/lorem-ipsum.txt +7 -0
- data/examples/uploads/example/mrt-datacite.xml +22 -0
- data/examples/uploads/example/mrt-dc.xml +13 -0
- data/examples/uploads/example/stash-wrapper.xml +56 -0
- data/lib/stash/sword.rb +39 -0
- data/lib/stash/sword/client.rb +132 -0
- data/lib/stash/sword/deposit_receipt.rb +44 -0
- data/lib/stash/sword/header_utils.rb +42 -0
- data/lib/stash/sword/http_helper.rb +120 -0
- data/lib/stash/sword/iri.rb +12 -0
- data/lib/stash/sword/log_utils.rb +39 -0
- data/lib/stash/sword/module_info.rb +12 -0
- data/lib/stash/sword/namespace.rb +30 -0
- data/lib/stash/sword/sequence_io.rb +105 -0
- data/notes/Dash_Submission_To_Merritt.txt +40 -0
- data/notes/service-document.xml +15 -0
- data/spec/.rubocop.yml +10 -0
- data/spec/data/deposit_receipt_merritt.xml +25 -0
- data/spec/data/deposit_receipt_spec.xml +58 -0
- data/spec/rspec_custom_matchers.rb +118 -0
- data/spec/spec_helper.rb +33 -0
- data/spec/unit/stash/sword2/client_spec.rb +110 -0
- data/spec/unit/stash/sword2/deposit_receipt_spec.rb +48 -0
- data/spec/unit/stash/sword2/http_helper_get_spec.rb +131 -0
- data/spec/unit/stash/sword2/http_helper_post_spec.rb +143 -0
- data/spec/unit/stash/sword2/http_helper_put_spec.rb +143 -0
- data/spec/unit/stash/sword2/log_spec.rb +23 -0
- data/spec/unit/stash/sword2/namespaces_spec.rb +31 -0
- data/spec/unit/stash/sword2/sequence_io_spec.rb +153 -0
- data/stash-sword.gemspec +47 -0
- metadata +279 -0
@@ -0,0 +1,15 @@
|
|
1
|
+
<service xmlns="http://www.w3.org/2007/app" xmlns:atom="http://www.w3.org/2005/Atom">
|
2
|
+
<workspace>
|
3
|
+
<atom:title type="text">Merritt</atom:title>
|
4
|
+
<collection href="http://merritt.cdlib.org/sword/v2/dash_ucb">
|
5
|
+
<atom:title type="text">UCB Dash</atom:title>
|
6
|
+
<accept>*/*</accept>
|
7
|
+
<accept alternate="multipart-related">*/*</accept>
|
8
|
+
<mediation xmlns="http://purl.org/net/sword/terms/">true</mediation>
|
9
|
+
<acceptPackaging xmlns="http://purl.org/net/sword/terms/">http://purl.org/net/sword/package/SimpleZip</acceptPackaging>
|
10
|
+
</collection>
|
11
|
+
</workspace>
|
12
|
+
<generator xmlns="http://www.w3.org/2005/Atom" uri="http://www.swordapp.org/" version="2.0" />
|
13
|
+
<version xmlns="http://purl.org/net/sword/terms/">2.0</version>
|
14
|
+
<maxUploadSize xmlns="http://purl.org/net/sword/terms/">10000000</maxUploadSize>
|
15
|
+
</service>
|
data/spec/.rubocop.yml
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
<entry xmlns="http://www.w3.org/2005/Atom">
|
2
|
+
<author>
|
3
|
+
<name>ucop_dash_submitter</name>
|
4
|
+
</author>
|
5
|
+
<title type="text">doi:10.5072/FK1465406644</title>
|
6
|
+
<id>http://n2t.net/ark:/99999/fk47h1tz4k</id>
|
7
|
+
<updated>2016-06-08T17:24:12.643Z</updated>
|
8
|
+
<generator uri="http://www.swordapp.org/" version="2.0"/>
|
9
|
+
<link
|
10
|
+
href="http://sword-aws-dev.cdlib.org:39001/mrtsword/edit/dash_cdl/doi%3A10.5072%2FFK1465406644"
|
11
|
+
rel="edit"
|
12
|
+
/>
|
13
|
+
<link
|
14
|
+
href="http://sword-aws-dev.cdlib.org:39001/mrtsword/edit/dash_cdl/doi%3A10.5072%2FFK1465406644"
|
15
|
+
rel="http://purl.org/net/sword/terms/add"
|
16
|
+
/>
|
17
|
+
<link
|
18
|
+
href="http://merritt-dev.cdlib.org/d/ark%3A%2F99999%2Ffk47h1tz4k"
|
19
|
+
rel="edit-media"
|
20
|
+
/>
|
21
|
+
<treatment xmlns="http://purl.org/net/sword/terms/">no treatment information available</treatment>
|
22
|
+
<link
|
23
|
+
href="http://n2t.net/ark:/99999/fk47h1tz4k" rel="alternate"
|
24
|
+
/>
|
25
|
+
</entry>
|
@@ -0,0 +1,58 @@
|
|
1
|
+
<entry xmlns="http://www.w3.org/2005/Atom"
|
2
|
+
xmlns:sword="http://purl.org/net/sword/"
|
3
|
+
xmlns:dcterms="http://purl.org/dc/terms/">
|
4
|
+
|
5
|
+
<title>My Deposit</title>
|
6
|
+
<id>info:something:1</id>
|
7
|
+
<updated>2008-08-18T14:27:08Z</updated>
|
8
|
+
<summary type="text">A summary</summary>
|
9
|
+
<generator uri="http://www.myrepository.ac.uk/sword-plugin" version="1.0"/>
|
10
|
+
|
11
|
+
<!-- the item's metadata -->
|
12
|
+
<dcterms:abstract>The abstract</dcterms:abstract>
|
13
|
+
<dcterms:accessRights>Access Rights</dcterms:accessRights>
|
14
|
+
<dcterms:alternative>Alternative Title</dcterms:alternative>
|
15
|
+
<dcterms:available>Date Available</dcterms:available>
|
16
|
+
<dcterms:bibliographicCitation>Bibliographic Citation</dcterms:bibliographicCitation>
|
17
|
+
<dcterms:contributor>Contributor</dcterms:contributor>
|
18
|
+
<dcterms:description>Description</dcterms:description>
|
19
|
+
<dcterms:hasPart>Has Part</dcterms:hasPart>
|
20
|
+
<dcterms:hasVersion>Has Version</dcterms:hasVersion>
|
21
|
+
<dcterms:identifier>Identifier</dcterms:identifier>
|
22
|
+
<dcterms:isPartOf>Is Part Of</dcterms:isPartOf>
|
23
|
+
<dcterms:publisher>Publisher</dcterms:publisher>
|
24
|
+
<dcterms:references>References</dcterms:references>
|
25
|
+
<dcterms:rightsHolder>Rights Holder</dcterms:rightsHolder>
|
26
|
+
<dcterms:source>Source</dcterms:source>
|
27
|
+
<dcterms:title>Title</dcterms:title>
|
28
|
+
<dcterms:type>Type</dcterms:type>
|
29
|
+
|
30
|
+
<sword:verboseDescription>Verbose description</sword:verboseDescription>
|
31
|
+
<sword:treatment>Unpacked. JPEG contents converted to JPEG2000.</sword:treatment>
|
32
|
+
|
33
|
+
<link rel="alternate" href="http://www.swordserver.ac.uk/col1/mydeposit.html"/>
|
34
|
+
<content type="application/zip" src="http://www.swordserver.ac.uk/col1/mydeposit"/>
|
35
|
+
<link rel="edit-media" href="http://www.swordserver.ac.uk/col1/mydeposit"/>
|
36
|
+
<link rel="edit" href="http://www.swordserver.ac.uk/col1/mydeposit.atom" />
|
37
|
+
<link rel="http://purl.org/net/sword/terms/add" href="http://www.swordserver.ac.uk/col1/mydeposit.atom" />
|
38
|
+
<sword:packaging>http://purl.org/net/sword/package/BagIt</sword:packaging>
|
39
|
+
|
40
|
+
<link rel="http://purl.org/net/sword/terms/originalDeposit"
|
41
|
+
type="application/zip"
|
42
|
+
href="http://www.swordserver.ac.uk/col1/mydeposit/package.zip"/>
|
43
|
+
<link rel="http://purl.org/net/sword/terms/derivedResource"
|
44
|
+
type="application/pdf"
|
45
|
+
href="http://www.swordserver.ac.uk/col1/mydeposit/file1.pdf"/>
|
46
|
+
<link rel="http://purl.org/net/sword/terms/derivedResource"
|
47
|
+
type="application/pdf"
|
48
|
+
href="http://www.swordserver.ac.uk/col1/mydeposit/file2.pdf"/>
|
49
|
+
|
50
|
+
<link rel="http://purl.org/net/sword/terms/statement"
|
51
|
+
type="application/atom+xml;type=feed"
|
52
|
+
href="http://www.swordserver.ac.uk/col1/mydeposit.feed"/>
|
53
|
+
<link rel="http://purl.org/net/sword/terms/statement"
|
54
|
+
type="application/rdf+xml"
|
55
|
+
href="http://www.swordserver.ac.uk/col1/mydeposit.rdf"/>
|
56
|
+
|
57
|
+
|
58
|
+
</entry>
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'rspec/expectations'
|
2
|
+
|
3
|
+
# Workaround for https://github.com/rspec/rspec-mocks/issues/1086
|
4
|
+
class RSpec::Mocks::ErrorGenerator # rubocop:disable Style/ClassAndModuleChildren
|
5
|
+
unless respond_to?(:_default_error_message)
|
6
|
+
alias _default_error_message default_error_message
|
7
|
+
|
8
|
+
def default_error_message(expectation, expected_args, actual_args)
|
9
|
+
failures = [_default_error_message(expectation, expected_args, actual_args)]
|
10
|
+
expectation.expected_args.each do |expected|
|
11
|
+
if expected.respond_to?(:failure_message)
|
12
|
+
failures << expected.failure_message
|
13
|
+
end
|
14
|
+
end
|
15
|
+
failures.join("\n ")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def value_for(key:, in_hash:)
|
21
|
+
matching_key = in_hash.keys.find { |k| k.to_s.casecmp(key.to_s.downcase).zero? }
|
22
|
+
in_hash[matching_key] if matching_key
|
23
|
+
end
|
24
|
+
|
25
|
+
def header?(key:, value:, in_hash:)
|
26
|
+
actual_values = value_for(key: key, in_hash: in_hash)
|
27
|
+
actual_values = [actual_values] unless actual_values.is_a?(Array)
|
28
|
+
actual_values.find do |actual_value|
|
29
|
+
if value.nil?
|
30
|
+
actual_value.nil?
|
31
|
+
elsif value.respond_to?(:match)
|
32
|
+
actual_value && value.match(actual_value)
|
33
|
+
else
|
34
|
+
value == actual_value
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def all?(headers:, in_hash:)
|
40
|
+
headers.each do |k, v|
|
41
|
+
return false unless header?(key: k, value: v, in_hash: in_hash)
|
42
|
+
end
|
43
|
+
true
|
44
|
+
end
|
45
|
+
|
46
|
+
def basic_auth(username, password)
|
47
|
+
'Basic ' + ["#{username}:#{password}"].pack('m').delete("\r\n")
|
48
|
+
end
|
49
|
+
|
50
|
+
RSpec::Matchers.define :request do
|
51
|
+
match do |actual|
|
52
|
+
failures_for(actual).empty?
|
53
|
+
end
|
54
|
+
|
55
|
+
failure_message do |actual|
|
56
|
+
failures_for(actual).join('; ')
|
57
|
+
end
|
58
|
+
|
59
|
+
chain :with_method do |method|
|
60
|
+
@method = method
|
61
|
+
end
|
62
|
+
|
63
|
+
chain :with_uri do |expected_uri|
|
64
|
+
@expected_uri = expected_uri
|
65
|
+
# actual.uri == expected_uri
|
66
|
+
end
|
67
|
+
|
68
|
+
chain :with_headers do |expected_headers|
|
69
|
+
@expected_headers = expected_headers
|
70
|
+
# has_all(headers: expected_headers, in_hash: actual.to_hash).empty
|
71
|
+
end
|
72
|
+
|
73
|
+
chain :with_auth do |username, password|
|
74
|
+
@expected_auth = basic_auth(username, password)
|
75
|
+
# value_for(key: 'Authorization', in_hash: actual.to_hash) == @expected_auth ? true : false
|
76
|
+
end
|
77
|
+
|
78
|
+
def failures_for(actual) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
79
|
+
return ["Expected Net::HTTPRequest, got: #{actual.class}"] unless actual.is_a?(Net::HTTPRequest)
|
80
|
+
failures = []
|
81
|
+
failures << "Expected method #{@method}, got: #{actual.method}" if bad_method(actual)
|
82
|
+
failures << "Expected uri #{@expected_uri}, got: #{actual.uri}" if bad_uri(actual)
|
83
|
+
failures << "Expected headers #{expected_headers}, got: #{actual.to_hash}" if bad_headers(actual)
|
84
|
+
failures << "Expected Authorization header #{@expected_auth}, got: #{value_for(key: 'Authorization', in_hash: actual.to_hash) || 'nil'}" if bad_auth(actual)
|
85
|
+
failures
|
86
|
+
end
|
87
|
+
|
88
|
+
def bad_method(actual)
|
89
|
+
actual.method != @method.to_s.upcase if @method
|
90
|
+
end
|
91
|
+
|
92
|
+
def bad_uri(actual)
|
93
|
+
actual.uri != @expected_uri if @expected_uri
|
94
|
+
end
|
95
|
+
|
96
|
+
def bad_headers(actual)
|
97
|
+
!all?(headers: @expected_headers, in_hash: actual.to_hash) if @expected_headers
|
98
|
+
end
|
99
|
+
|
100
|
+
def bad_auth(actual)
|
101
|
+
!header?(key: 'Authorization', value: @expected_auth, in_hash: actual.to_hash) if @expected_auth
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
RSpec::Matchers.define :include_header do |k, v|
|
107
|
+
def matching_key(k, actual)
|
108
|
+
actual.keys.find { |k2| k2.to_s.casecmp(k.to_s.downcase).zero? }
|
109
|
+
end
|
110
|
+
|
111
|
+
match do |actual|
|
112
|
+
all?(headers: { k => v }, in_hash: actual)
|
113
|
+
end
|
114
|
+
|
115
|
+
failure_message do |actual|
|
116
|
+
"expected #{k}: #{v} but found #{value_for(key: k, in_hash: actual) || 'nil'}"
|
117
|
+
end
|
118
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# ------------------------------------------------------------
|
2
|
+
# SimpleCov setup
|
3
|
+
|
4
|
+
if ENV['COVERAGE']
|
5
|
+
require 'simplecov'
|
6
|
+
require 'simplecov-console'
|
7
|
+
|
8
|
+
SimpleCov.minimum_coverage 100
|
9
|
+
SimpleCov.start do
|
10
|
+
add_filter '/spec/'
|
11
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
12
|
+
SimpleCov::Formatter::HTMLFormatter,
|
13
|
+
SimpleCov::Formatter::Console,
|
14
|
+
]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# ------------------------------------------------------------
|
19
|
+
# Rspec configuration
|
20
|
+
|
21
|
+
require 'rspec'
|
22
|
+
|
23
|
+
RSpec.configure do |config|
|
24
|
+
config.raise_errors_for_deprecations!
|
25
|
+
config.mock_with :rspec
|
26
|
+
end
|
27
|
+
|
28
|
+
require 'rspec_custom_matchers'
|
29
|
+
|
30
|
+
# ------------------------------------------------------------
|
31
|
+
# Code under test
|
32
|
+
|
33
|
+
require 'stash/sword'
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'webmock/rspec'
|
3
|
+
|
4
|
+
module Stash
|
5
|
+
module Sword
|
6
|
+
describe Client do
|
7
|
+
attr_reader :username, :client, :password, :on_behalf_of, :zipfile, :doi, :collection_uri
|
8
|
+
|
9
|
+
before(:each) do
|
10
|
+
@username = 'ucb_dash_submitter'
|
11
|
+
@password = 'ucb_dash_password'
|
12
|
+
@collection_uri = 'http://uc3-mrtsword-dev.cdlib.org:39001/mrtsword/collection/dash_ucb'
|
13
|
+
@on_behalf_of = 'ucb_dash_author'
|
14
|
+
@client = Client.new(collection_uri: @collection_uri, username: @username, password: @password, on_behalf_of: @on_behalf_of)
|
15
|
+
@zipfile = 'examples/uploads/example.zip'
|
16
|
+
@doi = "doi:10.5072/FK#{Time.now.getutc.xmlschema.gsub(/[^0-9a-z]/i, '')}"
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#create' do
|
20
|
+
it 'POSTs with the correct headers' do
|
21
|
+
authorized_uri = collection_uri.sub('http://', "http://#{username}:#{password}@")
|
22
|
+
|
23
|
+
stub_request(:post, authorized_uri).to_return(
|
24
|
+
body: '<entry xmlns="http://www.w3.org/2005/Atom"><id>http://merritt.cdlib.org/sword/v2/object/ark:/99999/fk4t157x4p</id><author><name>ucb_dash_submitter</name></author><generator uri="http://www.swordapp.org/" version="2.0" /><link href="http://merritt.cdlib.org/sword/v2/object/ark:/99999/fk4t157x4p" rel="edit" /><link href="http://merritt.cdlib.org/sword/v2/object/ark:/99999/fk4t157x4p" rel="http://purl.org/net/sword/terms/add" /><link href="http://merritt.cdlib.org/sword/v2/object/ark:/99999/fk4t157x4p" rel="edit-media" /><treatment xmlns="http://purl.org/net/sword/terms/">no treatment information available</treatment></entry>'
|
25
|
+
)
|
26
|
+
|
27
|
+
client.create(zipfile: zipfile, doi: doi)
|
28
|
+
|
29
|
+
md5 = Digest::MD5.file(zipfile).to_s
|
30
|
+
|
31
|
+
actual_headers = nil
|
32
|
+
expect(a_request(:post, authorized_uri).with do |req|
|
33
|
+
actual_headers = req.headers
|
34
|
+
end).to have_been_made
|
35
|
+
|
36
|
+
aggregate_failures('request headers') do
|
37
|
+
{
|
38
|
+
'On-Behalf-Of' => on_behalf_of,
|
39
|
+
'Packaging' => 'http://purl.org/net/sword/package/SimpleZip',
|
40
|
+
'Slug' => doi,
|
41
|
+
'Content-Disposition' => 'attachment; filename=example.zip',
|
42
|
+
'Content-MD5' => md5,
|
43
|
+
'Content-Length' => /[0-9]+/,
|
44
|
+
'Content-Type' => 'application/zip'
|
45
|
+
}.each do |k, v|
|
46
|
+
expect(actual_headers).to include_header(k, v)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'returns the entry'
|
52
|
+
it "gets the entry from the Edit-IRI in the Location: header if it isn't returned in the body"
|
53
|
+
it 'forwards a success response'
|
54
|
+
it 'forwards a 4xx error'
|
55
|
+
it 'forwards a 5xx error'
|
56
|
+
it 'forwards an internal exception'
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '#update' do
|
60
|
+
it 'PUTs with the correct headers' do
|
61
|
+
edit_iri = "http://merritt.cdlib.org/sword/v2/object/#{doi}"
|
62
|
+
authorized_uri = edit_iri.sub('http://', "http://#{username}:#{password}@")
|
63
|
+
|
64
|
+
stub_request(:put, authorized_uri)
|
65
|
+
|
66
|
+
client.update(edit_iri: edit_iri, zipfile: zipfile)
|
67
|
+
|
68
|
+
md5 = Digest::MD5.file(zipfile).to_s
|
69
|
+
|
70
|
+
actual_body = nil
|
71
|
+
actual_headers = nil
|
72
|
+
expect(a_request(:put, authorized_uri).with do |req|
|
73
|
+
actual_body = req.body
|
74
|
+
actual_headers = req.headers
|
75
|
+
end).to have_been_made
|
76
|
+
|
77
|
+
aggregate_failures('request headers') do
|
78
|
+
{
|
79
|
+
'Content-Length' => /[0-9]+/,
|
80
|
+
'Content-Type' => %r{multipart/related; type="application/atom\+xml"; boundary=.*},
|
81
|
+
'On-Behalf-Of' => on_behalf_of
|
82
|
+
}.each do |k, v|
|
83
|
+
expect(actual_headers).to include_header(k, v)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
mime_headers = {
|
88
|
+
'Packaging' => 'http://purl.org/net/sword/package/SimpleZip',
|
89
|
+
'Content-Disposition' => 'attachment; name=payload; filename="example.zip"',
|
90
|
+
'Content-Type' => 'application/zip',
|
91
|
+
'Content-MD5' => md5
|
92
|
+
}
|
93
|
+
|
94
|
+
aggregate_failures('MIME headers') do
|
95
|
+
mime_headers.each do |k, v|
|
96
|
+
expect(actual_body).to include("#{k}: #{v}"), "expected #{k}: #{v}, closest match was #{actual_body[/#{k}[^\n]+/m]}"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'does something clever and asynchronous'
|
102
|
+
it 'forwards a success response'
|
103
|
+
it 'forwards a 4xx error'
|
104
|
+
it 'forwards a 5xx error'
|
105
|
+
it 'forwards an internal exception'
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Stash
|
4
|
+
module Sword
|
5
|
+
describe DepositReceipt do
|
6
|
+
describe '#parse_xml' do
|
7
|
+
|
8
|
+
it 'parses the response from the spec' do
|
9
|
+
xml = File.read('spec/data/deposit_receipt_spec.xml')
|
10
|
+
receipt = DepositReceipt.parse_xml(xml)
|
11
|
+
expect(receipt).to be_a(DepositReceipt)
|
12
|
+
|
13
|
+
em_iri = receipt.link(rel: 'edit-media')
|
14
|
+
expect(em_iri.href).to eq(URI('http://www.swordserver.ac.uk/col1/mydeposit'))
|
15
|
+
|
16
|
+
se_iri = receipt.link(rel: URI('http://purl.org/net/sword/terms/add'))
|
17
|
+
expect(se_iri.href).to eq(URI('http://www.swordserver.ac.uk/col1/mydeposit.atom'))
|
18
|
+
|
19
|
+
edit_iri = receipt.link(rel: 'edit')
|
20
|
+
expect(edit_iri.href).to eq(URI('http://www.swordserver.ac.uk/col1/mydeposit.atom'))
|
21
|
+
|
22
|
+
expect(receipt.em_iri).to eq(em_iri.href)
|
23
|
+
expect(receipt.se_iri).to eq(se_iri.href)
|
24
|
+
expect(receipt.edit_iri).to eq(edit_iri.href)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'parses a Merritt response' do
|
28
|
+
xml = File.read('spec/data/deposit_receipt_merritt.xml')
|
29
|
+
receipt = DepositReceipt.parse_xml(xml)
|
30
|
+
expect(receipt).to be_a(DepositReceipt)
|
31
|
+
|
32
|
+
em_iri = receipt.link(rel: 'edit-media')
|
33
|
+
expect(em_iri.href).to eq(URI('http://merritt-dev.cdlib.org/d/ark%3A%2F99999%2Ffk47h1tz4k'))
|
34
|
+
|
35
|
+
se_iri = receipt.link(rel: URI('http://purl.org/net/sword/terms/add'))
|
36
|
+
expect(se_iri.href).to eq(URI('http://sword-aws-dev.cdlib.org:39001/mrtsword/edit/dash_cdl/doi%3A10.5072%2FFK1465406644'))
|
37
|
+
|
38
|
+
edit_iri = receipt.link(rel: 'edit')
|
39
|
+
expect(edit_iri.href).to eq(URI('http://sword-aws-dev.cdlib.org:39001/mrtsword/edit/dash_cdl/doi%3A10.5072%2FFK1465406644'))
|
40
|
+
|
41
|
+
expect(receipt.em_iri).to eq(em_iri.href)
|
42
|
+
expect(receipt.se_iri).to eq(se_iri.href)
|
43
|
+
expect(receipt.edit_iri).to eq(edit_iri.href)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Stash
|
4
|
+
module Sword
|
5
|
+
describe HTTPHelper do
|
6
|
+
|
7
|
+
# ------------------------------------------------------------
|
8
|
+
# Fixture
|
9
|
+
|
10
|
+
attr_writer :user_agent
|
11
|
+
|
12
|
+
def user_agent
|
13
|
+
@user_agent ||= 'elvis'
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_writer :helper
|
17
|
+
|
18
|
+
def helper
|
19
|
+
@helper ||= HTTPHelper.new(user_agent: user_agent)
|
20
|
+
end
|
21
|
+
|
22
|
+
# ------------------------------------------------------------
|
23
|
+
# Tests
|
24
|
+
|
25
|
+
describe '#get' do
|
26
|
+
|
27
|
+
# ------------------------------
|
28
|
+
# Fixture
|
29
|
+
|
30
|
+
before(:each) do
|
31
|
+
@http = instance_double(Net::HTTP)
|
32
|
+
allow(Net::HTTP).to receive(:new).and_return(@http)
|
33
|
+
allow(@http).to receive(:start).and_yield(@http)
|
34
|
+
@success = Net::HTTPOK.allocate
|
35
|
+
@body = 'I am the body of the response'
|
36
|
+
allow(@success).to receive(:body).and_return(@body)
|
37
|
+
end
|
38
|
+
|
39
|
+
# ------------------------------
|
40
|
+
# Tests
|
41
|
+
|
42
|
+
it 'gets the specified URI' do
|
43
|
+
uri = URI('http://example.org/')
|
44
|
+
expect(@http).to receive(:request).with(request.with_method('GET').with_uri(uri)).and_yield(@success)
|
45
|
+
helper.get(uri: uri)
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'gets a response' do
|
49
|
+
expect(@http).to receive(:request).and_yield(@success)
|
50
|
+
expect(helper.get(uri: URI('http://example.org/'))).to be(@body)
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'sets the User-Agent header' do
|
54
|
+
agent = 'Not Elvis'
|
55
|
+
helper = HTTPHelper.new(user_agent: agent)
|
56
|
+
expect(@http).to receive(:request).with(request.with_method('GET').with_headers('User-Agent' => agent)).and_yield(@success)
|
57
|
+
helper.get(uri: URI('http://example.org/'))
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'sets Basic-Auth headers' do
|
61
|
+
uri = URI('http://example.org/')
|
62
|
+
expect(@http).to receive(:request).with(request.with_method('GET').with_uri(uri).with_auth('elvis', 'presley')).and_yield(@success)
|
63
|
+
helper = HTTPHelper.new(user_agent: user_agent, username: 'elvis', password: 'presley')
|
64
|
+
helper.get(uri: uri)
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'uses SSL for https requests' do
|
68
|
+
uri = URI('https://example.org/')
|
69
|
+
expect(Net::HTTP).to receive(:start).with(uri.hostname, uri.port, use_ssl: true).and_call_original
|
70
|
+
expect(@http).to receive(:request).and_yield(@success)
|
71
|
+
helper.get(uri: uri)
|
72
|
+
end
|
73
|
+
|
74
|
+
it 're-requests on receiving a 1xx' do
|
75
|
+
uri = URI('http://example.org/')
|
76
|
+
@info = Net::HTTPContinue.allocate
|
77
|
+
|
78
|
+
expected = [@info, @success]
|
79
|
+
expect(@http).to receive(:request).twice.with(request.with_method('GET').with_uri(uri).with_headers('User-Agent' => user_agent)) do |&block|
|
80
|
+
block.call(expected.shift)
|
81
|
+
end
|
82
|
+
|
83
|
+
expect(helper.get(uri: uri)).to be(@body)
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'redirects on receiving a 3xx' do
|
87
|
+
uri = URI('http://example.org/')
|
88
|
+
uri2 = URI('http://example.org/new')
|
89
|
+
@redirect = Net::HTTPMovedPermanently.allocate
|
90
|
+
allow(@redirect).to receive(:[]).with('location').and_return(uri2.to_s)
|
91
|
+
expect(@http).to receive(:request).with(request.with_method('GET').with_uri(uri).with_headers('User-Agent' => user_agent)).and_yield(@redirect)
|
92
|
+
expect(@http).to receive(:request).with(request.with_method('GET').with_uri(uri2).with_headers('User-Agent' => user_agent)).and_yield(@success)
|
93
|
+
expect(helper.get(uri: uri)).to be(@body)
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'only redirects a limited number of times' do
|
97
|
+
uri = URI('http://example.org/')
|
98
|
+
@redirect = Net::HTTPMovedPermanently.allocate
|
99
|
+
allow(@redirect).to receive(:[]).with('location').and_return(uri.to_s)
|
100
|
+
expect(@http).to receive(:request).with(request.with_method('GET').with_uri(uri).with_headers('User-Agent' => user_agent)).exactly(HTTPHelper::DEFAULT_MAX_REDIRECTS).times.and_yield(@redirect)
|
101
|
+
expect { helper.get(uri: uri) }.to raise_error do |e|
|
102
|
+
expect(e.message).to match(/Redirect limit.*exceeded.*#{uri.to_s}/)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'fails on a 4xx' do
|
107
|
+
@error = Net::HTTPForbidden
|
108
|
+
allow(@error).to receive(:code).and_return(403)
|
109
|
+
allow(@error).to receive(:message).and_return('Forbidden')
|
110
|
+
expect(@http).to receive(:request).and_yield(@error)
|
111
|
+
uri = URI('http://example.org/')
|
112
|
+
expect { helper.get(uri: uri) }.to raise_error do |e|
|
113
|
+
expect(e.message).to match(/403.*Forbidden.*#{uri.to_s}/)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'fails on a 5xx' do
|
118
|
+
@error = Net::HTTPServerError
|
119
|
+
allow(@error).to receive(:code).and_return(500)
|
120
|
+
allow(@error).to receive(:message).and_return('Internal Server Error')
|
121
|
+
expect(@http).to receive(:request).and_yield(@error)
|
122
|
+
uri = URI('http://example.org/')
|
123
|
+
expect { helper.get(uri: uri) }.to raise_error do |e|
|
124
|
+
expect(e.message).to match(/500.*Internal Server Error.*#{uri.to_s}/)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|