berkeley_library-tind 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (162) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/build.yml +18 -0
  3. data/.gitignore +388 -0
  4. data/.idea/inspectionProfiles/Project_Default.xml +20 -0
  5. data/.idea/misc.xml +4 -0
  6. data/.idea/modules.xml +8 -0
  7. data/.idea/tind.iml +138 -0
  8. data/.idea/vcs.xml +6 -0
  9. data/.rubocop.yml +334 -0
  10. data/.ruby-version +1 -0
  11. data/.simplecov +8 -0
  12. data/.yardopts +1 -0
  13. data/CHANGES.md +58 -0
  14. data/Dockerfile +57 -0
  15. data/Gemfile +3 -0
  16. data/Jenkinsfile +18 -0
  17. data/LICENSE.md +21 -0
  18. data/README.md +73 -0
  19. data/Rakefile +20 -0
  20. data/berkeley_library-tind.gemspec +50 -0
  21. data/bin/tind-export +14 -0
  22. data/docker-compose.yml +15 -0
  23. data/lib/berkeley_library/tind.rb +3 -0
  24. data/lib/berkeley_library/tind/api.rb +1 -0
  25. data/lib/berkeley_library/tind/api/api.rb +132 -0
  26. data/lib/berkeley_library/tind/api/api_exception.rb +131 -0
  27. data/lib/berkeley_library/tind/api/collection.rb +82 -0
  28. data/lib/berkeley_library/tind/api/date_range.rb +67 -0
  29. data/lib/berkeley_library/tind/api/format.rb +32 -0
  30. data/lib/berkeley_library/tind/api/search.rb +100 -0
  31. data/lib/berkeley_library/tind/config.rb +103 -0
  32. data/lib/berkeley_library/tind/export.rb +1 -0
  33. data/lib/berkeley_library/tind/export/column.rb +54 -0
  34. data/lib/berkeley_library/tind/export/column_group.rb +144 -0
  35. data/lib/berkeley_library/tind/export/column_group_list.rb +131 -0
  36. data/lib/berkeley_library/tind/export/column_width_calculator.rb +76 -0
  37. data/lib/berkeley_library/tind/export/config.rb +154 -0
  38. data/lib/berkeley_library/tind/export/csv_exporter.rb +29 -0
  39. data/lib/berkeley_library/tind/export/export.rb +47 -0
  40. data/lib/berkeley_library/tind/export/export_command.rb +168 -0
  41. data/lib/berkeley_library/tind/export/export_exception.rb +8 -0
  42. data/lib/berkeley_library/tind/export/export_format.rb +67 -0
  43. data/lib/berkeley_library/tind/export/exporter.rb +105 -0
  44. data/lib/berkeley_library/tind/export/filter.rb +52 -0
  45. data/lib/berkeley_library/tind/export/no_results_error.rb +7 -0
  46. data/lib/berkeley_library/tind/export/ods_exporter.rb +138 -0
  47. data/lib/berkeley_library/tind/export/row.rb +24 -0
  48. data/lib/berkeley_library/tind/export/row_metrics.rb +18 -0
  49. data/lib/berkeley_library/tind/export/table.rb +175 -0
  50. data/lib/berkeley_library/tind/export/table_metrics.rb +116 -0
  51. data/lib/berkeley_library/tind/marc.rb +1 -0
  52. data/lib/berkeley_library/tind/marc/xml_reader.rb +144 -0
  53. data/lib/berkeley_library/tind/module_info.rb +14 -0
  54. data/lib/berkeley_library/util/arrays.rb +178 -0
  55. data/lib/berkeley_library/util/logging.rb +1 -0
  56. data/lib/berkeley_library/util/ods/spreadsheet.rb +170 -0
  57. data/lib/berkeley_library/util/ods/xml/content_doc.rb +26 -0
  58. data/lib/berkeley_library/util/ods/xml/document_node.rb +57 -0
  59. data/lib/berkeley_library/util/ods/xml/element_node.rb +106 -0
  60. data/lib/berkeley_library/util/ods/xml/loext/table_protection.rb +26 -0
  61. data/lib/berkeley_library/util/ods/xml/manifest/file_entry.rb +42 -0
  62. data/lib/berkeley_library/util/ods/xml/manifest/manifest.rb +73 -0
  63. data/lib/berkeley_library/util/ods/xml/manifest_doc.rb +26 -0
  64. data/lib/berkeley_library/util/ods/xml/namespace.rb +46 -0
  65. data/lib/berkeley_library/util/ods/xml/office/automatic_styles.rb +181 -0
  66. data/lib/berkeley_library/util/ods/xml/office/body.rb +17 -0
  67. data/lib/berkeley_library/util/ods/xml/office/document_content.rb +98 -0
  68. data/lib/berkeley_library/util/ods/xml/office/document_styles.rb +39 -0
  69. data/lib/berkeley_library/util/ods/xml/office/font_face_decls.rb +30 -0
  70. data/lib/berkeley_library/util/ods/xml/office/scripts.rb +17 -0
  71. data/lib/berkeley_library/util/ods/xml/office/spreadsheet.rb +37 -0
  72. data/lib/berkeley_library/util/ods/xml/office/styles.rb +39 -0
  73. data/lib/berkeley_library/util/ods/xml/style/cell_style.rb +58 -0
  74. data/lib/berkeley_library/util/ods/xml/style/column_style.rb +36 -0
  75. data/lib/berkeley_library/util/ods/xml/style/default_style.rb +31 -0
  76. data/lib/berkeley_library/util/ods/xml/style/family.rb +85 -0
  77. data/lib/berkeley_library/util/ods/xml/style/font_face.rb +46 -0
  78. data/lib/berkeley_library/util/ods/xml/style/paragraph_properties.rb +30 -0
  79. data/lib/berkeley_library/util/ods/xml/style/row_style.rb +37 -0
  80. data/lib/berkeley_library/util/ods/xml/style/style.rb +44 -0
  81. data/lib/berkeley_library/util/ods/xml/style/table_cell_properties.rb +40 -0
  82. data/lib/berkeley_library/util/ods/xml/style/table_column_properties.rb +30 -0
  83. data/lib/berkeley_library/util/ods/xml/style/table_properties.rb +25 -0
  84. data/lib/berkeley_library/util/ods/xml/style/table_row_properties.rb +28 -0
  85. data/lib/berkeley_library/util/ods/xml/style/table_style.rb +27 -0
  86. data/lib/berkeley_library/util/ods/xml/style/text_properties.rb +52 -0
  87. data/lib/berkeley_library/util/ods/xml/styles_doc.rb +26 -0
  88. data/lib/berkeley_library/util/ods/xml/table/named_expressions.rb +17 -0
  89. data/lib/berkeley_library/util/ods/xml/table/repeatable.rb +38 -0
  90. data/lib/berkeley_library/util/ods/xml/table/table.rb +193 -0
  91. data/lib/berkeley_library/util/ods/xml/table/table_cell.rb +46 -0
  92. data/lib/berkeley_library/util/ods/xml/table/table_column.rb +43 -0
  93. data/lib/berkeley_library/util/ods/xml/table/table_row.rb +136 -0
  94. data/lib/berkeley_library/util/ods/xml/text/p.rb +118 -0
  95. data/lib/berkeley_library/util/paths.rb +111 -0
  96. data/lib/berkeley_library/util/stringios.rb +30 -0
  97. data/lib/berkeley_library/util/strings.rb +42 -0
  98. data/lib/berkeley_library/util/sys_exits.rb +15 -0
  99. data/lib/berkeley_library/util/times.rb +22 -0
  100. data/lib/berkeley_library/util/uris.rb +44 -0
  101. data/lib/berkeley_library/util/uris/appender.rb +162 -0
  102. data/lib/berkeley_library/util/uris/requester.rb +62 -0
  103. data/lib/berkeley_library/util/uris/validator.rb +32 -0
  104. data/rakelib/bundle.rake +8 -0
  105. data/rakelib/coverage.rake +11 -0
  106. data/rakelib/gem.rake +54 -0
  107. data/rakelib/rubocop.rake +18 -0
  108. data/rakelib/spec.rake +2 -0
  109. data/spec/.rubocop.yml +40 -0
  110. data/spec/berkeley_library/tind/api/api_exception_spec.rb +91 -0
  111. data/spec/berkeley_library/tind/api/api_spec.rb +143 -0
  112. data/spec/berkeley_library/tind/api/collection_spec.rb +74 -0
  113. data/spec/berkeley_library/tind/api/date_range_spec.rb +110 -0
  114. data/spec/berkeley_library/tind/api/format_spec.rb +54 -0
  115. data/spec/berkeley_library/tind/api/search_spec.rb +364 -0
  116. data/spec/berkeley_library/tind/config_spec.rb +86 -0
  117. data/spec/berkeley_library/tind/export/column_group_spec.rb +29 -0
  118. data/spec/berkeley_library/tind/export/column_spec.rb +43 -0
  119. data/spec/berkeley_library/tind/export/config_spec.rb +206 -0
  120. data/spec/berkeley_library/tind/export/export_command_spec.rb +169 -0
  121. data/spec/berkeley_library/tind/export/export_format_spec.rb +59 -0
  122. data/spec/berkeley_library/tind/export/export_matcher.rb +112 -0
  123. data/spec/berkeley_library/tind/export/export_spec.rb +150 -0
  124. data/spec/berkeley_library/tind/export/exporter_spec.rb +125 -0
  125. data/spec/berkeley_library/tind/export/row_spec.rb +118 -0
  126. data/spec/berkeley_library/tind/export/table_spec.rb +322 -0
  127. data/spec/berkeley_library/tind/marc/xml_reader_spec.rb +93 -0
  128. data/spec/berkeley_library/util/arrays_spec.rb +340 -0
  129. data/spec/berkeley_library/util/ods/spreadsheet_spec.rb +124 -0
  130. data/spec/berkeley_library/util/ods/xml/content_doc_spec.rb +121 -0
  131. data/spec/berkeley_library/util/ods/xml/manifest/file_entry_spec.rb +27 -0
  132. data/spec/berkeley_library/util/ods/xml/manifest/manifest_spec.rb +33 -0
  133. data/spec/berkeley_library/util/ods/xml/office/document_content_spec.rb +60 -0
  134. data/spec/berkeley_library/util/ods/xml/style/automatic_styles_spec.rb +37 -0
  135. data/spec/berkeley_library/util/ods/xml/style/family_spec.rb +57 -0
  136. data/spec/berkeley_library/util/ods/xml/table/table_row_spec.rb +179 -0
  137. data/spec/berkeley_library/util/ods/xml/table/table_spec.rb +218 -0
  138. data/spec/berkeley_library/util/paths_spec.rb +90 -0
  139. data/spec/berkeley_library/util/stringios_spec.rb +34 -0
  140. data/spec/berkeley_library/util/strings_spec.rb +27 -0
  141. data/spec/berkeley_library/util/times_spec.rb +39 -0
  142. data/spec/berkeley_library/util/uris_spec.rb +118 -0
  143. data/spec/data/collection-names.txt +438 -0
  144. data/spec/data/collections.json +4827 -0
  145. data/spec/data/disjoint-records.xml +187 -0
  146. data/spec/data/record-184453.xml +58 -0
  147. data/spec/data/record-184458.xml +63 -0
  148. data/spec/data/record-187888.xml +78 -0
  149. data/spec/data/records-api-search-cjk-p1.xml +6381 -0
  150. data/spec/data/records-api-search-cjk-p2.xml +5 -0
  151. data/spec/data/records-api-search-p1.xml +4506 -0
  152. data/spec/data/records-api-search-p2.xml +4509 -0
  153. data/spec/data/records-api-search-p3.xml +4506 -0
  154. data/spec/data/records-api-search-p4.xml +4509 -0
  155. data/spec/data/records-api-search-p5.xml +4506 -0
  156. data/spec/data/records-api-search-p6.xml +2436 -0
  157. data/spec/data/records-api-search-p7.xml +5 -0
  158. data/spec/data/records-api-search.xml +234 -0
  159. data/spec/data/records-manual-search.xml +547 -0
  160. data/spec/spec_helper.rb +30 -0
  161. data/test/profile/table_from_records_profile.rb +46 -0
  162. metadata +585 -0
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+
3
+ module BerkeleyLibrary
4
+ module TIND
5
+ module Export
6
+ describe ExportFormat do
7
+ describe :to_str do
8
+ it 'returns the value' do
9
+ ExportFormat.each do |fmt|
10
+ # rubocop:disable Style/StringConcatenation
11
+ expect('' + fmt).to eq(fmt.value)
12
+ # rubocop:enable Style/StringConcatenation
13
+ end
14
+ end
15
+ end
16
+
17
+ describe :mime_type do
18
+ it 'returns the correct MIME type' do
19
+ {
20
+ ExportFormat::CSV => 'text/csv',
21
+ ExportFormat::ODS => 'application/vnd.oasis.opendocument.spreadsheet'
22
+ }.each do |fmt, mime_type|
23
+ expect(fmt.mime_type).to eq(mime_type)
24
+ end
25
+ end
26
+ end
27
+
28
+ describe :ensure_format do
29
+ it 'rejects unsupported formats' do
30
+ expect { ExportFormat.ensure_format(:wks) }.to raise_error(ArgumentError)
31
+ end
32
+ end
33
+
34
+ describe :description do
35
+ it 'returns a description' do
36
+ ExportFormat.each do |fmt|
37
+ expect(fmt.description).to be_a(String)
38
+ expect(fmt.description).not_to be_empty
39
+ end
40
+ end
41
+ end
42
+
43
+ describe :DEFAULT do
44
+ it 'defaults to ODS' do
45
+ expect(ExportFormat::DEFAULT).to be(ExportFormat::ODS)
46
+ end
47
+ end
48
+
49
+ describe :default? do
50
+ it 'returns true for default, false otherwise' do
51
+ ExportFormat.each do |fmt|
52
+ expect(fmt.default?).to eq(fmt == ExportFormat::DEFAULT)
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,112 @@
1
+ require 'rspec'
2
+ require 'berkeley_library/util/strings'
3
+
4
+ RSpec::Matchers.define :match_table do |expected_table|
5
+ match { |actual| diff(expected_table, actual).empty? }
6
+
7
+ failure_message do |actual|
8
+ diff = diff(expected_table, actual)
9
+
10
+ msg_elements = ['output differs from table:']
11
+ diff.each do |(row, col), (v_expected, v_actual)|
12
+ msg = format_diff_msg(row, col, v_expected, v_actual)
13
+ msg_elements << msg
14
+ end
15
+ msg_elements.join("\n\t")
16
+ end
17
+
18
+ def diff(expected_table, ss_or_csv_str)
19
+ return diff_spreadsheet(expected_table, ss_or_csv_str) if ss_or_csv_str.respond_to?(:cell)
20
+
21
+ diff_csv(expected_table, ss_or_csv_str)
22
+ end
23
+
24
+ def diff_spreadsheet(expected_table, spreadsheet)
25
+ {}.tap do |diffs|
26
+ # NOTE: spreadsheet rows are 1-indexed, but row 1 is header
27
+ expected_table.headers.each_with_index do |expected_header, col|
28
+ cell_index = [1, 1 + col]
29
+ next if expected_header == (actual_header = spreadsheet.cell(*cell_index))
30
+
31
+ diffs[cell_index] = [expected_header, actual_header]
32
+ end
33
+
34
+ (0..expected_table.row_count).each do |row|
35
+ ss_row = 2 + row # row 1 is header
36
+ (0..expected_table.column_count).each do |col|
37
+ cell_index = [ss_row, 1 + col]
38
+ expected_value = expected_table.value_at(row, col)
39
+ actual_value = spreadsheet.cell(*cell_index)
40
+ next if expected_value == actual_value
41
+
42
+ diffs[cell_index] = [expected_value, actual_value]
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ def diff_csv(expected_table, csv_string)
49
+ # NOTE: CSV.parse() returns zero-indexed row array
50
+ csv = CSV.parse(csv_string, headers: false)
51
+ {}.tap do |diffs|
52
+ header_row = csv[0]
53
+ expected_table.headers.each_with_index do |expected_header, col|
54
+ next if expected_header == (actual_header = header_row[col])
55
+
56
+ diffs[[0, col]] = [expected_header, actual_header]
57
+ end
58
+
59
+ (0...expected_table.row_count).each do |row|
60
+ csv_row = 1 + row # row 1 is header
61
+ row_values = csv[csv_row] || []
62
+ (0...expected_table.column_count).each do |col|
63
+ expected_value = expected_table.value_at(row, col)
64
+ actual_value = row_values[col]
65
+ next if expected_value == actual_value
66
+
67
+ diffs[[csv_row, col]] = [expected_value, actual_value]
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ def matching_spreadsheet?(expected_table, spreadsheet)
74
+ aggregate_failures 'headers' do
75
+ expected_table.headers.each_with_index do |h, col|
76
+ ss_col = 1 + col
77
+ actual_header = spreadsheet.cell(1, ss_col)
78
+ expect(actual_header).to eq(h), "Expected header #{h.inspect} for column #{ss_col}, got #{actual_header.inspect}"
79
+ end
80
+ end
81
+
82
+ aggregate_failures 'values' do
83
+ (0..expected_table.row_count).each do |row|
84
+ ss_row = 2 + row # row 1 is header
85
+ (0..expected_table.column_count).each do |col|
86
+ ss_col = 1 + col
87
+ expected_value = expected_table.value_at(row, col)
88
+ actual_value = spreadsheet.cell(ss_row, ss_col)
89
+ expect(actual_value).to eq(expected_value), "(#{ss_row}, #{ss_col}): expected #{expected_value.inspect}, got #{actual_value.inspect}"
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ # TODO: make this work better
96
+ def format_diff_msg(row, col, v_expected, v_actual)
97
+ if (diff_index = BerkeleyLibrary::Util::Strings.diff_index(v_expected, v_actual))
98
+ <<~MSG
99
+ (#{row}, #{col}):
100
+ expected: #{v_expected.inspect}#{' '}
101
+ got: #{v_actual.inspect}
102
+ #{' ' * diff_index}^
103
+ MSG
104
+ else
105
+ <<~MSG
106
+ (#{row}, #{col}):#{' '}
107
+ expected: #{v_expected.inspect}#{' '}
108
+ got: #{v_actual.inspect}
109
+ MSG
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,150 @@
1
+ require 'spec_helper'
2
+ require 'roo'
3
+
4
+ require_relative 'export_matcher'
5
+
6
+ module BerkeleyLibrary
7
+ # noinspection RubyYardParamTypeMatch
8
+ module TIND
9
+ describe Export do
10
+ let(:basename) { File.basename(__FILE__, '.rb') }
11
+
12
+ describe 'export' do
13
+ let(:collection) { 'Bancroft Library' }
14
+
15
+ describe 'with results' do
16
+
17
+ let(:records) do
18
+ (1..7)
19
+ .map { |page| File.read("spec/data/records-api-search-p#{page}.xml") }
20
+ .map { |p| BerkeleyLibrary::TIND::MARC::XMLReader.new(p, freeze: true).to_a }
21
+ .flatten
22
+ end
23
+ let(:expected_table) { Export::Table.from_records(records, freeze: true, exportable_only: true) }
24
+
25
+ before(:each) do
26
+ search = instance_double(BerkeleyLibrary::TIND::API::Search)
27
+ allow(search).to receive(:each_result).with(freeze: true).and_return(records.each)
28
+ allow(BerkeleyLibrary::TIND::API::Search).to receive(:new).with(collection: collection).and_return(search)
29
+ end
30
+
31
+ describe :export do
32
+ describe 'CSV formats' do
33
+ it 'defaults to CSV' do
34
+ actual_csv = Export.export(collection)
35
+ expect(actual_csv).to match_table(expected_table)
36
+ end
37
+
38
+ [Export::ExportFormat::CSV, :csv, 'CSV'].each do |fmt|
39
+ it "accepts #{fmt.inspect} as a format parameter" do
40
+ actual_csv = Export.export(collection, fmt)
41
+ expect(actual_csv).to match_table(expected_table)
42
+ end
43
+ end
44
+ end
45
+
46
+ describe 'ODS formats' do
47
+ [Export::ExportFormat::ODS, :ods, 'ODS'].each_with_index do |fmt, i|
48
+ it "accepts #{fmt.inspect} as a format parameter" do
49
+ Dir.mktmpdir(basename) do |dir|
50
+ output_path = File.join(dir, "#{basename}-#{i}.ods")
51
+ Export.export(collection, fmt, output_path)
52
+
53
+ ss = Roo::Spreadsheet.open(output_path)
54
+ begin
55
+ expect(ss).to match_table(expected_table)
56
+ ensure
57
+ ss.close
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ describe 'without results' do
67
+ before(:each) do
68
+ search = instance_double(BerkeleyLibrary::TIND::API::Search)
69
+ allow(search).to receive(:each_result).with(freeze: true).and_return([].each)
70
+ allow(BerkeleyLibrary::TIND::API::Search).to receive(:new).with(collection: collection).and_return(search)
71
+ end
72
+
73
+ it 'raises NoResultsError' do
74
+ expect { Export.export(collection) }.to raise_error(Export::NoResultsError)
75
+ end
76
+ end
77
+
78
+ describe 'CJK support' do
79
+ let(:collection) { 'Houcun ju shi ji' }
80
+ let(:records) { BerkeleyLibrary::TIND::MARC::XMLReader.new('spec/data/records-api-search-cjk-p1.xml', freeze: true).to_a }
81
+ let(:expected_table) { Export::Table.from_records(records, freeze: true, exportable_only: true) }
82
+
83
+ before(:each) do
84
+ search = instance_double(BerkeleyLibrary::TIND::API::Search)
85
+ allow(search).to receive(:each_result).with(freeze: true).and_return(records.each)
86
+ allow(BerkeleyLibrary::TIND::API::Search).to receive(:new).with(collection: collection).and_return(search)
87
+ end
88
+
89
+ describe 'LibreOffice' do
90
+ it 'works for LibreOffice' do
91
+ Dir.mktmpdir(basename) do |dir|
92
+ output_path = File.join(dir, "#{basename}.ods")
93
+ Export.export(collection, 'ods', output_path)
94
+
95
+ ss = Roo::Spreadsheet.open(output_path)
96
+ begin
97
+ expect(ss).to match_table(expected_table)
98
+ ensure
99
+ ss.close
100
+ end
101
+ end
102
+ end
103
+
104
+ it 'writes to an exploded directory' do
105
+ Dir.mktmpdir(basename) do |dir|
106
+ Export.export(collection, 'ods', dir)
107
+
108
+ expected_paths_relative = %w[META-INF/manifest.xml styles.xml content.xml]
109
+ expected_paths_absolute = expected_paths_relative.map do |path_relative|
110
+ joined_path = File.join(dir, path_relative)
111
+ File.absolute_path(joined_path)
112
+ end
113
+
114
+ expected_paths_absolute.each do |path_absolute|
115
+ expect(File.file?(path_absolute)).to eq(true)
116
+ end
117
+ end
118
+ end
119
+ end
120
+
121
+ it 'works for CSV' do
122
+ actual_csv = Export.export(collection, 'csv')
123
+ expect(actual_csv).to match_table(expected_table)
124
+ end
125
+ end
126
+ end
127
+
128
+ describe 'with no results' do
129
+ let(:collection) { 'Not a collection' }
130
+
131
+ before(:each) do
132
+ search = instance_double(BerkeleyLibrary::TIND::API::Search)
133
+ empty_enumerator = Enumerator.new({})
134
+ allow(search).to receive(:each_result).and_return(empty_enumerator)
135
+ allow(BerkeleyLibrary::TIND::API::Search).to receive(:new).with(collection: collection).and_return(search)
136
+ end
137
+
138
+ describe :export do
139
+ it 'raises an error' do
140
+ Export::ExportFormat.each do |fmt|
141
+ out = instance_double(IO)
142
+ %i[reopen rewind << write].each { |m| expect(out).not_to receive(m) }
143
+ expect { Export.export(collection, fmt, out) }.to raise_error(Export::NoResultsError)
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,125 @@
1
+ require 'spec_helper'
2
+ require 'roo'
3
+
4
+ require_relative 'export_matcher'
5
+
6
+ module BerkeleyLibrary
7
+ module TIND
8
+ module Export
9
+ describe Exporter do
10
+ let(:collection) { 'Bancroft Library' }
11
+
12
+ describe 'base class' do
13
+ let(:exporter) { Exporter.new(collection) }
14
+
15
+ it "doesn't implement :export" do
16
+ expect { exporter.export }.to raise_error(NoMethodError)
17
+ end
18
+
19
+ it "doesn't respond to :export" do
20
+ expect(exporter.respond_to?('export')).to eq(false)
21
+ expect(exporter.respond_to?(:export)).to eq(false)
22
+ end
23
+ end
24
+
25
+ Export::ExportFormat.each do |export_format|
26
+
27
+ attr_reader :search
28
+
29
+ before(:each) do
30
+ @search = instance_double(BerkeleyLibrary::TIND::API::Search)
31
+ allow(BerkeleyLibrary::TIND::API::Search).to receive(:new).with(collection: collection).and_return(search)
32
+ end
33
+
34
+ describe export_format.to_s do
35
+ let(:ext) { export_format.to_s.downcase }
36
+ let(:exporter) { export_format.exporter_for(collection) }
37
+
38
+ it 'responds to :export' do
39
+ expect(exporter.respond_to?('export')).to eq(true)
40
+ expect(exporter.respond_to?(:export)).to eq(true)
41
+ end
42
+
43
+ describe 'with results' do
44
+ let(:records) do
45
+ (1..7)
46
+ .map { |page| File.read("spec/data/records-api-search-p#{page}.xml") }
47
+ .map { |p| BerkeleyLibrary::TIND::MARC::XMLReader.new(p, freeze: true).to_a }
48
+ .flatten
49
+ end
50
+
51
+ before(:each) do
52
+ allow(search).to receive(:each_result).with(freeze: true).and_return(records.each)
53
+ end
54
+
55
+ describe :any_results? do
56
+ it 'returns true' do
57
+ expect(exporter.any_results?).to eq(true)
58
+ end
59
+
60
+ it 'caches the search result' do
61
+ 2.times { exporter.any_results? }
62
+ expect(search).to have_received(:each_result).with(freeze: true).exactly(1).time
63
+ end
64
+ end
65
+
66
+ describe :export do
67
+ it 'exports' do
68
+ exported_data = exporter.export
69
+ expect(exported_data).not_to be_nil
70
+ end
71
+
72
+ it 'caches search results' do
73
+ 2.times { expect(exporter.export).not_to be_nil }
74
+ expect(search).to have_received(:each_result).with(freeze: true).exactly(1).time
75
+ end
76
+
77
+ it 'caches search results from a previous `has_results?` invocation' do
78
+ exporter.any_results?
79
+ exporter.export
80
+ expect(search).to have_received(:each_result).with(freeze: true).exactly(1).time
81
+ end
82
+ end
83
+ end
84
+
85
+ describe 'without results' do
86
+ before(:each) do
87
+ allow(search).to receive(:each_result).with(freeze: true).and_return([].each)
88
+ end
89
+
90
+ describe :any_results? do
91
+ it 'returns false' do
92
+ expect(exporter.any_results?).to eq(false)
93
+ end
94
+
95
+ it 'caches the search result' do
96
+ 2.times { exporter.any_results? }
97
+ expect(search).to have_received(:each_result).with(freeze: true).exactly(1).time
98
+ end
99
+ end
100
+
101
+ describe :export do
102
+ it 'raises NoResultsError' do
103
+ expect { exporter.export }.to raise_error(NoResultsError)
104
+ end
105
+
106
+ it 'caches the failed search result' do
107
+ 2.times do
108
+ expect { exporter.export }.to raise_error(NoResultsError)
109
+ end
110
+ expect(search).to have_received(:each_result).with(freeze: true).exactly(1).time
111
+ end
112
+
113
+ it 'caches the failed search result from a previous `has_results?` invocation' do
114
+ exporter.any_results?
115
+ expect { exporter.export }.to raise_error(NoResultsError)
116
+ expect(search).to have_received(:each_result).with(freeze: true).exactly(1).time
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end