resync 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +42 -0
  3. data/.rubocop.yml +23 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +2 -0
  6. data/Gemfile +3 -0
  7. data/LICENSE.md +22 -0
  8. data/README.md +92 -0
  9. data/Rakefile +56 -0
  10. data/example.rb +100 -0
  11. data/lib/resync/capability_list.rb +85 -0
  12. data/lib/resync/change_dump.rb +15 -0
  13. data/lib/resync/change_dump_manifest.rb +15 -0
  14. data/lib/resync/change_list.rb +15 -0
  15. data/lib/resync/change_list_index.rb +26 -0
  16. data/lib/resync/link.rb +87 -0
  17. data/lib/resync/metadata.rb +112 -0
  18. data/lib/resync/resource.rb +72 -0
  19. data/lib/resync/resource_dump.rb +15 -0
  20. data/lib/resync/resource_dump_manifest.rb +15 -0
  21. data/lib/resync/resource_list.rb +15 -0
  22. data/lib/resync/resource_list_index.rb +15 -0
  23. data/lib/resync/shared/augmented.rb +76 -0
  24. data/lib/resync/shared/base_resource_list.rb +117 -0
  25. data/lib/resync/shared/descriptor.rb +135 -0
  26. data/lib/resync/shared/sitemap_index.rb +32 -0
  27. data/lib/resync/shared/sorted_resource_list.rb +60 -0
  28. data/lib/resync/source_description.rb +14 -0
  29. data/lib/resync/types/change.rb +14 -0
  30. data/lib/resync/types/change_frequency.rb +18 -0
  31. data/lib/resync/types.rb +6 -0
  32. data/lib/resync/version.rb +4 -0
  33. data/lib/resync/xml.rb +216 -0
  34. data/lib/resync/xml_parser.rb +65 -0
  35. data/lib/resync.rb +4 -0
  36. data/resync.gemspec +36 -0
  37. data/spec/acceptance/xml_parser_spec.rb +1049 -0
  38. data/spec/data/examples/README.md +1 -0
  39. data/spec/data/examples/example-1.xml +12 -0
  40. data/spec/data/examples/example-12.xml +25 -0
  41. data/spec/data/examples/example-13.xml +25 -0
  42. data/spec/data/examples/example-14.xml +23 -0
  43. data/spec/data/examples/example-15.xml +21 -0
  44. data/spec/data/examples/example-16.xml +24 -0
  45. data/spec/data/examples/example-17.xml +39 -0
  46. data/spec/data/examples/example-18.xml +25 -0
  47. data/spec/data/examples/example-19.xml +28 -0
  48. data/spec/data/examples/example-2.xml +18 -0
  49. data/spec/data/examples/example-20.xml +22 -0
  50. data/spec/data/examples/example-21.xml +31 -0
  51. data/spec/data/examples/example-22.xml +41 -0
  52. data/spec/data/examples/example-23.xml +41 -0
  53. data/spec/data/examples/example-24.xml +28 -0
  54. data/spec/data/examples/example-25.xml +21 -0
  55. data/spec/data/examples/example-26.xml +18 -0
  56. data/spec/data/examples/example-27.xml +36 -0
  57. data/spec/data/examples/example-28.xml +34 -0
  58. data/spec/data/examples/example-29.xml +27 -0
  59. data/spec/data/examples/example-3.xml +17 -0
  60. data/spec/data/examples/example-30.xml +18 -0
  61. data/spec/data/examples/example-31.xml +16 -0
  62. data/spec/data/examples/example-32.xml +22 -0
  63. data/spec/data/examples/example-33.xml +22 -0
  64. data/spec/data/examples/example-4.xml +10 -0
  65. data/spec/data/examples/example-5.xml +18 -0
  66. data/spec/data/examples/example-6.xml +21 -0
  67. data/spec/data/examples/example-7.xml +13 -0
  68. data/spec/data/examples/example-8.xml +12 -0
  69. data/spec/data/resourcesync.xsd +148 -0
  70. data/spec/data/siteindex.xsd +75 -0
  71. data/spec/data/sitemap.xsd +116 -0
  72. data/spec/rspec_custom_matchers.rb +89 -0
  73. data/spec/spec_helper.rb +31 -0
  74. data/spec/todo.rb +11 -0
  75. data/spec/unit/resync/capability_list_spec.rb +138 -0
  76. data/spec/unit/resync/change_dump_manifest_spec.rb +75 -0
  77. data/spec/unit/resync/change_dump_spec.rb +61 -0
  78. data/spec/unit/resync/change_list_index_spec.rb +49 -0
  79. data/spec/unit/resync/change_list_spec.rb +75 -0
  80. data/spec/unit/resync/link_spec.rb +93 -0
  81. data/spec/unit/resync/metadata_spec.rb +169 -0
  82. data/spec/unit/resync/resource_dump_manifest_spec.rb +59 -0
  83. data/spec/unit/resync/resource_dump_spec.rb +62 -0
  84. data/spec/unit/resync/resource_list_index_spec.rb +53 -0
  85. data/spec/unit/resync/resource_list_spec.rb +60 -0
  86. data/spec/unit/resync/resource_spec.rb +176 -0
  87. data/spec/unit/resync/shared/augmented_examples.rb +58 -0
  88. data/spec/unit/resync/shared/base_resource_list_examples.rb +103 -0
  89. data/spec/unit/resync/shared/descriptor_examples.rb +122 -0
  90. data/spec/unit/resync/shared/descriptor_spec.rb +33 -0
  91. data/spec/unit/resync/shared/sorted_list_examples.rb +134 -0
  92. data/spec/unit/resync/shared/uri_field_examples.rb +36 -0
  93. data/spec/unit/resync/source_description_spec.rb +55 -0
  94. data/spec/unit/resync/xml/timenode_spec.rb +48 -0
  95. data/spec/unit/resync/xml/xml_spec.rb +40 -0
  96. data/spec/unit/resync/xml_parser_spec.rb +82 -0
  97. 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