resync 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 +23 -0
- data/.ruby-version +1 -0
- data/.travis.yml +2 -0
- data/Gemfile +3 -0
- data/LICENSE.md +22 -0
- data/README.md +92 -0
- data/Rakefile +56 -0
- data/example.rb +100 -0
- data/lib/resync/capability_list.rb +85 -0
- data/lib/resync/change_dump.rb +15 -0
- data/lib/resync/change_dump_manifest.rb +15 -0
- data/lib/resync/change_list.rb +15 -0
- data/lib/resync/change_list_index.rb +26 -0
- data/lib/resync/link.rb +87 -0
- data/lib/resync/metadata.rb +112 -0
- data/lib/resync/resource.rb +72 -0
- data/lib/resync/resource_dump.rb +15 -0
- data/lib/resync/resource_dump_manifest.rb +15 -0
- data/lib/resync/resource_list.rb +15 -0
- data/lib/resync/resource_list_index.rb +15 -0
- data/lib/resync/shared/augmented.rb +76 -0
- data/lib/resync/shared/base_resource_list.rb +117 -0
- data/lib/resync/shared/descriptor.rb +135 -0
- data/lib/resync/shared/sitemap_index.rb +32 -0
- data/lib/resync/shared/sorted_resource_list.rb +60 -0
- data/lib/resync/source_description.rb +14 -0
- data/lib/resync/types/change.rb +14 -0
- data/lib/resync/types/change_frequency.rb +18 -0
- data/lib/resync/types.rb +6 -0
- data/lib/resync/version.rb +4 -0
- data/lib/resync/xml.rb +216 -0
- data/lib/resync/xml_parser.rb +65 -0
- data/lib/resync.rb +4 -0
- data/resync.gemspec +36 -0
- data/spec/acceptance/xml_parser_spec.rb +1049 -0
- data/spec/data/examples/README.md +1 -0
- data/spec/data/examples/example-1.xml +12 -0
- data/spec/data/examples/example-12.xml +25 -0
- data/spec/data/examples/example-13.xml +25 -0
- data/spec/data/examples/example-14.xml +23 -0
- data/spec/data/examples/example-15.xml +21 -0
- data/spec/data/examples/example-16.xml +24 -0
- data/spec/data/examples/example-17.xml +39 -0
- data/spec/data/examples/example-18.xml +25 -0
- data/spec/data/examples/example-19.xml +28 -0
- data/spec/data/examples/example-2.xml +18 -0
- data/spec/data/examples/example-20.xml +22 -0
- data/spec/data/examples/example-21.xml +31 -0
- data/spec/data/examples/example-22.xml +41 -0
- data/spec/data/examples/example-23.xml +41 -0
- data/spec/data/examples/example-24.xml +28 -0
- data/spec/data/examples/example-25.xml +21 -0
- data/spec/data/examples/example-26.xml +18 -0
- data/spec/data/examples/example-27.xml +36 -0
- data/spec/data/examples/example-28.xml +34 -0
- data/spec/data/examples/example-29.xml +27 -0
- data/spec/data/examples/example-3.xml +17 -0
- data/spec/data/examples/example-30.xml +18 -0
- data/spec/data/examples/example-31.xml +16 -0
- data/spec/data/examples/example-32.xml +22 -0
- data/spec/data/examples/example-33.xml +22 -0
- data/spec/data/examples/example-4.xml +10 -0
- data/spec/data/examples/example-5.xml +18 -0
- data/spec/data/examples/example-6.xml +21 -0
- data/spec/data/examples/example-7.xml +13 -0
- data/spec/data/examples/example-8.xml +12 -0
- data/spec/data/resourcesync.xsd +148 -0
- data/spec/data/siteindex.xsd +75 -0
- data/spec/data/sitemap.xsd +116 -0
- data/spec/rspec_custom_matchers.rb +89 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/todo.rb +11 -0
- data/spec/unit/resync/capability_list_spec.rb +138 -0
- data/spec/unit/resync/change_dump_manifest_spec.rb +75 -0
- data/spec/unit/resync/change_dump_spec.rb +61 -0
- data/spec/unit/resync/change_list_index_spec.rb +49 -0
- data/spec/unit/resync/change_list_spec.rb +75 -0
- data/spec/unit/resync/link_spec.rb +93 -0
- data/spec/unit/resync/metadata_spec.rb +169 -0
- data/spec/unit/resync/resource_dump_manifest_spec.rb +59 -0
- data/spec/unit/resync/resource_dump_spec.rb +62 -0
- data/spec/unit/resync/resource_list_index_spec.rb +53 -0
- data/spec/unit/resync/resource_list_spec.rb +60 -0
- data/spec/unit/resync/resource_spec.rb +176 -0
- data/spec/unit/resync/shared/augmented_examples.rb +58 -0
- data/spec/unit/resync/shared/base_resource_list_examples.rb +103 -0
- data/spec/unit/resync/shared/descriptor_examples.rb +122 -0
- data/spec/unit/resync/shared/descriptor_spec.rb +33 -0
- data/spec/unit/resync/shared/sorted_list_examples.rb +134 -0
- data/spec/unit/resync/shared/uri_field_examples.rb +36 -0
- data/spec/unit/resync/source_description_spec.rb +55 -0
- data/spec/unit/resync/xml/timenode_spec.rb +48 -0
- data/spec/unit/resync/xml/xml_spec.rb +40 -0
- data/spec/unit/resync/xml_parser_spec.rb +82 -0
- metadata +340 -0
@@ -0,0 +1,138 @@
|
|
1
|
+
require_relative 'shared/base_resource_list_examples'
|
2
|
+
|
3
|
+
module Resync
|
4
|
+
describe CapabilityList do
|
5
|
+
|
6
|
+
# ------------------------------------------------------
|
7
|
+
# Superclass conformance
|
8
|
+
|
9
|
+
# Fixture overrides
|
10
|
+
|
11
|
+
def required_arguments
|
12
|
+
# TODO: Figure out if 'up' is mandatory for all resource lists, or just CapabilityList
|
13
|
+
{ links: [Link.new(rel: 'up', uri: 'http://example.org/')] }
|
14
|
+
end
|
15
|
+
|
16
|
+
def valid_resources
|
17
|
+
[Resource.new(uri: 'http://example.com/dataset1/resourcelist.xml', metadata: Metadata.new(capability: 'resourcelist')),
|
18
|
+
Resource.new(uri: 'http://example.com/dataset1/resourcedump.xml', metadata: Metadata.new(capability: 'resourcedump')),
|
19
|
+
Resource.new(uri: 'http://example.com/dataset1/changelist.xml', metadata: Metadata.new(capability: 'changelist')),
|
20
|
+
Resource.new(uri: 'http://example.com/dataset1/changedump.xml', metadata: Metadata.new(capability: 'changedump'))]
|
21
|
+
end
|
22
|
+
|
23
|
+
# Tests
|
24
|
+
|
25
|
+
it_behaves_like BaseResourceList
|
26
|
+
|
27
|
+
# ------------------------------------------------------
|
28
|
+
# Tests
|
29
|
+
|
30
|
+
describe '#new' do
|
31
|
+
|
32
|
+
describe 'links' do
|
33
|
+
it 'accepts a list of links' do
|
34
|
+
# TODO: Figure out if 'up' is mandatory for all resource lists, or just CapabilityList
|
35
|
+
links = [Link.new(rel: 'up', uri: 'http://example.org/'), Link.new(rel: 'duplicates', uri: 'http://example.com/')]
|
36
|
+
list = CapabilityList.new(links: links)
|
37
|
+
expect(list.links).to eq(links)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe 'source description' do
|
42
|
+
it 'extracts the "up" link' do
|
43
|
+
expected = 'http://example.org/desc.xml'
|
44
|
+
links = [Link.new(rel: 'up', uri: expected), Link.new(rel: 'duplicates', uri: 'http://example.com/')]
|
45
|
+
list = CapabilityList.new(links: links)
|
46
|
+
expect(list.source_description).to eq(URI(expected))
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'fails if no source description specified' do
|
50
|
+
links = [Link.new(rel: 'describedby', uri: 'http://example.org/'), Link.new(rel: 'duplicates', uri: 'http://example.com/')]
|
51
|
+
expect { CapabilityList.new(links: links) }.to raise_error(ArgumentError)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe 'resources' do
|
56
|
+
it 'fails if a resource does not have a capability' do
|
57
|
+
resources = [Resource.new(uri: 'http://example.com/dataset1/resourcelist.xml')]
|
58
|
+
expect { CapabilityList.new(resources: resources, links: [Link.new(rel: 'up', uri: 'http://example.org/')]) }.to raise_error(ArgumentError)
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'fails if more than one resource is defined for the same capability' do
|
62
|
+
resources = [1, 2].map { |n| Resource.new(uri: "http://example.com/list-#{n}.xml", metadata: Metadata.new(capability: 'resourcelist')) }
|
63
|
+
expect { CapabilityList.new(resources: resources, links: [Link.new(rel: 'up', uri: 'http://example.org/')]) }.to raise_error(ArgumentError)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe 'resource_for' do
|
69
|
+
it 'maps resources by capability' do
|
70
|
+
resources = valid_resources
|
71
|
+
capability_list = CapabilityList.new(resources: resources, links: [Link.new(rel: 'up', uri: 'http://example.org/')])
|
72
|
+
expect(capability_list.resource_for(capability: 'resourcelist')).to eq(resources[0])
|
73
|
+
expect(capability_list.resource_for(capability: 'resourcedump')).to eq(resources[1])
|
74
|
+
expect(capability_list.resource_for(capability: 'changelist')).to eq(resources[2])
|
75
|
+
expect(capability_list.resource_for(capability: 'changedump')).to eq(resources[3])
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe 'resources_for' do
|
80
|
+
it 'maps resources by capability' do
|
81
|
+
resources = valid_resources
|
82
|
+
capability_list = CapabilityList.new(resources: resources, links: [Link.new(rel: 'up', uri: 'http://example.org/')])
|
83
|
+
expect(capability_list.resources_for(capability: 'resourcelist')).to eq([resources[0]])
|
84
|
+
expect(capability_list.resources_for(capability: 'resourcedump')).to eq([resources[1]])
|
85
|
+
expect(capability_list.resources_for(capability: 'changelist')).to eq([resources[2]])
|
86
|
+
expect(capability_list.resources_for(capability: 'changedump')).to eq([resources[3]])
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe 'XML conversion' do
|
91
|
+
describe '#from_xml' do
|
92
|
+
it 'parses an XML string' do
|
93
|
+
data = File.read('spec/data/examples/example-13.xml')
|
94
|
+
list = CapabilityList.load_from_xml(XML.element(data))
|
95
|
+
|
96
|
+
links = list.links
|
97
|
+
expect(links.size).to eq(2)
|
98
|
+
ln0 = links[0]
|
99
|
+
expect(ln0.rel).to eq('describedby')
|
100
|
+
expect(ln0.uri).to eq(URI('http://example.com/info_about_set1_of_resources.xml'))
|
101
|
+
ln1 = links[1]
|
102
|
+
expect(ln1.rel).to eq('up')
|
103
|
+
expect(ln1.uri).to eq(URI('http://example.com/resourcesync_description.xml'))
|
104
|
+
|
105
|
+
md = list.metadata
|
106
|
+
expect(md.capability).to eq('capabilitylist')
|
107
|
+
|
108
|
+
resources = list.resources
|
109
|
+
expect(resources.size).to eq(4)
|
110
|
+
|
111
|
+
expected_capabilities = %w(resourcelist resourcedump changelist changedump)
|
112
|
+
(0..3).each do |i|
|
113
|
+
resource = resources[i]
|
114
|
+
capability = expected_capabilities[i]
|
115
|
+
expect(resource.uri).to eq(URI("http://example.com/dataset1/#{capability}.xml"))
|
116
|
+
md = resource.metadata
|
117
|
+
expect(md.capability).to eq(capability)
|
118
|
+
|
119
|
+
expect(list.resource_for(capability: capability)).to eq(resource)
|
120
|
+
end
|
121
|
+
|
122
|
+
source_desc = list.source_description
|
123
|
+
expect(source_desc).to eq(URI('http://example.com/resourcesync_description.xml'))
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
describe '#save_to_xml' do
|
128
|
+
it 'can round-trip to XML' do
|
129
|
+
data = File.read('spec/data/examples/example-13.xml')
|
130
|
+
list = CapabilityList.load_from_xml(XML.element(data))
|
131
|
+
xml = list.save_to_xml
|
132
|
+
expect(xml).to be_xml(data)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require_relative 'shared/sorted_list_examples'
|
2
|
+
|
3
|
+
module Resync
|
4
|
+
describe ChangeDumpManifest do
|
5
|
+
it_behaves_like SortedResourceList
|
6
|
+
describe 'XML conversion' do
|
7
|
+
describe '#from_xml' do
|
8
|
+
it 'parses an XML string' do
|
9
|
+
|
10
|
+
xml = File.read('spec/data/examples/example-23.xml')
|
11
|
+
urlset = ChangeDumpManifest.load_from_xml(XML.element(xml))
|
12
|
+
|
13
|
+
links = urlset.links
|
14
|
+
expect(links.size).to eq(1)
|
15
|
+
ln0 = links[0]
|
16
|
+
expect(ln0.rel).to eq('up')
|
17
|
+
expect(ln0.uri).to eq(URI('http://example.com/dataset1/capabilitylist.xml'))
|
18
|
+
|
19
|
+
md = urlset.metadata
|
20
|
+
expect(md.capability).to eq('changedump-manifest')
|
21
|
+
expect(md.from_time).to be_time(Time.utc(2013, 1, 2))
|
22
|
+
expect(md.until_time).to be_time(Time.utc(2013, 1, 3))
|
23
|
+
|
24
|
+
urls = urlset.resources
|
25
|
+
expect(urls.size).to eq(4)
|
26
|
+
|
27
|
+
expected_filenames = %w(res7 res9 res5 res7)
|
28
|
+
expected_extensions = %w(html pdf tiff html)
|
29
|
+
expected_lastmods = [
|
30
|
+
Time.utc(2013, 1, 2, 12),
|
31
|
+
Time.utc(2013, 1, 2, 13),
|
32
|
+
Time.utc(2013, 1, 2, 19),
|
33
|
+
Time.utc(2013, 1, 2, 20)
|
34
|
+
]
|
35
|
+
expected_changes = [Types::Change::CREATED, Types::Change::UPDATED, Types::Change::DELETED, Types::Change::UPDATED]
|
36
|
+
expected_hashes = [
|
37
|
+
{ 'md5' => '1c1b0e264fa9b7e1e9aa6f9db8d6362b' },
|
38
|
+
{ 'md5' => 'f906610c3d4aa745cb2b986f25b37c5a' },
|
39
|
+
{},
|
40
|
+
{ 'md5' => '0988647082c8bc51778894a48ec3b576' }
|
41
|
+
]
|
42
|
+
expected_lengths = [4339, 38_297, nil, 5426]
|
43
|
+
expected_types = [
|
44
|
+
'text/html',
|
45
|
+
'application/pdf',
|
46
|
+
nil,
|
47
|
+
'text/html'
|
48
|
+
]
|
49
|
+
expected_paths = ['/changes/res7.html', '/changes/res9.pdf', nil, '/changes/res7-v2.html']
|
50
|
+
|
51
|
+
(0..3).each do |i|
|
52
|
+
url = urls[i]
|
53
|
+
expect(url.uri).to eq(URI("http://example.com/#{expected_filenames[i]}.#{expected_extensions[i]}"))
|
54
|
+
expect(url.modified_time).to be_time(expected_lastmods[i])
|
55
|
+
md = url.metadata
|
56
|
+
expect(md.change).to eq(expected_changes[i])
|
57
|
+
expect(md.hashes).to eq(expected_hashes[i])
|
58
|
+
expect(md.length).to eq(expected_lengths[i])
|
59
|
+
expect(md.mime_type).to be_mime_type(expected_types[i])
|
60
|
+
expect(md.path).to eq(expected_paths[i])
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '#save_to_xml' do
|
66
|
+
it 'can round-trip to XML' do
|
67
|
+
data = File.read('spec/data/examples/example-23.xml')
|
68
|
+
manifest = ChangeDumpManifest.load_from_xml(XML.element(data))
|
69
|
+
xml = manifest.save_to_xml
|
70
|
+
expect(xml).to be_xml(data)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require_relative 'shared/sorted_list_examples'
|
2
|
+
|
3
|
+
module Resync
|
4
|
+
describe ChangeDump do
|
5
|
+
it_behaves_like SortedResourceList
|
6
|
+
|
7
|
+
describe 'XML conversion' do
|
8
|
+
describe '#from_xml' do
|
9
|
+
it 'parses an XML string' do
|
10
|
+
xml = File.read('spec/data/examples/example-22.xml')
|
11
|
+
urlset = ChangeDump.load_from_xml(XML.element(xml))
|
12
|
+
links = urlset.links
|
13
|
+
expect(links.size).to eq(1)
|
14
|
+
ln0 = links[0]
|
15
|
+
expect(ln0.rel).to eq('up')
|
16
|
+
expect(ln0.uri).to eq(URI('http://example.com/dataset1/capabilitylist.xml'))
|
17
|
+
|
18
|
+
md = urlset.metadata
|
19
|
+
expect(md.capability).to eq('changedump')
|
20
|
+
expect(md.from_time).to be_time(Time.utc(2013, 1, 1))
|
21
|
+
|
22
|
+
urls = urlset.resources
|
23
|
+
expect(urls.size).to eq(3)
|
24
|
+
|
25
|
+
expected_lastmods = [
|
26
|
+
Time.utc(2013, 1, 1, 23, 59, 59),
|
27
|
+
Time.utc(2013, 1, 2, 23, 59, 59),
|
28
|
+
Time.utc(2013, 1, 3, 23, 59, 59)
|
29
|
+
]
|
30
|
+
expected_lengths = [3109, 6629, 8124]
|
31
|
+
|
32
|
+
(0..2).each do |i|
|
33
|
+
url = urls[i]
|
34
|
+
expect(url.uri).to eq(URI("http://example.com/2013010#{i + 1}-changedump.zip"))
|
35
|
+
expect(url.modified_time).to eq(expected_lastmods[i])
|
36
|
+
md = url.metadata
|
37
|
+
expect(md.mime_type).to be_mime_type('application/zip')
|
38
|
+
expect(md.length).to eq(expected_lengths[i])
|
39
|
+
expect(md.from_time).to be_time(Time.utc(2013, 1, i + 1))
|
40
|
+
expect(md.until_time).to be_time(Time.utc(2013, 1, i + 2))
|
41
|
+
links = url.links
|
42
|
+
expect(links.size).to eq(1)
|
43
|
+
ln = links[0]
|
44
|
+
expect(ln.rel).to eq('contents')
|
45
|
+
expect(ln.uri).to eq(URI("http://example.com/2013010#{i + 1}-changedump-manifest.xml"))
|
46
|
+
expect(ln.mime_type).to be_mime_type('application/xml')
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '#save_to_xml' do
|
52
|
+
it 'can round-trip to XML' do
|
53
|
+
data = File.read('spec/data/examples/example-22.xml')
|
54
|
+
dump = ChangeDump.load_from_xml(XML.element(data))
|
55
|
+
xml = dump.save_to_xml
|
56
|
+
expect(xml).to be_xml(data)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require_relative 'shared/sorted_list_examples'
|
2
|
+
|
3
|
+
module Resync
|
4
|
+
describe ChangeListIndex do
|
5
|
+
it_behaves_like SortedResourceList
|
6
|
+
|
7
|
+
describe 'XML conversion' do
|
8
|
+
describe '#from_xml' do
|
9
|
+
it 'parses an XML string' do
|
10
|
+
|
11
|
+
xml = File.read('spec/data/examples/example-20.xml')
|
12
|
+
sitemapindex = ChangeListIndex.load_from_xml(XML.element(xml))
|
13
|
+
|
14
|
+
links = sitemapindex.links
|
15
|
+
expect(links.size).to eq(1)
|
16
|
+
ln0 = links[0]
|
17
|
+
expect(ln0.rel).to eq('up')
|
18
|
+
expect(ln0.uri).to eq(URI('http://example.com/dataset1/capabilitylist.xml'))
|
19
|
+
|
20
|
+
md = sitemapindex.metadata
|
21
|
+
expect(md.capability).to eq('changelist')
|
22
|
+
expect(md.from_time).to be_time(Time.utc(2013, 1, 1))
|
23
|
+
|
24
|
+
sitemaps = sitemapindex.resources
|
25
|
+
expect(sitemaps.size).to eq(3)
|
26
|
+
|
27
|
+
expected_froms = [Time.utc(2013, 1, 1), Time.utc(2013, 1, 2), Time.utc(2013, 1, 3)]
|
28
|
+
expected_untils = [Time.utc(2013, 1, 2), Time.utc(2013, 1, 3), nil]
|
29
|
+
(0..2).each do |i|
|
30
|
+
sitemap = sitemaps[i]
|
31
|
+
expect(sitemap.uri).to eq(URI("http://example.com/2013010#{i + 1}-changelist.xml"))
|
32
|
+
md = sitemap.metadata
|
33
|
+
expect(md.from_time).to be_time(expected_froms[i])
|
34
|
+
expect(md.until_time).to be_time(expected_untils[i])
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '#save_to_xml' do
|
40
|
+
it 'can round-trip to XML' do
|
41
|
+
data = File.read('spec/data/examples/example-20.xml')
|
42
|
+
list = ChangeListIndex.load_from_xml(XML.element(data))
|
43
|
+
xml = list.save_to_xml
|
44
|
+
expect(xml).to be_xml(data)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require_relative 'shared/sorted_list_examples'
|
2
|
+
|
3
|
+
module Resync
|
4
|
+
describe ChangeList do
|
5
|
+
it_behaves_like SortedResourceList
|
6
|
+
|
7
|
+
describe 'XML conversion' do
|
8
|
+
describe '#from_xml' do
|
9
|
+
it 'parses an XML string' do
|
10
|
+
|
11
|
+
xml = File.read('spec/data/examples/example-27.xml')
|
12
|
+
list = ChangeList.load_from_xml(XML.element(xml))
|
13
|
+
|
14
|
+
links = list.links
|
15
|
+
expect(links.size).to eq(1)
|
16
|
+
ln0 = links[0]
|
17
|
+
expect(ln0.rel).to eq('up')
|
18
|
+
expect(ln0.uri).to eq(URI('http://example.com/dataset1/capabilitylist.xml'))
|
19
|
+
|
20
|
+
md = list.metadata
|
21
|
+
expect(md.capability).to eq('changelist')
|
22
|
+
expect(md.from_time).to be_time(Time.utc(2013, 1, 3))
|
23
|
+
|
24
|
+
urls = list.resources
|
25
|
+
expect(urls.size).to eq(2)
|
26
|
+
|
27
|
+
url0 = urls[0]
|
28
|
+
expect(url0.uri).to eq(URI('http://example.com/res4'))
|
29
|
+
expect(url0.modified_time).to be_time(Time.utc(2013, 1, 3, 17))
|
30
|
+
md0 = url0.metadata
|
31
|
+
expect(md0.change).to be(Types::Change::UPDATED)
|
32
|
+
expect(md0.hashes).to eq('sha-256' => 'f4OxZX_x_DFGFDgghgdfb6rtSx-iosjf6735432nklj')
|
33
|
+
expect(md0.length).to eq(56_778)
|
34
|
+
expect(md0.mime_type).to be_mime_type('application/json')
|
35
|
+
lns0 = url0.links
|
36
|
+
expect(lns0.size).to eq(1)
|
37
|
+
ln0 = lns0[0]
|
38
|
+
expect(ln0.rel).to(eq('http://www.openarchives.org/rs/terms/patch'))
|
39
|
+
expect(ln0.uri).to(eq(URI('http://example.com/res4-json-patch')))
|
40
|
+
expect(ln0.modified_time).to(eq(Time.utc(2013, 1, 3, 17)))
|
41
|
+
expect(ln0.hashes).to(eq('sha-256' => 'y66dER_t_HWEIKpesdkeb7rtSc-ippjf9823742opld'))
|
42
|
+
expect(ln0.length).to(eq(73))
|
43
|
+
expect(ln0.mime_type).to(be_mime_type('application/json-patch'))
|
44
|
+
|
45
|
+
url1 = urls[1]
|
46
|
+
expect(url1.uri).to eq(URI('http://example.com/res5-full.tiff'))
|
47
|
+
expect(url1.modified_time).to be_time(Time.utc(2013, 1, 3, 18))
|
48
|
+
md1 = url1.metadata
|
49
|
+
expect(md1.change).to be(Types::Change::UPDATED)
|
50
|
+
expect(md1.hashes).to eq('sha-256' => 'f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk')
|
51
|
+
expect(md1.length).to eq(9_788_456_778)
|
52
|
+
expect(md1.mime_type).to be_mime_type('image/tiff')
|
53
|
+
lns1 = url1.links
|
54
|
+
expect(lns1.size).to eq(1)
|
55
|
+
ln1 = lns1[0]
|
56
|
+
expect(ln1.rel).to(eq('http://www.openarchives.org/rs/terms/patch'))
|
57
|
+
expect(ln1.uri).to(eq(URI('http://example.com/res5-diff')))
|
58
|
+
expect(ln1.modified_time).to(eq(Time.utc(2013, 1, 3, 18)))
|
59
|
+
expect(ln1.hashes).to(eq('sha-256' => 'h986gT_t_87HTkjHYE76G558hY-jdfgy76t55sadJUYT'))
|
60
|
+
expect(ln1.length).to(eq(4533))
|
61
|
+
expect(ln1.mime_type).to(be_mime_type('application/x-tiff-diff'))
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '#save_to_xml' do
|
66
|
+
it 'can round-trip to XML' do
|
67
|
+
data = File.read('spec/data/examples/example-27.xml')
|
68
|
+
list = ChangeList.load_from_xml(XML.element(data))
|
69
|
+
xml = list.save_to_xml
|
70
|
+
expect(xml).to be_xml(data)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require_relative 'shared/uri_field_examples'
|
3
|
+
require_relative 'shared/descriptor_examples'
|
4
|
+
|
5
|
+
module Resync
|
6
|
+
describe Link do
|
7
|
+
|
8
|
+
def required_arguments
|
9
|
+
{ rel: 'describedby', uri: 'http://example.org' }
|
10
|
+
end
|
11
|
+
|
12
|
+
it_behaves_like Descriptor
|
13
|
+
|
14
|
+
describe '#new' do
|
15
|
+
|
16
|
+
describe 'rel' do
|
17
|
+
it 'accepts a relation' do
|
18
|
+
rel = 'describedby'
|
19
|
+
link = Link.new(rel: rel, uri: 'http://example.org')
|
20
|
+
expect(link.rel).to eq(rel)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'requires a relation' do
|
24
|
+
expect { Link.new(uri: 'http://example.org') }.to raise_error(ArgumentError)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe 'uri' do
|
29
|
+
it_behaves_like 'a URI field'
|
30
|
+
end
|
31
|
+
|
32
|
+
describe 'priority' do
|
33
|
+
it 'accepts a priority' do
|
34
|
+
priority = 1.234
|
35
|
+
link = Link.new(rel: 'describedby', uri: 'http://example.org', priority: priority)
|
36
|
+
expect(link.priority).to eq(priority)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'defaults to nil if no priority specified' do
|
40
|
+
link = Link.new(rel: 'describedby', uri: 'http://example.org')
|
41
|
+
expect(link.priority).to be_nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
describe 'XML conversion' do
|
48
|
+
describe '#from_xml' do
|
49
|
+
it 'parses an XML string' do
|
50
|
+
xml = '<ln
|
51
|
+
encoding="utf-8"
|
52
|
+
hash="md5:1e0d5cb8ef6ba40c99b14c0237be735e"
|
53
|
+
href="http://example.org/"
|
54
|
+
length="12345"
|
55
|
+
modified="2013-01-03T09:00:00Z"
|
56
|
+
path="/foo/"
|
57
|
+
pri="3.14159"
|
58
|
+
rel="bar"
|
59
|
+
type="baz/qux"
|
60
|
+
/>'
|
61
|
+
link = Link.load_from_xml(XML.element(xml))
|
62
|
+
expect(link).to be_a(Link)
|
63
|
+
expect(link.rel).to eq('bar')
|
64
|
+
expect(link.uri).to eq(URI('http://example.org'))
|
65
|
+
expect(link.priority).to eq(3.14159)
|
66
|
+
expect(link.modified_time).to be_time(Time.utc(2013, 1, 3, 9))
|
67
|
+
expect(link.length).to eq(12_345)
|
68
|
+
expect(link.mime_type).to be_mime_type('baz/qux')
|
69
|
+
expect(link.hash('md5')).to eq('1e0d5cb8ef6ba40c99b14c0237be735e')
|
70
|
+
expect(link.path).to eq('/foo/')
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'can round-trip to XML' do
|
75
|
+
data = '<ln
|
76
|
+
encoding="utf-8"
|
77
|
+
hash="md5:1e0d5cb8ef6ba40c99b14c0237be735e"
|
78
|
+
href="http://example.org/"
|
79
|
+
length="12345"
|
80
|
+
modified="2013-01-03T09:00:00Z"
|
81
|
+
path="/foo/"
|
82
|
+
pri="3.14159"
|
83
|
+
rel="bar"
|
84
|
+
type="baz/qux"
|
85
|
+
/>'
|
86
|
+
link = Link.load_from_xml(XML.element(data))
|
87
|
+
xml = link.save_to_xml
|
88
|
+
expect(xml).to be_xml(data)
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require_relative 'shared/descriptor_examples'
|
3
|
+
|
4
|
+
module Resync
|
5
|
+
describe Metadata do
|
6
|
+
|
7
|
+
it_behaves_like Descriptor
|
8
|
+
|
9
|
+
describe '#new' do
|
10
|
+
|
11
|
+
describe 'timestamps' do
|
12
|
+
|
13
|
+
# TODO: Find a way to share the examples for the different time attributes
|
14
|
+
it 'accepts an at timestamp' do
|
15
|
+
at_time = Time.utc(1997, 7, 16, 19, 20, 30.45)
|
16
|
+
metadata = Metadata.new(at_time: at_time)
|
17
|
+
expect(metadata.at_time).to be_time(at_time)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'defaults to nil if no at timestamp is specified' do
|
21
|
+
metadata = Metadata.new
|
22
|
+
expect(metadata.at_time).to be_nil
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'fails if the at timestamp is not a time' do
|
26
|
+
expect { Metadata.new(at_time: '12:45 pm') }.to raise_error(ArgumentError)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'accepts a from timestamp' do
|
30
|
+
from_time = Time.utc(1997, 7, 16, 19, 20, 30.45)
|
31
|
+
metadata = Metadata.new(from_time: from_time)
|
32
|
+
expect(metadata.from_time).to be_time(from_time)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'defaults to nil if no from timestamp is specified' do
|
36
|
+
metadata = Metadata.new
|
37
|
+
expect(metadata.from_time).to be_nil
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'fails if the from timestamp is not a time' do
|
41
|
+
expect { Metadata.new(from_time: '12:45 pm') }.to raise_error(ArgumentError)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'accepts an until timestamp' do
|
45
|
+
until_time = Time.utc(1997, 7, 16, 19, 20, 30.45)
|
46
|
+
metadata = Metadata.new(until_time: until_time)
|
47
|
+
expect(metadata.until_time).to be_time(until_time)
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'defaults to nil if no until timestamp is specified' do
|
51
|
+
metadata = Metadata.new
|
52
|
+
expect(metadata.until_time).to be_nil
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'fails if the until timestamp is not a time' do
|
56
|
+
expect { Metadata.new(until_time: '12:45 pm') }.to raise_error(ArgumentError)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'accepts a completed timestamp' do
|
60
|
+
completed_time = Time.utc(1997, 7, 16, 19, 20, 30.45)
|
61
|
+
metadata = Metadata.new(completed_time: completed_time)
|
62
|
+
expect(metadata.completed_time).to be_time(completed_time)
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'defaults to nil if no completed timestamp is specified' do
|
66
|
+
metadata = Metadata.new
|
67
|
+
expect(metadata.completed_time).to be_nil
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'fails if the completed timestamp is not a time' do
|
71
|
+
expect { Metadata.new(completed_time: '12:45 pm') }.to raise_error(ArgumentError)
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
describe 'capability' do
|
77
|
+
it 'accepts a capability' do
|
78
|
+
cap = 'resourcelist'
|
79
|
+
metadata = Metadata.new(capability: cap)
|
80
|
+
expect(metadata.capability).to eq(cap)
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'defaults to nil if no capability specified' do
|
84
|
+
metadata = Metadata.new
|
85
|
+
expect(metadata.capability).to be_nil
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe 'change' do
|
90
|
+
it 'accepts a change' do
|
91
|
+
change = Types::Change::DELETED
|
92
|
+
metadata = Metadata.new(change: change)
|
93
|
+
expect(metadata.change).to eq(change)
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'defaults to nil if no change specified' do
|
97
|
+
metadata = Metadata.new
|
98
|
+
expect(metadata.change).to be_nil
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe 'XML conversion' do
|
104
|
+
describe '#from_xml' do
|
105
|
+
it 'parses an XML string' do
|
106
|
+
xml = '<md
|
107
|
+
at="2001-01-01T01:00:00Z"
|
108
|
+
capability="resourcelist"
|
109
|
+
change="updated"
|
110
|
+
completed="2002-02-02T02:00:00Z"
|
111
|
+
encoding="utf-16"
|
112
|
+
from="2003-03-03T03:00:00Z"
|
113
|
+
hash="md5:1e0d5cb8ef6ba40c99b14c0237be735e"
|
114
|
+
length="54321"
|
115
|
+
modified="2004-04-04T04:00:00Z"
|
116
|
+
path="/foo"
|
117
|
+
type="bar/baz"
|
118
|
+
until="2005-05-05T05:00:00Z"
|
119
|
+
/>'
|
120
|
+
metadata = Metadata.load_from_xml(XML.element(xml))
|
121
|
+
expect(metadata).to be_a(Metadata)
|
122
|
+
expect(metadata.at_time).to be_time(Time.utc(2001, 1, 1, 1))
|
123
|
+
expect(metadata.capability).to eq('resourcelist')
|
124
|
+
expect(metadata.change).to eq(Types::Change::UPDATED)
|
125
|
+
expect(metadata.completed_time).to be_time(Time.utc(2002, 2, 2, 2))
|
126
|
+
expect(metadata.encoding).to eq('utf-16')
|
127
|
+
expect(metadata.from_time).to be_time(Time.utc(2003, 3, 3, 3))
|
128
|
+
expect(metadata.hash('md5')).to eq('1e0d5cb8ef6ba40c99b14c0237be735e')
|
129
|
+
expect(metadata.length).to eq(54_321)
|
130
|
+
expect(metadata.modified_time).to be_time(Time.utc(2004, 4, 4, 4))
|
131
|
+
expect(metadata.path).to eq('/foo')
|
132
|
+
expect(metadata.mime_type).to be_mime_type('bar/baz')
|
133
|
+
expect(metadata.until_time).to be_time(Time.utc(2005, 5, 5, 5))
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
describe '#save_to_xml' do
|
138
|
+
it 'can round-trip to XML' do
|
139
|
+
data = '<md
|
140
|
+
at="2001-01-01T01:00:00Z"
|
141
|
+
capability="resourcelist"
|
142
|
+
change="updated"
|
143
|
+
completed="2002-02-02T02:00:00Z"
|
144
|
+
encoding="utf-16"
|
145
|
+
from="2003-03-03T03:00:00Z"
|
146
|
+
hash="md5:1e0d5cb8ef6ba40c99b14c0237be735e"
|
147
|
+
length="54321"
|
148
|
+
modified="2004-04-04T04:00:00Z"
|
149
|
+
path="/foo"
|
150
|
+
type="bar/baz"
|
151
|
+
until="2005-05-05T05:00:00Z"
|
152
|
+
/>'
|
153
|
+
md = Metadata.load_from_xml(XML.element(data))
|
154
|
+
xml = md.save_to_xml
|
155
|
+
expect(xml).to be_xml(data)
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'doesn\'t have side effects that prevent <sitemapindex> parsing' do
|
159
|
+
Metadata.xml_mapping_nodes(mapping: :sitemapindex)
|
160
|
+
|
161
|
+
data = File.read('spec/data/examples/example-8.xml')
|
162
|
+
root = REXML::Document.new(data).root
|
163
|
+
sitemapindex = XMLParser.parse(root)
|
164
|
+
expect(sitemapindex).to be_a(ResourceListIndex)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|