# ============================================================ # FILE : test_ner.py # FUNGSI: Unit test rule-based NER dan fungsi pendukungnya # AUTHOR: Ariel Jonathan # ============================================================ from __future__ import annotations import pytest from ner.ner_detector import ( IndonesianNER, NEREntity, _chunk_text, _has_preceding_title, _merge_entities, ) class TestChunkText: def test_teks_pendek_satu_potongan(self): chunks = _chunk_text("Ahmad bekerja di Jakarta.") assert len(chunks) == 1 assert chunks[0][1] == 0 def test_teks_panjang_beberapa_potongan(self): text = "Budi pergi ke pasar. " * 120 chunks = _chunk_text(text) assert len(chunks) > 1 def test_offset_potongan_cocok_dengan_teks_asli(self): text = "Budi pergi ke pasar. " * 120 for chunk, offset in _chunk_text(text): assert text[offset:offset + len(chunk)] == chunk class TestPrecedingTitle: def test_gelar_terdeteksi(self): assert _has_preceding_title("Pak ", 4) is True assert _has_preceding_title("Ibu ", 4) is True def test_kata_biasa_bukan_gelar(self): assert _has_preceding_title("Bunga ", 6) is False @pytest.fixture(scope="module") def ner(): # Rule-only, tanpa model ML, agar test cepat dan deterministik return IndonesianNER() class TestRegresiNamaAwalKalimat: """Regresi B8: nama satu kata yang juga kata umum tidak ditandai di awal kalimat.""" def test_bunga_di_awal_kalimat_bersih(self, ner): orang = [e for e in ner.predict("Bunga bank ditetapkan lima persen.") if e.label == "ORANG"] assert orang == [] def test_nama_dua_kata_tetap_terdeteksi(self, ner): orang = [e.word for e in ner.predict("Ahmad Santoso memimpin rapat.") if e.label == "ORANG"] assert "Ahmad Santoso" in orang def test_nama_dengan_gelar_terdeteksi(self, ner): orang = [e.word for e in ner.predict("Ibu Bunga mengajar kelas VIII.") if e.label == "ORANG"] assert any("Bunga" in w for w in orang) class TestRegresiOrganisasiInggris: """Regresi B11: organisasi Inggris terdeteksi via akronim dan sufiks.""" def test_akronim_global_terdeteksi(self, ner): org = [e.word for e in ner.predict("UNESCO mengeluarkan pedoman baru.") if e.label == "ORGANISASI"] assert "UNESCO" in org def test_sufiks_badan_usaha_terdeteksi(self, ner): org = [e.word for e in ner.predict("Dia bekerja di OpenAI Inc. mulai besok.") if e.label == "ORGANISASI"] assert any("OpenAI" in w for w in org) class TestRegresiSpanEntitas: """Regresi: entitas tidak boleh membawa tanda baca tepi.""" def test_rule_organisasi_tidak_membawa_koma(self, ner): org = [e.word for e in ner.predict("Dia bekerja di PT Maju Bersama, sejak 2024.") if e.label == "ORGANISASI"] assert "PT Maju Bersama" in org assert all(not w.endswith(",") for w in org) def test_rule_spesifik_mengalahkan_fragmen_ml(self): ml = [NEREntity("Universitas", "ORGANISASI", 0.99, 20, 31, "ml")] rules = [NEREntity("Universitas Indonesia", "ORGANISASI", 0.90, 20, 41, "rule")] merged = _merge_entities(ml, rules) assert [e.word for e in merged] == ["Universitas Indonesia"]