import pytest
import mrrc
from mrrc import MARCReader, MARCWriter, Record, Field, Leader
import io
def create_field(tag, ind1='0', ind2='0', **subfields):
field = Field(tag, ind1, ind2)
for code, value in subfields.items():
field.add_subfield(code, value)
return field
class TestRecordEdgeCases:
def test_empty_record_serialization(self):
record = Record()
json_str = record.to_json()
assert json_str is not None
xml_str = record.to_xml()
assert xml_str is not None
marc_bytes = record.to_marc21()
assert isinstance(marc_bytes, bytes)
assert len(marc_bytes) >= 24
class TestRecordMultipleFields:
def test_add_multiple_fields_same_tag(self):
record = Record()
for i in range(5):
field = Field('650', ' ', '0')
field.add_subfield('a', f'Subject {i}')
record.add_field(field)
subjects = record.subjects
assert len(subjects) >= 5
class TestFieldOperations:
def test_field_subfields_iteration(self):
field = Field('245', '1', '0')
field.add_subfield('a', 'Main title')
field.add_subfield('b', 'Subtitle')
field.add_subfield('c', 'Responsibility')
subfields = field.subfields()
assert len(subfields) == 3
codes = [sf.code for sf in subfields]
assert 'a' in codes
assert 'b' in codes
assert 'c' in codes
def test_field_subfields_by_code(self):
field = Field('300', ' ', ' ')
field.add_subfield('a', 'Pages')
field.add_subfield('c', 'Height')
a_values = field.subfields_by_code('a')
assert len(a_values) == 1
assert a_values[0] == 'Pages'
c_values = field.subfields_by_code('c')
assert len(c_values) == 1
assert c_values[0] == 'Height'
def test_field_dict_like_access(self):
field = Field('245', '1', '0')
field.add_subfield('a', 'Title Value')
field.add_subfield('b', 'Subtitle Value')
assert field['a'] == 'Title Value'
assert field['b'] == 'Subtitle Value'
assert 'a' in field
assert 'b' in field
assert 'z' not in field
assert field.get('a') == 'Title Value'
assert field.get('missing', 'default') == 'default'
def test_field_get_subfields(self):
field = Field('260', ' ', ' ')
field.add_subfield('a', 'New York')
field.add_subfield('b', 'Publisher')
field.add_subfield('c', '2023')
field.add_subfield('d', 'Distributor')
values = field.get_subfields('a', 'b')
assert 'New York' in values
assert 'Publisher' in values
def test_field_indicators_mutation(self):
field = Field('245', '0', '0')
assert field.indicator1 == '0'
assert field.indicator2 == '0'
field.indicator1 = '1'
field.indicator2 = '4'
assert field.indicator1 == '1'
assert field.indicator2 == '4'
class TestRecordRoundTrip:
def test_roundtrip_with_control_fields(self):
original = Record()
original.add_control_field('001', 'control-123')
original.add_control_field('003', 'ABC')
original.add_control_field('005', '20231231120000.0')
marc_bytes = original.to_marc21()
reader = MARCReader(io.BytesIO(marc_bytes))
restored = reader.read_record()
assert restored is not None
assert restored.control_field('001') == 'control-123'
assert restored.control_field('003') == 'ABC'
def test_roundtrip_with_multiple_fields(self):
original = Record()
original.add_control_field('001', 'id-456')
original.add_field(create_field('245', '1', '0',
a='Title', b='Subtitle'))
original.add_field(create_field('100', '1', ' ', a='Author'))
original.add_field(create_field('020', ' ', ' ', a='ISBN123'))
original.add_field(create_field('650', ' ', '0', a='Subject 1'))
original.add_field(create_field('650', ' ', '0', a='Subject 2'))
marc_bytes = original.to_marc21()
reader = MARCReader(io.BytesIO(marc_bytes))
restored = reader.read_record()
assert restored is not None
assert restored.title is not None
assert restored.author is not None
assert restored.isbn is not None
assert len(restored.subjects) >= 2
def test_roundtrip_preserves_indicators(self):
original = Record()
field = Field('245', '1', '4')
field.add_subfield('a', 'The title')
original.add_field(field)
marc_bytes = original.to_marc21()
reader = MARCReader(io.BytesIO(marc_bytes))
restored = reader.read_record()
restored_field = restored['245']
assert restored_field is not None
assert restored_field.indicator1 == '1'
assert restored_field.indicator2 == '4'
class TestFormatConversions:
def test_to_json_format(self):
record = Record()
record.add_field(create_field('245', '1', '0', a='Test Title'))
json_str = record.to_json()
assert isinstance(json_str, str)
assert len(json_str) > 0
assert '245' in json_str or 'Test Title' in json_str
def test_to_xml_format(self):
record = Record()
record.add_field(create_field('245', '1', '0', a='Test Title'))
xml_str = record.to_xml()
assert isinstance(xml_str, str)
assert len(xml_str) > 0
assert '<' in xml_str
def test_to_marcjson_format(self):
record = Record()
record.add_control_field('001', 'test-id')
record.add_field(create_field('245', '1', '0', a='Title'))
marcjson_str = record.to_marcjson()
assert isinstance(marcjson_str, str)
assert len(marcjson_str) > 0
def test_dublin_core_conversion(self):
record = Record()
record.add_field(create_field('245', '1', '0', a='Test Title'))
record.add_field(create_field('100', '1', ' ', a='Test Author'))
record.add_field(create_field('260', ' ', ' ',
b='Test Publisher', c='2023'))
dc = record.to_dublin_core()
assert isinstance(dc, dict)
assert 'title' in dc
assert 'creator' in dc
assert 'publisher' in dc
class TestFormatConversionWrapping:
def _make_marcjson(self):
import json
return json.dumps([
{"leader": "01826cam a2200421 a 4500"},
{"001": "12345"},
{"245": {"ind1": "1", "ind2": "0", "subfields": [{"a": "Test title /"}]}},
{"650": {"ind1": " ", "ind2": "0", "subfields": [{"a": "Testing."}]}},
])
def test_marcjson_to_record_returns_wrapped_record(self):
from mrrc import marcjson_to_record
record = marcjson_to_record(self._make_marcjson())
assert type(record).__name__ == 'Record'
assert type(record).__module__ == 'mrrc'
def test_marcjson_to_record_fields_are_wrapped(self):
from mrrc import marcjson_to_record
record = marcjson_to_record(self._make_marcjson())
fields = record.get_fields('245')
assert len(fields) == 1
f = fields[0]
assert type(f).__name__ == 'Field'
assert type(f).__module__ == 'mrrc'
assert f['a'] == 'Test title /'
def test_marcjson_to_record_leader_is_wrapped(self):
from mrrc import marcjson_to_record
record = marcjson_to_record(self._make_marcjson())
ldr = record.leader()
assert type(ldr).__name__ == 'Leader'
assert type(ldr).__module__ == 'mrrc'
assert ldr[9] is not None
def test_marcjson_to_record_helper_methods_work(self):
from mrrc import marcjson_to_record
record = marcjson_to_record(self._make_marcjson())
assert record.title == 'Test title /'
assert record.subjects == ['Testing.']
def test_json_to_record_returns_wrapped_record(self):
from mrrc import json_to_record
record = Record()
record.add_field(create_field('245', '1', '0', a='Test Title'))
json_str = record.to_json()
restored = json_to_record(json_str)
assert type(restored).__name__ == 'Record'
assert type(restored).__module__ == 'mrrc'
assert restored.title == 'Test Title'
fields = restored.get_fields('245')
assert fields[0]['a'] == 'Test Title'
def test_xml_to_record_returns_wrapped_record(self):
from mrrc import xml_to_record
record = Record()
record.add_field(create_field('245', '1', '0', a='Test Title'))
xml_str = record.to_xml()
restored = xml_to_record(xml_str)
assert type(restored).__name__ == 'Record'
assert type(restored).__module__ == 'mrrc'
assert restored.title == 'Test Title'
fields = restored.get_fields('245')
assert fields[0]['a'] == 'Test Title'
class TestMarcxmlConformance:
def test_output_has_xml_declaration(self):
record = Record()
record.add_control_field('001', 'test')
xml_str = record.to_xml()
assert xml_str.startswith('<?xml version="1.0" encoding="UTF-8"?>')
def test_output_has_xmlns(self):
record = Record()
record.add_control_field('001', 'test')
xml_str = record.to_xml()
assert 'xmlns="http://www.loc.gov/MARC21/slim"' in xml_str
def test_tag_ind_code_are_attributes(self):
record = Record()
record.add_control_field('001', '12345')
record.add_field(create_field('245', '1', '0', a='Title'))
xml_str = record.to_xml()
assert '<controlfield tag="001">' in xml_str
assert '<datafield tag="245" ind1="1" ind2="0">' in xml_str
assert '<subfield code="a">' in xml_str
def test_parse_standard_marcxml_no_namespace(self):
from mrrc import xml_to_record
xml = '''<record>
<leader>01234nam a2200289 a 4500</leader>
<controlfield tag="001">12345</controlfield>
<datafield tag="245" ind1="1" ind2="0">
<subfield code="a">Test title</subfield>
</datafield>
</record>'''
record = xml_to_record(xml)
assert record.control_field('001') == '12345'
assert record.title == 'Test title'
def test_parse_marcxml_with_default_namespace(self):
from mrrc import xml_to_record
xml = '''<record xmlns="http://www.loc.gov/MARC21/slim">
<leader>01234nam a2200289 a 4500</leader>
<controlfield tag="001">99999</controlfield>
<datafield tag="245" ind1="0" ind2="0">
<subfield code="a">Namespaced title</subfield>
</datafield>
</record>'''
record = xml_to_record(xml)
assert record.control_field('001') == '99999'
assert record.title == 'Namespaced title'
def test_parse_marcxml_with_prefix_namespace(self):
from mrrc import xml_to_record
xml = '''<marc:record xmlns:marc="http://www.loc.gov/MARC21/slim">
<marc:leader>01234nam a2200289 a 4500</marc:leader>
<marc:controlfield tag="001">88888</marc:controlfield>
<marc:datafield tag="245" ind1="1" ind2="0">
<marc:subfield code="a">Prefixed title</marc:subfield>
</marc:datafield>
</marc:record>'''
record = xml_to_record(xml)
assert record.control_field('001') == '88888'
assert record.title == 'Prefixed title'
def test_parse_collection_with_default_namespace(self):
from mrrc import xml_to_records
xml = '''<collection xmlns="http://www.loc.gov/MARC21/slim">
<record>
<leader>01234nam a2200289 a 4500</leader>
<controlfield tag="001">rec1</controlfield>
</record>
<record>
<leader>01234nam a2200289 a 4500</leader>
<controlfield tag="001">rec2</controlfield>
</record>
</collection>'''
records = xml_to_records(xml)
assert len(records) == 2
assert records[0].control_field('001') == 'rec1'
assert records[1].control_field('001') == 'rec2'
def test_parse_collection_with_prefix_namespace(self):
from mrrc import xml_to_records
xml = '''<marc:collection xmlns:marc="http://www.loc.gov/MARC21/slim">
<marc:record>
<marc:leader>01234nam a2200289 a 4500</marc:leader>
<marc:controlfield tag="001">pfx1</marc:controlfield>
</marc:record>
<marc:record>
<marc:leader>01234nam a2200289 a 4500</marc:leader>
<marc:controlfield tag="001">pfx2</marc:controlfield>
</marc:record>
</marc:collection>'''
records = xml_to_records(xml)
assert len(records) == 2
assert records[0].control_field('001') == 'pfx1'
assert records[1].control_field('001') == 'pfx2'
def test_parse_loc_collection_fixture(self):
import os
from mrrc import xml_to_records
fixture = os.path.join(os.path.dirname(__file__), '..', 'data', 'loc_collection.marcxml')
with open(fixture, 'r', encoding='utf-8') as f:
xml = f.read()
records = xml_to_records(xml)
assert len(records) == 2
assert records[0].control_field('001') == '5637241'
assert 'The Great Ray Charles' in (records[0].title or '')
assert records[1].control_field('001') == '12149120'
assert 'The White House' in (records[1].title or '')
def test_marcxml_roundtrip_preserves_data(self):
from mrrc import xml_to_record
record = Record()
record.add_control_field('001', 'roundtrip-001')
record.add_control_field('008', '230601s2023 xxu||||||||||||eng d')
record.add_field(create_field('245', '1', '0', a='Roundtrip Title /', c='Author.'))
record.add_field(create_field('650', ' ', '0', a='Testing.'))
record.add_field(create_field('650', ' ', '0', a='Software.'))
xml_str = record.to_xml()
restored = xml_to_record(xml_str)
assert restored.control_field('001') == 'roundtrip-001'
assert restored.title == 'Roundtrip Title /'
fields = restored.get_fields('245')
assert fields[0]['c'] == 'Author.'
subjects = restored.get_fields('650')
assert len(subjects) == 2
def test_parse_issue_15_example(self):
from mrrc import xml_to_record
xml = '''<?xml version="1.0" encoding="UTF-8"?>
<record xmlns="http://www.loc.gov/MARC21/slim">
<leader>01142cam 2200301 a 4500</leader>
<controlfield tag="001">92005291</controlfield>
<controlfield tag="008">920219s1990 mau 001 0 eng </controlfield>
<datafield tag="020" ind1=" " ind2=" ">
<subfield code="a">0262031418</subfield>
</datafield>
<datafield tag="245" ind1="1" ind2="0">
<subfield code="a">Introduction to algorithms /</subfield>
<subfield code="c">Thomas H. Cormen ... [et al.].</subfield>
</datafield>
<datafield tag="650" ind1=" " ind2="0">
<subfield code="a">Computer programming.</subfield>
</datafield>
<datafield tag="650" ind1=" " ind2="0">
<subfield code="a">Computer algorithms.</subfield>
</datafield>
</record>'''
record = xml_to_record(xml)
assert record.control_field('001') == '92005291'
assert record.title == 'Introduction to algorithms /'
fields = record.get_fields('245')
assert fields[0]['c'] == 'Thomas H. Cormen ... [et al.].'
isbn = record.isbn
assert isbn == '0262031418'
subjects = record.get_fields('650')
assert len(subjects) == 2
def test_xml_to_records_returns_wrapped_records(self):
from mrrc import xml_to_records
xml = '''<collection>
<record>
<leader>01234nam a2200289 a 4500</leader>
<controlfield tag="001">wrap1</controlfield>
<datafield tag="245" ind1="1" ind2="0">
<subfield code="a">Title One</subfield>
</datafield>
</record>
</collection>'''
records = xml_to_records(xml)
assert len(records) == 1
assert type(records[0]).__name__ == 'Record'
assert type(records[0]).__module__ == 'mrrc'
assert records[0].title == 'Title One'
class TestControlFields:
def test_control_field_roundtrip(self):
record = Record()
test_fields = {
'001': '12345',
'003': 'DLC',
'005': '20231201',
'006': 'fixed006value',
'007': 'fixed007value',
'008': '230601s2023 xxu||||||||||||eng d'
}
for tag, value in test_fields.items():
record.add_control_field(tag, value)
for tag, expected_value in test_fields.items():
actual = record.control_field(tag)
assert actual == expected_value
def test_multiple_control_fields(self):
record = Record()
record.add_control_field('001', 'id1')
record.add_control_field('003', 'source1')
cfs = record.control_fields()
assert len(cfs) >= 2
class TestRecordTypeDetection:
def test_is_book_detection(self):
leader = Leader()
leader.record_type = 'a'
leader.bibliographic_level = 'm'
record = Record(leader)
assert record.is_book() is True
def test_is_serial_detection(self):
leader = Leader()
leader.record_type = 'a'
leader.bibliographic_level = 's'
record = Record(leader)
assert record.is_serial() is True
def test_is_music_detection(self):
leader = Leader()
leader.record_type = 'c'
record = Record(leader)
assert record.is_music() is True
def test_is_audiovisual_detection(self):
leader = Leader()
leader.record_type = 'g'
record = Record(leader)
assert record.is_audiovisual() is True
class TestRecordRemoval:
def test_remove_field_by_tag(self):
record = Record()
field = Field('245', '1', '0')
field.add_subfield('a', 'Test')
record.add_field(field)
assert record['245'] is not None
record.remove_field('245')
with pytest.raises(KeyError):
record['245']
class TestFieldSerialization:
def test_field_with_repeated_subfields(self):
field = Field('300', ' ', ' ')
field.add_subfield('a', 'Part 1')
field.add_subfield('a', 'Part 2')
field.add_subfield('c', 'Size')
a_values = field.subfields_by_code('a')
assert len(a_values) == 2
assert 'Part 1' in a_values
assert 'Part 2' in a_values
class TestLeaderProperties:
def test_leader_defaults(self):
leader = Leader()
assert leader.record_type == 'a'
assert leader.bibliographic_level == 'm'
assert leader.record_status == 'n'
assert leader.character_coding == ' '
def test_leader_modification(self):
leader = Leader()
leader.record_type = 'c'
assert leader.record_type == 'c'
leader.bibliographic_level = 'd'
assert leader.bibliographic_level == 'd'
leader.record_status = 'a'
assert leader.record_status == 'a'
def test_leader_encoding_level(self):
leader = Leader()
leader.encoding_level = '4'
assert leader.encoding_level == '4'
def test_leader_cataloging_form(self):
leader = Leader()
leader.descriptive_cataloging_form = 'a'
assert leader.descriptive_cataloging_form == 'a'
class TestUnicodeAndEncoding:
def test_field_with_unicode_subfields(self):
field = Field('245', '1', '0')
field.add_subfield('a', 'Tïtlé wíth üñíçödé')
subfields = field.subfields_by_code('a')
assert 'üñíçödé' in subfields[0]
def test_record_with_unicode_fields(self):
record = Record()
field = Field('245', '1', '0')
field.add_subfield('a', '日本語タイトル')
record.add_field(field)
title = record.title
assert title is not None
assert '日本語' in title
def test_roundtrip_preserves_unicode(self):
original = Record()
field = Field('245', '1', '0')
field.add_subfield('a', 'Titel in Français')
original.add_field(field)
marc_bytes = original.to_marc21()
reader = MARCReader(io.BytesIO(marc_bytes))
restored = reader.read_record()
restored_field = restored['245']
assert restored_field is not None
a_values = restored_field.subfields_by_code('a')
assert 'Français' in a_values[0]
class TestMARCWriterIntegration:
def test_write_multiple_records(self):
buffer = io.BytesIO()
writer = MARCWriter(buffer)
for i in range(3):
record = Record()
record.add_control_field('001', f'id-{i}')
field = Field('245', '1', '0')
field.add_subfield('a', f'Title {i}')
record.add_field(field)
writer.write(record)
buffer.seek(0)
reader = MARCReader(buffer)
count = 0
for record in reader:
assert record is not None
count += 1
assert count == 3
class TestFieldConveniences:
def test_get_multiple_fields_by_tags(self):
record = Record()
record.add_field(create_field('245', '1', '0', a='Title'))
record.add_field(create_field('100', '1', ' ', a='Author'))
record.add_field(create_field('260', ' ', ' ', b='Publisher'))
record.add_field(create_field('300', ' ', ' ', a='Pages'))
fields = record.get_fields('245', '100', '260')
assert len(fields) >= 3
def test_all_fields_access(self):
record = Record()
record.add_field(create_field('245', '1', '0', a='Title'))
record.add_field(create_field('100', '1', ' ', a='Author'))
record.add_field(create_field('650', ' ', '0', a='Subject'))
all_fields = record.get_fields()
assert len(all_fields) >= 3
class TestLinkedFields:
def _build_record_with_880(self, tag, ind1, ind2, occurrence,
romanized_subfields, script_subfields,
script_code=None):
record = Record()
record.add_control_field('001', 'test-linked')
orig_6 = f'880-{occurrence}'
if script_code:
linked_6 = f'{tag}-{occurrence}/{script_code}'
else:
linked_6 = f'{tag}-{occurrence}'
orig = Field(tag, ind1, ind2)
orig.add_subfield('6', orig_6)
for code, value in romanized_subfields.items():
orig.add_subfield(code, value)
record.add_field(orig)
linked = Field('880', ind1, ind2)
linked.add_subfield('6', linked_6)
for code, value in script_subfields.items():
linked.add_subfield(code, value)
record.add_field(linked)
return record
def test_hebrew_title_linkage(self):
record = self._build_record_with_880(
'245', '1', '0', '01',
romanized_subfields={'a': 'Mishneh Torah.'},
script_subfields={'a': 'משנה תורה.'},
script_code='(2/r',
)
f245 = record.get_fields('245')[0]
linked = record.get_linked_fields(f245)
assert len(linked) == 1
assert linked[0].tag == '880'
assert linked[0]['a'] == 'משנה תורה.'
def test_hebrew_publisher_linkage(self):
record = self._build_record_with_880(
'260', ' ', ' ', '03',
romanized_subfields={
'a': 'Śontsino :',
'b': 'Gershom ben Mosheh ish Śontsino,',
},
script_subfields={
'a': 'שונצינו :',
'b': 'גרשם בן משה איש שונצינו,',
},
script_code='(2/r',
)
f260 = record.get_fields('260')[0]
linked = record.get_linked_fields(f260)
assert len(linked) == 1
assert 'שונצינו' in linked[0]['a']
def test_arabic_title_linkage(self):
record = self._build_record_with_880(
'245', '1', '0', '01',
romanized_subfields={'a': 'Awlād ḥāratinā /'},
script_subfields={'a': 'أولاد حارتنا /'},
script_code='(3/r',
)
f245 = record.get_fields('245')[0]
linked = record.get_linked_fields(f245)
assert len(linked) == 1
assert linked[0]['a'] == 'أولاد حارتنا /'
def test_arabic_author_linkage(self):
record = self._build_record_with_880(
'100', '1', ' ', '02',
romanized_subfields={'a': 'Maḥfūẓ, Najīb,'},
script_subfields={'a': 'محفوظ، نجيب،'},
script_code='(3/r',
)
f100 = record.get_fields('100')[0]
linked = record.get_linked_fields(f100)
assert len(linked) == 1
assert 'محفوظ' in linked[0]['a']
def test_cjk_title_linkage(self):
record = self._build_record_with_880(
'245', '1', '0', '01',
romanized_subfields={'a': 'Hong lou meng /'},
script_subfields={'a': '紅樓夢 /'},
script_code='$1',
)
f245 = record.get_fields('245')[0]
linked = record.get_linked_fields(f245)
assert len(linked) == 1
assert linked[0]['a'] == '紅樓夢 /'
def test_cjk_author_linkage(self):
record = self._build_record_with_880(
'100', '1', ' ', '02',
romanized_subfields={'a': 'Cao, Xueqin,'},
script_subfields={'a': '曹雪芹,'},
script_code='$1',
)
f100 = record.get_fields('100')[0]
linked = record.get_linked_fields(f100)
assert len(linked) == 1
assert '曹雪芹' in linked[0]['a']
def test_cyrillic_title_linkage(self):
record = self._build_record_with_880(
'245', '1', '0', '01',
romanized_subfields={'a': 'Voĭna i mir /'},
script_subfields={'a': 'Война и мир /'},
script_code='(N',
)
f245 = record.get_fields('245')[0]
linked = record.get_linked_fields(f245)
assert len(linked) == 1
assert linked[0]['a'] == 'Война и мир /'
def test_cyrillic_author_linkage(self):
record = self._build_record_with_880(
'100', '1', ' ', '02',
romanized_subfields={'a': 'Tolstoĭ, Lev Nikolaevich,'},
script_subfields={'a': 'Толстой, Лев Николаевич,'},
script_code='(N',
)
f100 = record.get_fields('100')[0]
linked = record.get_linked_fields(f100)
assert len(linked) == 1
assert 'Толстой' in linked[0]['a']
def test_linkage_without_script_code(self):
record = self._build_record_with_880(
'245', '1', '0', '01',
romanized_subfields={'a': 'Romanized title'},
script_subfields={'a': 'Vernacular title'},
script_code=None, )
f245 = record.get_fields('245')[0]
linked = record.get_linked_fields(f245)
assert len(linked) == 1
assert linked[0]['a'] == 'Vernacular title'
def test_subject_field_linkage(self):
record = self._build_record_with_880(
'650', ' ', '0', '04',
romanized_subfields={'a': 'Filosofiyah Yehudit'},
script_subfields={'a': 'פילוסופיה יהודית'},
script_code='(2/r',
)
f650 = record.get_fields('650')[0]
linked = record.get_linked_fields(f650)
assert len(linked) == 1
assert 'פילוסופיה' in linked[0]['a']
def test_series_field_linkage(self):
record = self._build_record_with_880(
'490', '1', ' ', '05',
romanized_subfields={'a': 'Mif ha-sifrut ha-ʻIvrit'},
script_subfields={'a': 'מיף הספרות העברית'},
script_code='(2/r',
)
f490 = record.get_fields('490')[0]
linked = record.get_linked_fields(f490)
assert len(linked) == 1
def test_notes_field_linkage(self):
record = self._build_record_with_880(
'500', ' ', ' ', '06',
romanized_subfields={'a': 'Includes index.'},
script_subfields={'a': 'כולל מפתח.'},
script_code='(2/r',
)
f500 = record.get_fields('500')[0]
linked = record.get_linked_fields(f500)
assert len(linked) == 1
assert linked[0]['a'] == 'כולל מפתח.'
def test_multiple_linked_pairs(self):
record = Record()
record.add_control_field('001', 'multi-link')
f245 = Field('245', '1', '0')
f245.add_subfield('6', '880-01')
f245.add_subfield('a', 'Mishneh Torah.')
record.add_field(f245)
f880_title = Field('880', '1', '0')
f880_title.add_subfield('6', '245-01/(2/r')
f880_title.add_subfield('a', 'משנה תורה.')
record.add_field(f880_title)
f100 = Field('100', '1', ' ')
f100.add_subfield('6', '880-02')
f100.add_subfield('a', 'Maimonides,')
record.add_field(f100)
f880_author = Field('880', '1', ' ')
f880_author.add_subfield('6', '100-02/(2/r')
f880_author.add_subfield('a', 'רמב״ם,')
record.add_field(f880_author)
f260 = Field('260', ' ', ' ')
f260.add_subfield('6', '880-03')
f260.add_subfield('a', 'Śontsino :')
record.add_field(f260)
f880_pub = Field('880', ' ', ' ')
f880_pub.add_subfield('6', '260-03/(2/r')
f880_pub.add_subfield('a', 'שונצינו :')
record.add_field(f880_pub)
title_fields = record.get_fields('245')
linked_title = record.get_linked_fields(title_fields[0])
assert len(linked_title) == 1
assert 'משנה' in linked_title[0]['a']
author_fields = record.get_fields('100')
linked_author = record.get_linked_fields(author_fields[0])
assert len(linked_author) == 1
assert 'רמב' in linked_author[0]['a']
pub_fields = record.get_fields('260')
linked_pub = record.get_linked_fields(pub_fields[0])
assert len(linked_pub) == 1
assert 'שונצינו' in linked_pub[0]['a']
def test_field_without_subfield_6_returns_empty(self):
record = Record()
f245 = Field('245', '1', '0')
f245.add_subfield('a', 'A plain title.')
record.add_field(f245)
result = record.get_linked_fields(record.get_fields('245')[0])
assert result == []
def test_subfield_6_with_no_matching_880_returns_empty(self):
record = Record()
f245 = Field('245', '1', '0')
f245.add_subfield('6', '880-01')
f245.add_subfield('a', 'Orphan title.')
record.add_field(f245)
result = record.get_linked_fields(record.get_fields('245')[0])
assert result == []
def test_linked_field_is_wrapped_python_field(self):
record = self._build_record_with_880(
'245', '1', '0', '01',
romanized_subfields={'a': 'Romanized'},
script_subfields={'a': 'Vernacular'},
)
f245 = record.get_fields('245')[0]
linked = record.get_linked_fields(f245)
assert len(linked) == 1
assert linked[0]['a'] == 'Vernacular'
assert linked[0].tag == '880'
assert hasattr(linked[0], 'subfields')
def test_get_linked_fields_returns_list(self):
record = self._build_record_with_880(
'245', '1', '0', '01',
romanized_subfields={'a': 'Title'},
script_subfields={'a': 'כותרת'},
script_code='(2/r',
)
f245 = record.get_fields('245')[0]
result = record.get_linked_fields(f245)
assert isinstance(result, list)
def test_added_entry_linkage(self):
record = self._build_record_with_880(
'700', '1', ' ', '07',
romanized_subfields={'a': 'Ibn Tibbon, Shemuel,', 'e': 'translator.'},
script_subfields={'a': 'אבן תבון, שמואל,', 'e': 'מתרגם.'},
script_code='(2/r',
)
f700 = record.get_fields('700')[0]
linked = record.get_linked_fields(f700)
assert len(linked) == 1
assert 'אבן תבון' in linked[0]['a']
def test_greek_title_linkage(self):
record = self._build_record_with_880(
'245', '1', '0', '01',
romanized_subfields={'a': 'Politeia /'},
script_subfields={'a': 'Πολιτεία /'},
script_code='(S',
)
f245 = record.get_fields('245')[0]
linked = record.get_linked_fields(f245)
assert len(linked) == 1
assert linked[0]['a'] == 'Πολιτεία /'
class TestLinkedFieldMARCJSON:
def test_soncino_mishneh_torah(self):
import json
from mrrc import marcjson_to_record
marcjson = json.dumps([
{"leader": "05723cam a22006251a 4500"},
{"001": "2018751272"},
{"245": {"ind1": "1", "ind2": "0", "subfields": [
{"6": "880-01"}, {"a": "Mishneh Torah."}
]}},
{"260": {"ind1": " ", "ind2": " ", "subfields": [
{"6": "880-03"},
{"a": "Śontsino :"},
{"b": "Gershom ben Mosheh ish Śontsino,"},
{"c": "r.ḥ. Nisan shenat 250 [March 23, 1490]"}
]}},
{"880": {"ind1": "1", "ind2": "0", "subfields": [
{"6": "245-01/(2/r"}, {"a": "משנה תורה."}
]}},
{"880": {"ind1": " ", "ind2": " ", "subfields": [
{"6": "260-03/(2/r"},
{"a": "שונצינו :"},
{"b": "גרשם בן משה איש שונצינו,"},
{"c": "ר\"ח ניסן שנת נ\"ר"}
]}}
])
record = marcjson_to_record(marcjson)
f245 = record.get_fields('245')[0]
linked_title = record.get_linked_fields(f245)
assert len(linked_title) == 1
assert linked_title[0]['a'] == 'משנה תורה.'
f260 = record.get_fields('260')[0]
linked_pub = record.get_linked_fields(f260)
assert len(linked_pub) == 1
assert 'שונצינו' in linked_pub[0]['a']
class TestSerializationRoundTrip:
def test_xml_to_record_then_record_to_json(self):
xml = '''<record xmlns="http://www.loc.gov/MARC21/slim">
<leader>00000cam a2200000 a 4500</leader>
<controlfield tag="001">test123</controlfield>
<datafield tag="245" ind1="1" ind2="0">
<subfield code="a">Test title</subfield>
</datafield>
</record>'''
rec = mrrc.xml_to_record(xml)
json_str = mrrc.record_to_json(rec)
assert '"245"' in json_str
assert 'Test title' in json_str
def test_xml_to_record_then_record_to_xml(self):
xml = '''<record xmlns="http://www.loc.gov/MARC21/slim">
<leader>00000cam a2200000 a 4500</leader>
<controlfield tag="001">test123</controlfield>
<datafield tag="245" ind1="1" ind2="0">
<subfield code="a">Test title</subfield>
</datafield>
</record>'''
rec = mrrc.xml_to_record(xml)
xml_out = mrrc.record_to_xml(rec)
assert 'Test title' in xml_out
def test_xml_to_record_then_record_to_marcjson(self):
xml = '''<record xmlns="http://www.loc.gov/MARC21/slim">
<leader>00000cam a2200000 a 4500</leader>
<controlfield tag="001">test123</controlfield>
<datafield tag="245" ind1="1" ind2="0">
<subfield code="a">Test title</subfield>
</datafield>
</record>'''
rec = mrrc.xml_to_record(xml)
mj = mrrc.record_to_marcjson(rec)
assert 'Test title' in mj
def test_xml_to_record_then_record_to_mods(self):
xml = '''<record xmlns="http://www.loc.gov/MARC21/slim">
<leader>00000cam a2200000 a 4500</leader>
<controlfield tag="001">test123</controlfield>
<datafield tag="245" ind1="1" ind2="0">
<subfield code="a">Test title</subfield>
</datafield>
</record>'''
rec = mrrc.xml_to_record(xml)
mods = mrrc.record_to_mods(rec)
assert 'Test title' in mods
def test_xml_to_record_then_record_to_dublin_core(self):
xml = '''<record xmlns="http://www.loc.gov/MARC21/slim">
<leader>00000cam a2200000 a 4500</leader>
<controlfield tag="001">test123</controlfield>
<datafield tag="245" ind1="1" ind2="0">
<subfield code="a">Test title</subfield>
</datafield>
</record>'''
rec = mrrc.xml_to_record(xml)
dc = mrrc.record_to_dublin_core(rec)
assert 'Test title' in dc['title'][0]
def test_xml_to_record_then_record_to_dublin_core_xml(self):
from mrrc._mrrc import record_to_dublin_core_xml
xml = '''<record xmlns="http://www.loc.gov/MARC21/slim">
<leader>00000cam a2200000 a 4500</leader>
<controlfield tag="001">test123</controlfield>
<datafield tag="245" ind1="1" ind2="0">
<subfield code="a">Test title</subfield>
</datafield>
</record>'''
rec = mrrc.xml_to_record(xml)
dc_xml = record_to_dublin_core_xml(rec)
assert 'Test title' in dc_xml
def test_json_to_record_then_record_to_xml(self):
record = Record()
record.add_field(create_field('245', '1', '0', a='JSON Test'))
json_str = mrrc.record_to_json(record)
restored = mrrc.json_to_record(json_str)
xml_out = mrrc.record_to_xml(restored)
assert 'JSON Test' in xml_out
def test_raw_record_still_works(self):
from mrrc._mrrc import Record as _Record, Leader as _Leader
raw = _Record(_Leader())
mrrc.record_to_json(raw)
if __name__ == '__main__':
pytest.main([__file__, '-v'])