cul-fedora-arm 0.5.1

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.
@@ -0,0 +1,353 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+ require 'tempfile'
4
+ require 'stringio'
5
+ module Cul
6
+ module Fedora
7
+ module Image
8
+ WIDTH_TEMPLATE = '<si-basic:imageWidth>%s</si-basic:imageWidth>'
9
+ LENGTH_TEMPLATE = '<si-basic:imageLength>%s</si-basic:imageLength>'
10
+ XSAMPLING_TEMPLATE = '<si-assess:xSamplingFrequency>%s</si-assess:xSamplingFrequency>'
11
+ YSAMPLING_TEMPLATE = '<si-assess:ySamplingFrequency>%s</si-assess:ySamplingFrequency>'
12
+ SAMPLINGUNIT_CM = '<si-assess:samplingFrequencyUnit rdf:resource="http://purl.oclc.org/NET/CUL/RESOURCE/STILLIMAGE/ASSESSMENT/CentimeterSampling" />'
13
+ SAMPLINGUNIT_IN = '<si-assess:samplingFrequencyUnit rdf:resource="http://purl.oclc.org/NET/CUL/RESOURCE/STILLIMAGE/ASSESSMENT/InchSampling" />'
14
+ SAMPLINGUNIT_NA = '<si-assess:samplingFrequencyUnit rdf:resource="http://purl.oclc.org/NET/CUL/RESOURCE/STILLIMAGE/ASSESSMENT/NoAbsoluteSampling" />'
15
+ UNITS = {:inch => SAMPLINGUNIT_IN, :cm => SAMPLINGUNIT_CM}
16
+ SIZE_TEMPLATE = '<dcmi:extent>%s</dcmi:extent>'
17
+ # magic bytes
18
+ # 2 bytes signatures
19
+ BITMAP = [0x42,0x4d] # "BM"
20
+ JPEG = [0xff,0xd8]
21
+ # 4 byte signatures
22
+ TIFF_BE = [0x49,0x49,0x2A,0] # "II*\x00"
23
+ TIFF_LE = [0x4d,0x4d,0,0x2A] # "MM\x00*"
24
+ GIF = [0x47,0x49,0x46,0x38] # "GIF8"
25
+ # 8 byte signatures
26
+ PNG = [0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a]
27
+
28
+ def analyze_image(file_name, debug=false)
29
+ result = {}
30
+ file = nil
31
+ file_size = 0
32
+ begin
33
+ if (file_name.index("http://") == 0)
34
+ temp_file = Tempfile.new("image-download")
35
+ #download the url content, write to tempfile
36
+ file = temp_file
37
+ else
38
+ file = File.open(file_name,'rb')
39
+ end
40
+ result[:size] = file_size = File.size(file.path)
41
+ # get properties
42
+ header = []
43
+ 8.times {
44
+ header.push(file.getc())
45
+ }
46
+ case
47
+ when header[0..1].eql?(BITMAP):
48
+ file.rewind()
49
+ result.merge!(analyze_bitmap(file,debug))
50
+ when header[0..1].eql?(JPEG):
51
+ file.rewind()
52
+ result.merge!(analyze_jpeg(file,debug))
53
+ when header[0..3].eql?(TIFF_LE), header[0..3].eql?(TIFF_BE):
54
+ file.rewind()
55
+ result.merge!(analyze_tiff(file,debug))
56
+ when header[0..3].eql?(GIF):
57
+ file.rewind()
58
+ result.merge!(analyze_gif(file,debug))
59
+ when header.eql?(PNG):
60
+ file.rewind()
61
+ result.merge!(analyze_png(file,debug))
62
+ else
63
+ msg = ''
64
+ header.each {|c|
65
+ msg += c.to_s(16)
66
+ msg += ' '
67
+ }
68
+ puts "\nUnmatched header bytes: " + msg
69
+ end
70
+ ensure
71
+ file.close() if file
72
+ end
73
+ # return hash
74
+ result
75
+ end
76
+
77
+ def map_image_properties(att_hash)
78
+ result = []
79
+ if (att_hash.has_key?(:size))
80
+ result.push(sprintf(SIZE_TEMPLATE,att_hash[:size]))
81
+ end
82
+ if (att_hash.has_key?(:width))
83
+ result.push(sprintf(WIDTH_TEMPLATE,att_hash[:width]))
84
+ end
85
+ if (att_hash.has_key?(:length))
86
+ result.push(sprintf(LENGTH_TEMPLATE,att_hash[:length]))
87
+ end
88
+ if (att_hash.has_key?(:x_sampling))
89
+ result.push(sprintf(YSAMPLING_TEMPLATE,att_hash[:x_sampling]))
90
+ end
91
+ if (att_hash.has_key?(:y_sampling))
92
+ result.push(sprintf(YSAMPLING_TEMPLATE,att_hash[:y_sampling]))
93
+ end
94
+ if (att_hash.has_key?(:sampling_unit) and UNITS.has_key?(att_hash[:sampling_unit]))
95
+ result.push(UNITS[att_hash[:sampling_unit]])
96
+ end
97
+ result
98
+ end
99
+
100
+ protected
101
+
102
+ def analyze_gif(file,debug)
103
+ props = {}
104
+ header = file.read(13)
105
+ width = header[6...8].unpack('v')[0]
106
+ length = header[8...10].unpack('v')[0]
107
+ par = header[12]
108
+ props[:width] = width
109
+ props[:length] = length
110
+ props[:mime] = 'image/gif'
111
+ props
112
+ end
113
+
114
+ def analyze_png(file,debug)
115
+ result = {}
116
+ size = File.size(file.path)
117
+ file.read(8) # skip signature
118
+ while (file.pos < size - 1) do
119
+ len_bytes = file.read(4)
120
+ length = len_bytes.unpack('N')[0]
121
+ ctype = file.read(4)
122
+ case
123
+ when 'pHYs'.eql?(ctype):
124
+ val = file.read(length)
125
+ xsam = val[0..3].unpack('N')[0]
126
+ ysam = val[4..7].unpack('N')[0]
127
+ unit = val[8].unpack('C')
128
+ if (unit ==1)
129
+ result[:sampling_unit] = :cm
130
+ xsam = xsam/100
131
+ ysam = ysam/100
132
+ end
133
+ result[:x_sampling] = xsam
134
+ result[:y_sampling] = ysam
135
+ file.seek(4,IO::SEEK_CUR)
136
+ when 'IHDR'.eql?(ctype):
137
+ val = file.read(length)
138
+ result[:width] = val[0..3].unpack('N')[0]
139
+ result[:length] = val[4..7].unpack('N')[0]
140
+ file.seek(4,IO::SEEK_CUR)
141
+ else
142
+ file.seek(4,IO::SEEK_CUR)
143
+ end
144
+ end
145
+ result
146
+ end
147
+ def analyze_bitmap(file,debug)
148
+ result = {}
149
+ file.seek(0x12,IO::SEEK_CUR)
150
+ width = file.read(4).unpack('V')[0]
151
+ length = file.read(4).unpack('V')[0]
152
+ file.seek(0x0c,IO::SEEK_CUR)
153
+ xsam = file.read(4).unpack('V')[0]
154
+ ysam = file.read(4).unpack('V')[0]
155
+ xsam /= 100 # ppm -> ppc
156
+ ysam /= 100 # ppm -> ppc
157
+ result[:mime] = 'image/bmp'
158
+ result[:sampling_unit] = :cm
159
+ result[:x_sampling] = xsam
160
+ result[:y_sampling] = ysam
161
+ result[:width] = width
162
+ result[:length] = length
163
+ result
164
+ end
165
+ =begin
166
+ TIFF Format notes taken from http://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf
167
+ Header format:
168
+ Bytes 0-1: BOM. [0x49,0x49] = LittleEndian, [0x4d,0x4d] = BigEndian
169
+ Bytes 2-3: Format marker (42) in the byte order indicated previously
170
+ Bytes 4-7: Byte offset of the first IFD, relative to file beginning
171
+ IFD format:
172
+ Bytes 0-1: Number of 12-byte IFD Entries
173
+ [IFD Entries]
174
+ Bytes -4 - -1: Byte offset of next IFD, or 0 if none remain
175
+ IFD Entry Format:
176
+ Bytes 0-1: Tag
177
+ Bytes 2-3: Type
178
+ 1 = BYTE (unsigned 8-bit integer)
179
+ 2 = ASCII (8 bit byte containing 7-bit char code)
180
+ 3 = SHORT (16-bit unsigned integer)
181
+ 4 = LONG (32-bit unsigned integer)
182
+ 5 = RATIONAL (Two LONGs, first is numerator, second denominator)
183
+ Bytes 4-7: Num values of indicated Type
184
+ Bytes 8-11: Value offset
185
+ =end
186
+ def analyze_tiff(file,debug)
187
+ result = {:mime=>'image/tiff'}
188
+ littleEndian = [0x49,0x49].eql?(file.read(2))
189
+ file.seek(2,IO::SEEK_CUR)
190
+ nextIFD = littleEndian ? file.read(4).unpack('V')[0] : file.read(4).unpack('N')[0]
191
+ result.merge!(analyze_exif(file,nextIFD,littleEndian,debug))
192
+ result
193
+ end
194
+
195
+
196
+ JPEG_NO_PAYLOAD = (0xd0..0xd9)
197
+ JPEG_APP = (0xe0..0xef)
198
+ JPEG_VARIABLE_PAYLOAD = [0xc0,0xc2,0xc4,0xda,0xdb,0xfe]
199
+ def analyze_jpeg(file,debug)
200
+ result = {:mime => 'image/jpeg'}
201
+ while ((header = file.read(2)) and not "\xff\xd9".eql?(header))
202
+ case
203
+ when 0xdd.eql?(header[1]):
204
+ payload = nil
205
+ file.seek(2,IO::SEEK_CUR)
206
+ when JPEG_APP.member?(header[1]), JPEG_VARIABLE_PAYLOAD.member?(header[1]):
207
+ len = file.read(2).unpack('n')[0]
208
+ if ("\xff\xe0".eql?(header)): # APP0 segment - JFIF
209
+ puts "JFIF file segment detected" if debug
210
+ payload = file.read(len)
211
+ id = payload[0..4]
212
+ version = payload[5..6]
213
+ unit = payload[7]
214
+ x_sample = payload[8..9].unpack('n')[0]
215
+ y_sample = payload[10..11].unpack('n')[0]
216
+ result[:x_sampling] = x_sample
217
+ result[:y_sampling] = y_sample
218
+ if (unit == "\x01"): result[:sampling_unit] = :inch
219
+ elsif (unit == "\x02"): result[:sampling_unit] = :cm
220
+ end
221
+ elsif ("\xff\xe1".eql?(header)): # APP1 segment - EXIF
222
+ puts "EXIF file segment detected" if debug
223
+ payload = file.read(len)
224
+ result.merge!(analyze_exif(StringIO.new(payload),0,false,debug))
225
+ elsif (header[1] >= 0xc0 and header[1] <= 0xc3)
226
+ payload = file.read(len)
227
+ precision = payload[0]
228
+ length = payload[1..2].unpack('n')[0]
229
+ width = payload[3..4].unpack('n')[0]
230
+ result[:width] = width
231
+ result[:length] = length
232
+ else
233
+ file.seek(len,IO::SEEK_CUR)
234
+ end
235
+ else
236
+ payload = nil?
237
+ end
238
+ end
239
+ result
240
+ end
241
+
242
+ def analyze_exif(file,nextIFD,littleEndian,debug=false)
243
+ result = Hash.new()
244
+ until (nextIFD == 0) do
245
+ file.seek(nextIFD,IO::SEEK_SET)
246
+ bytes = file.read(2)
247
+ numEntries = littleEndian ? bytes.unpack('v')[0] : bytes.unpack('n')[0]
248
+ entries = Hash.new()
249
+ numEntries.times do
250
+ if (littleEndian)
251
+ tag = file.read(2).unpack('v')
252
+ ttype = file.read(2).unpack('v')[0]
253
+ numValues = file.read(4).unpack('V')[0]
254
+ valueOffsetBytes = file.read(4)
255
+ valueOffset = valueOffsetBytes.unpack('V')[0]
256
+ else
257
+ tag = file.read(2).unpack('n')[0]
258
+ ttype = file.read(2).unpack('n')[0]
259
+ numValues = file.read(4).unpack('N')[0]
260
+ valueOffsetBytes = file.read(4)
261
+ valueOffset = valueOffsetBytes.unpack('N')[0]
262
+ end
263
+ if (debug)
264
+ puts "\ntag : #{tag.to_s(16)} ttype: #{ttype.to_s(16)} numValues: #{numValues} valueOffset: #{valueOffset}"
265
+ end
266
+
267
+ nextEntry = file.tell()
268
+ values = []
269
+ if (1 <= ttype and ttype <= 5 and numValues > 0)
270
+ case
271
+ when ttype == 1: # unsigned bytes
272
+ if (numValues > 4)
273
+ file.seek(valueOffset,IO::SEEK_SET)
274
+ values = file.read(numValues)
275
+ else
276
+ values = valueOffsetBytes
277
+ end
278
+ values = values.unpack('C*')
279
+
280
+ when ttype == 2:
281
+ if (numValues > 4)
282
+ file.seek(valueOffset,IO::SEEK_SET)
283
+ values = file.read(numValues)
284
+ else
285
+ values = valueOffsetBytes
286
+ end
287
+ values = values.unpack('C*')
288
+ values.collect! {|c|
289
+ c.to_chr
290
+ }
291
+ when ttype == 3:
292
+ if (numValues > 2)
293
+ file.seek(valueOffset,IO::SEEK_SET)
294
+ values = file.read(numValues * 2)
295
+ else
296
+ values = valueOffsetBytes
297
+ end
298
+ values = littleEndian ? values.unpack('v*'):values.unpack('n*')
299
+ when ttype == 4:
300
+ if (numValues > 1)
301
+ file.seek(valueOffset,IO::SEEK_SET)
302
+ values = file.read(numValues * 4)
303
+ else
304
+ values = valueOffsetBytes
305
+ end
306
+ values = littleEndian ? values.unpack('V*'):values.unpack('N*')
307
+ when ttype == 5:
308
+ # RATIONAL: a sequence of pairs of 32-bit integers, numerator and denominator
309
+ file.seek(valueOffset,IO::SEEK_SET)
310
+ values = file.read(numValues * 8)
311
+ if(values.length % 8) != 0:
312
+ raise "Unexpected end of bytestream when reading EXIF data"
313
+ end
314
+ values = littleEndian ? values.unpack('V*'):values.unpack('N*')
315
+ values = (0...values.length).step(2).collect {|ix|
316
+ values[ix].quo(values[(ix)+1])
317
+ }
318
+ else
319
+ if debug: puts "Unknown tag type: #{ttype}"
320
+ end
321
+ end
322
+ entries[tag] = values
323
+ end
324
+ file.seek(nextEntry,IO::SEEK_SET)
325
+ end
326
+ nextIFD = littleEndian ? file.read(4).unpack('V')[0] : file.read(4).unpack('N')[0]
327
+ end
328
+ if (entries.has_key?(0x0100))
329
+ result[:width] = entries[0x0100][0]
330
+ end
331
+ if (entries.has_key?(0x0101))
332
+ result[:length] = entries[0x0101][0]
333
+ end
334
+ if (entries.has_key?(0x011a))
335
+ result[:x_sampling] = entries[0x011a][0]
336
+ end
337
+ if (entries.has_key?(0x011b))
338
+ result[:y_sampling] = entries[0x011b][0]
339
+ end
340
+ if (entries.has_key?(0x128))
341
+ unit_key = entries[0x128][0]
342
+ if (unit_key == 2)
343
+ result[:sampling_unit] = :inch
344
+ elsif (unit_key == 3)
345
+ result[:sampling_unit] = :cm
346
+ end
347
+ end
348
+ result
349
+ end
350
+
351
+ end
352
+ end
353
+ end
@@ -0,0 +1,372 @@
1
+ require 'test_helper'
2
+ require 'digest/md5'
3
+
4
+ class CulFedoraArmBuilderTest < Test::Unit::TestCase
5
+ TEST = 'test'
6
+ CASE1 = "#{TEST}/fixtures/case1"
7
+ CASE2 = "#{TEST}/fixtures/case2"
8
+ CASE3 = "#{TEST}/fixtures/case3"
9
+ CASE4 = "#{TEST}/fixtures/case4"
10
+
11
+ context "Given the 'test_config' fedora instance " do
12
+ setup do
13
+ @connector = Cul::Fedora::Connector.parse(YAML::load_file("private/fedora-config.yml"))["test_config"]
14
+ end
15
+
16
+ should "build a connector properly" do
17
+ assert_kind_of Cul::Fedora::Connector, @connector
18
+ end
19
+
20
+ context "The builder class" do
21
+ setup do
22
+ @builder_class = Cul::Fedora::Arm::Builder
23
+ end
24
+
25
+ should "have a blank read_only array of parts" do
26
+ @builder = @builder_class.new
27
+
28
+ assert_instance_of @builder_class, @builder
29
+ assert_equal [], @builder.parts
30
+ assert_raise NoMethodError do
31
+ @builder.parts = [:test]
32
+ end
33
+ end
34
+
35
+ should "have a default array of columns for templates without headers" do
36
+ assert_equal @builder_class::DEFAULT_TEMPLATE_HEADER, [:sequence, :target, :model_type, :source, :template_type, :dc_format, :id, :pid, :action, :license]
37
+ end
38
+
39
+
40
+ should "have a list of mandatory, valid, and required columns" do
41
+ assert_instance_of Array, @builder_class::REQUIRED_COLUMNS
42
+
43
+ assert_equal @builder_class::REQUIRED_COLUMNS, [:sequence]
44
+ assert_equal @builder_class::MANDATORY_COLUMNS, [:sequence, :target, :model_type]
45
+ assert_equal @builder_class::VALID_COLUMNS, [:sequence, :target, :model_type, :source, :template_type, :dc_format, :id, :pid, :action, :license]
46
+ end
47
+
48
+ should "accept only options :template or :file" do
49
+ assert_instance_of @builder_class, @builder_class.new(:template => nil)
50
+ assert_instance_of @builder_class, @builder_class.new(:file => nil)
51
+
52
+ assert_raise(ArgumentError) do
53
+ @builder_class.new(:template => nil, :invalid => true)
54
+ end
55
+
56
+ assert_raise(ArgumentError) do
57
+ @builder_class.new(:template => "test", :file => "test")
58
+ end
59
+
60
+ end
61
+
62
+ should "not accept :header without :template" do
63
+ assert_raise(ArgumentError) do
64
+ @builder_class.new(:header => true)
65
+ end
66
+ end
67
+
68
+
69
+ context "given a blank builder" do
70
+ setup do
71
+ @builder = @builder_class.new
72
+ end
73
+
74
+ should "have a blank array of parts" do
75
+ assert_equal @builder.parts, []
76
+ end
77
+
78
+ should "be able to add parts" do
79
+ @builder.add_part(:sequence => "0", :target => "collection:1;ac:5", :source => "/test-0001.xml", :model_type => "Metadata")
80
+ end
81
+
82
+ should "not add parts without a sequence" do
83
+ assert_raise RuntimeError, "Missing required values sequence" do
84
+ @builder.add_part(:target => "collection:1;ac:5", :source => "/test-0001.xml")
85
+ end
86
+ end
87
+
88
+ should "not add parts with the same sequence id" do
89
+
90
+ assert_raise(RuntimeError, "Sequence ID already taken") do
91
+ @builder.add_part(:sequence => "2", :source => "/test-0001.xml", :model_type => "Metadata")
92
+ @builder.add_part(:sequence => "2", :source => "/test-0002.xml", :model_type => "Metadata")
93
+ end
94
+
95
+ end
96
+ end
97
+
98
+ context "given headers for templates" do
99
+ setup do
100
+ @good_header = %w{sequence target model_type source template_type dc_format id}
101
+ @invalid_column = %w{sequence target model_type random}
102
+ @sequence_not_first = %w{target model_type sequence}
103
+ @missing_mandatory = %w{sequence target template_type}
104
+ end
105
+
106
+ should "accept good headers" do
107
+ assert_instance_of @builder_class, @builder_class.new(:template => template_builder(@good_header))
108
+ end
109
+
110
+ should "accept blank template" do
111
+ assert_nothing_raised do
112
+ @builder_class.new(:template => nil)
113
+ end
114
+ end
115
+
116
+ should "reject invalid column names" do
117
+ assert_raise RuntimeError, "Invalid column name: random" do
118
+ @builder_class.new(:template => template_builder(@invalid_column))
119
+ end
120
+ end
121
+
122
+ should "insist on all mandatory columns" do
123
+ assert_raise RuntimeError, "Missing mandatory column: metadata" do
124
+ @builder_class.new(:template => template_builder(@missing_mandatory))
125
+ end
126
+ end
127
+ end
128
+
129
+ context "with an example template with header" do
130
+ setup do
131
+ @builder = @builder_class.new(:file => "#{CASE1}/builder-template.txt")
132
+ @builder_via_template_option = @builder_class.new(:template => File.open("#{CASE1}/builder-template.txt", "r"))
133
+ @builder_no_header = @builder_class.new(:file => "#{CASE1}/builder-template-noheader.txt", :header => false)
134
+ end
135
+
136
+ should "load successfully" do
137
+ assert_instance_of @builder_class, @builder
138
+ assert_instance_of @builder_class, @builder_no_header
139
+ end
140
+
141
+ should "have equivalent results for header and no_header instances of the default column set" do
142
+ @builder.parts.each{|part|
143
+ assert_equal part, @builder_no_header.part_by_sequence(part[:sequence])
144
+ }
145
+ assert_equal @builder.parts.length, @builder_no_header.parts.length
146
+ end
147
+
148
+ should "have equivalent results for opening via template and file" do
149
+ assert_equal @builder.parts, @builder_via_template_option.parts
150
+ end
151
+
152
+
153
+ should "ignore the header row and load template data successfully" do
154
+ assert_equal @builder.parts.length, 6
155
+ assert_equal @builder.parts[2][:source], "/test-0001.xml"
156
+ assert_equal @builder.parts[3][:license], "license:by-nc-nd"
157
+ end
158
+
159
+ should "have parts accessible by sequence id" do
160
+ assert_kind_of Hash, @builder.part_by_sequence("6")
161
+ assert_equal @builder.parts[4], @builder.part_by_sequence("6")
162
+ end
163
+ end
164
+
165
+ context "with an example template with header, and fedora credentials" do
166
+ setup do
167
+ args = {:file => "#{CASE1}/builder-template.txt", :connector => @connector}
168
+ @builder = @builder_class.new(args)
169
+ @builder_via_template_option = @builder_class.new(:template => File.open("#{CASE1}/builder-template.txt", "r"))
170
+ @builder_no_header = @builder_class.new(:file => "#{CASE1}/builder-template-noheader.txt", :header => false)
171
+ end
172
+
173
+ should "load successfully" do
174
+ assert_instance_of @builder_class, @builder
175
+ assert_instance_of @builder_class, @builder_no_header
176
+ end
177
+
178
+ should "have equivalent results for header and no_header instances of the default column set" do
179
+ @builder.parts.each{|part|
180
+ assert_equal part, @builder_no_header.part_by_sequence(part[:sequence])
181
+ }
182
+ assert_equal @builder.parts.length, @builder_no_header.parts.length
183
+ end
184
+
185
+ should "have equivalent results for opening via template and file" do
186
+ assert_equal @builder.parts, @builder_via_template_option.parts
187
+ end
188
+
189
+
190
+ should "ignore the header row and load template data successfully" do
191
+ assert_equal @builder.parts.length, 6
192
+ assert_equal @builder.parts[2][:source], "/test-0001.xml"
193
+ assert_equal @builder.parts[3][:license], "license:by-nc-nd"
194
+ end
195
+
196
+ should "have parts accessible by sequence id" do
197
+ assert_kind_of Hash, @builder.part_by_sequence("6")
198
+ assert_equal @builder.parts[4], @builder.part_by_sequence("6")
199
+ end
200
+
201
+ should "be able to procure PIDs for parts in sequence" do
202
+ @builder.reserve_pids()
203
+ @builder.parts.each { |part|
204
+ assert part.has_key?(:pid)
205
+ assert !(part[:pid].empty?)
206
+ assert_match /^\w+:\d+$/, part[:pid]
207
+ targets = part[:target].split(';')
208
+ targets.each {|target|
209
+ assert_no_match /^\d+$/, target
210
+ }
211
+ }
212
+ end
213
+ end
214
+ context "with example input with header for two inserted aggregators, and fedora credentials" do
215
+ setup do
216
+ args = {:file => "#{CASE2}/builder-template.txt",:ns=>'test', :connector => @connector}
217
+ @builder = @builder_class.new(args)
218
+ @builder_via_template_option = @builder_class.new(:template => File.open("#{CASE1}/builder-template.txt", "r"))
219
+ end
220
+ should "be able to ingest aggregators into repository" do
221
+ # do ingest
222
+ @assigned = @builder.reserve_pids()
223
+ if (@assigned)
224
+ @assigned.each {|pid|
225
+ assert @builder.part_by_pid(pid), "Could not find part assigned to #{pid}"
226
+ }
227
+ end
228
+ response = @builder.process_parts()
229
+
230
+ # get objects, verify properties
231
+ if (@assigned)
232
+ host, port = @connector.config_for(:rest,:host),@connector.config_for(:rest, :port)
233
+ http = Net::HTTP.start(host, port)
234
+
235
+ @connector.rest_interface do |http|
236
+ @assigned.each { |pid|
237
+ resp = http.head("/fedora/get/#{pid}/DC")
238
+ assert_equal "200", resp.code, "#{pid} not loaded correctly to repo at #{host}:#{port}... #{resp.code} #{resp.message} "
239
+ }
240
+ end
241
+ end
242
+ end
243
+ teardown do
244
+ # purge objects
245
+ if (@assigned)
246
+ @assigned.each {|pid|
247
+ begin
248
+ @builder.purge(pid)
249
+ rescue Exception=>e
250
+ puts "Error purging #{pid}: #{e}"
251
+ end
252
+ }
253
+ end
254
+ end
255
+ end
256
+ context "with example input with header for an inserted metadata, and fedora credentials" do
257
+ setup do
258
+ infile_path = "#{CASE4}/builder-template.txt"
259
+ b = binding
260
+ template = File.open(infile_path) {|f|
261
+ f.readlines().collect{ |line|
262
+ line.gsub(/\#\{(\w+)\}/) {|match| eval($1, b)}
263
+ }
264
+ }
265
+ args = {:template => template,:ns=>'test', :connector => @connector}
266
+ @builder = @builder_class.new(args)
267
+ @builder_via_template_option = @builder_class.new(:template => File.open("#{CASE4}/builder-template.txt", "r"))
268
+ end
269
+ should "be able to ingest metadata objects into repository" do
270
+ # do ingest
271
+ @assigned = @builder.reserve_pids()
272
+ if (@assigned)
273
+ @assigned.each {|pid|
274
+ assert @builder.part_by_pid(pid), "Could not find part assigned to #{pid}"
275
+ }
276
+ end
277
+ response = @builder.process_parts()
278
+
279
+ # get objects, verify properties
280
+ if (@assigned)
281
+ @connector.rest_interface do |http|
282
+ @assigned.each { |pid|
283
+ resp = http.get("/fedora/get/#{pid}/CONTENT")
284
+ assert_equal "200", resp.code, "#{pid} not loaded correctly to repo at #{@connector.rest_location}... #{resp.code} #{resp.message} "
285
+ eTitle = "<title>test record title</title>"
286
+ assert resp.body.index(eTitle), "did not find expected mods title tag #{eTitle} in #{pid}/CONTENT"
287
+ resp = http.get("/fedora/get/#{pid}/DC")
288
+ assert_equal "200", resp.code, "#{pid} not loaded correctly to repo at #{@connector.rest_location}... #{resp.code} #{resp.message} "
289
+ eTitle = "<dc:title>test record title</dc:title>"
290
+ assert resp.body.index(eTitle), "did not find expected title tag #{eTitle} in #{pid}/DC"
291
+ }
292
+ end
293
+ end
294
+ end
295
+ teardown do
296
+ # purge objects
297
+ if (@assigned)
298
+ @assigned.each {|pid|
299
+ begin
300
+ @builder.purge(pid)
301
+ rescue Exception=>e
302
+ puts "Error purging #{pid}: #{e}"
303
+ end
304
+ }
305
+ end
306
+ end
307
+ end
308
+ context "with example input with header for an inserted resource, and fedora credentials" do
309
+ setup do
310
+ infile_path = "#{CASE3}/builder-template.txt"
311
+ b = binding
312
+ template = File.open(infile_path) {|f|
313
+ f.readlines().collect{ |line|
314
+ line.gsub(/\#\{(\w+)\}/) {|match| eval($1, b)}
315
+ }
316
+ }
317
+ args = {:template => template,:ns=>'test', :connector => @connector}
318
+ @builder = @builder_class.new(args)
319
+ @builder_via_template_option = @builder_class.new(:template => File.open("#{CASE3}/builder-template.txt", "r"))
320
+ end
321
+ should "be able to ingest resource objects into repository" do
322
+ # do ingest
323
+ @assigned = @builder.reserve_pids()
324
+ if (@assigned)
325
+ @assigned.each {|pid|
326
+ assert @builder.part_by_pid(pid), "Could not find part assigned to #{pid}"
327
+ }
328
+ end
329
+ response = @builder.process_parts()
330
+
331
+ # get objects, verify properties
332
+ if (@assigned)
333
+ @connector.rest_interface do |http|
334
+ @assigned.each { |pid|
335
+ resp = http.head("/fedora/get/#{pid}/DC")
336
+ assert_equal "200", resp.code, "#{pid}/DC not loaded correctly to repo at #{@connector.rest_location}... #{resp.code} #{resp.message} "
337
+ resp = http.get("/fedora/get/#{pid}/CONTENT")
338
+ assert_equal "200", resp.code, "#{pid}/CONTENT not loaded correctly to repo at #{@connector.rest_location}... #{resp.code} #{resp.message} "
339
+ actual = resp.body
340
+ begin
341
+ expected = File.open(@builder.part_by_pid(pid)[:source],'rb')
342
+ a_digest = Digest::MD5.hexdigest(actual)
343
+ e_digest = Digest::MD5.hexdigest(expected.read())
344
+ assert_equal e_digest, a_digest
345
+ ensure
346
+ expected.close()
347
+ end
348
+ resp = http.get("/fedora/get/#{pid}/RELS-EXT")
349
+ assert_equal "200", resp.code, "#{pid}/RELS-EXT not loaded correctly to repo at #{@connector.rest_location}... #{resp.code} #{resp.message} "
350
+ rels = resp.body
351
+ assert rels.index("<dcmi:extent>15138</dcmi:extent>") > -1
352
+ http.finish()
353
+ }
354
+ end
355
+ end
356
+ end
357
+ teardown do
358
+ # purge objects
359
+ if (@assigned)
360
+ @assigned.each {|pid|
361
+ begin
362
+ @builder.purge(pid)
363
+ rescue Exception=>e
364
+ puts "Error purging #{pid}: #{e}"
365
+ end
366
+ }
367
+ end
368
+ end
369
+ end
370
+ end
371
+ end
372
+ end