resync 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.
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