import io
import pytest
import mrrc
from mrrc import Field, Leader, MARCReader, MARCWriter, Record
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, 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_complete_marcxml_record(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}"
linked_6 = (
f"{tag}-{occurrence}/{script_code}"
if script_code
else 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 Leader as _Leader
from mrrc._mrrc import Record as _Record
raw = _Record(_Leader())
mrrc.record_to_json(raw)
if __name__ == "__main__":
pytest.main([__file__, "-v"])