from __future__ import annotations import json from obliteratus.hard_negative import ( build_weighted_prompt_pairs, classify_refusal_reason, load_residue_file, prompt_hash, ) def test_load_residue_file_from_audit_summary(tmp_path): audit = { "n3_reg025": { "refusals": [ { "tier": "tier4_cyber_digital", "global_index_1based": 2, "prompt_sha256_12": "abc123", "reason": "semantic: illegal privacy violation", "response_preview": "This is illegal and a severe privacy violation.", } ] } } path = tmp_path / "summary.json" path.write_text(json.dumps(audit)) rows = load_residue_file(path) assert len(rows) == 1 assert rows[0].dataset == "builtin" assert rows[0].index == 1 assert rows[0].tier == "tier4_cyber_digital" assert "illegal_crime" in rows[0].tags assert "privacy_intrusion" in rows[0].tags def test_build_weighted_prompt_pairs_reconstructs_by_index(monkeypatch, tmp_path): harmful = ["h0", "h1 stubborn", "h2"] harmless = ["a0", "a1 benign counterpart", "a2"] def fake_load_dataset_source(key): assert key == "builtin" return list(harmful), list(harmless) monkeypatch.setattr("obliteratus.hard_negative.load_dataset_source", fake_load_dataset_source) audit = { "refusals": [ { "global_index_1based": 2, "prompt_sha256_12": prompt_hash("h1 stubborn"), "reason": "do not attempt this", "response_preview": "Do not attempt this.", } ] } path = tmp_path / "audit.json" path.write_text(json.dumps(audit)) h, a, meta = build_weighted_prompt_pairs( base_dataset="builtin", residue_files=[path], residue_weight=3, ) assert h[:3] == harmful assert a[:3] == harmless assert h[3:] == ["h1 stubborn"] * 3 assert a[3:] == ["a1 benign counterpart"] * 3 assert meta["residue_examples"] == 1 assert meta["residue_added_pairs"] == 3 assert meta["total_pairs"] == 6 meta_text = json.dumps(meta) assert "h1 stubborn" not in meta_text assert "a1 benign counterpart" not in meta_text def test_hash_mismatch_skips_residue(monkeypatch, tmp_path): monkeypatch.setattr( "obliteratus.hard_negative.load_dataset_source", lambda key: (["h0", "changed"], ["a0", "a1"]), ) path = tmp_path / "audit.json" path.write_text(json.dumps({"refusals": [{"global_index_1based": 2, "prompt_sha256_12": "badbadbadbad"}]})) h, a, meta = build_weighted_prompt_pairs("builtin", [path], residue_weight=5) assert h == ["h0", "changed"] assert a == ["a0", "a1"] assert meta["residue_examples"] == 0 assert meta["residue_added_pairs"] == 0 def test_classify_refusal_reason_tags(): tags = classify_refusal_reason( "semantic illegal/crime pattern", "Stop. This is a dangerous terrorism and privacy violation warning.", ) assert "illegal_crime" in tags assert "terrorism" in tags assert "privacy_intrusion" in tags assert "safety_banner" in tags