stash-sword 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.
- 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
|