import hashlib import io import json from pathlib import Path import sqlite3 import tarfile import tempfile import unittest from paper_atlas.collectors.openreview import collect,decision_record,paper_record from paper_atlas.ingestion.records import ingest_file class SnapshotTests(unittest.TestCase): def setUp(self): self.tmp=tempfile.TemporaryDirectory();self.addCleanup(self.tmp.cleanup) self.root=Path(self.tmp.name) def paper(self,forum='paper1',v2=True): content={'title':'A public paper','abstract':'A public abstract','authors':['Jane Doe'], 'authorids':['private@example.org'],'keywords':['learning']} if v2:content={key:{'value':value} for key,value in content.items()} number=int(forum[-1]) if forum[-1].isdigit() else 42 return {'id':forum,'forum':forum,'number':number,'readers':['everyone'],'cdate':1760000000000, 'invitations':['ICLR.cc/2026/Conference/-/Submission'],'content':content} def decision(self,forum='paper1',identifier='decision1',value='Reject'): return {'id':identifier,'forum':forum,'_paper_forum':forum,'readers':['everyone'], 'invitations':['ICLR.cc/2026/Conference/Submission42/-/Decision'], 'signatures':['ICLR.cc/2026/Conference/Program_Chairs'],'cdate':1770000000000, 'content':{'decision':{'value':value},'comment':{'value':'PRIVATE REVIEW BODY'}}} def archive(self,papers,reviews): path=self.root/'snapshot.tar.bz2' with tarfile.open(path,'w:bz2') as archive: # Real release order is not guaranteed; decisions precede submissions. for kind,rows in [('reviews',reviews),('papers',papers)]: data=(''.join(json.dumps(row)+'\n' for row in rows)).encode() entry=tarfile.TarInfo('data/iclr_2026/'+kind+'.jsonl');entry.size=len(data) archive.addfile(entry,io.BytesIO(data)) return path,hashlib.sha256(path.read_bytes()).hexdigest() def test_public_v1_and_v2_metadata_omit_authorids_and_review_bodies(self): for v2 in (False,True): row,reason=paper_record(self.paper(v2=v2),2026) self.assertIsNone(reason);self.assertEqual(row['authors'],['Jane Doe']) self.assertNotIn('private@example.org',json.dumps(row)) self.assertEqual(row['identifiers']['openreview'],'paper1') note=self.paper();note['readers']=['chairs'] self.assertEqual(paper_record(note,2026)[1],'not_public') def test_only_public_signed_formal_decisions_count(self): note=self.decision();row,reason=decision_record(note,2026) self.assertIsNone(reason);self.assertEqual(row['status'],'rejected') self.assertNotIn('PRIVATE REVIEW BODY',json.dumps(row)) note['invitations']=['ICLR.cc/2026/Conference/Submission42/-/Official_Review'] self.assertEqual(decision_record(note,2026)[1],'not_decision') note=self.decision();note['signatures']=['ICLR.cc/2026/Conference/Submission42/Reviewer_1'] self.assertEqual(decision_record(note,2026)[1],'unverified_signature') note=self.decision();note['readers']=['chairs'] self.assertEqual(decision_record(note,2026)[1],'not_public') note=self.decision(value='Borderline Accept') self.assertEqual(decision_record(note,2026)[0]['status'],'unknown') def test_unordered_snapshot_import_is_idempotent_and_preserves_unknown_and_orphans(self): path,sha=self.archive([self.paper(),self.paper('paper2'),self.paper('paper3')], [self.decision(),self.decision('paper2','decision2','Accept (poster)'),self.decision('absent','orphan')]) out=self.root/'out';manifest=collect(path,out,[2026],sha)[2026] self.assertEqual(manifest['submission_status_counts'],{'rejected':1,'accepted':1,'unknown':1}) self.assertEqual(manifest['unmatched_public_decisions'],1) self.assertEqual(len(Path(manifest['unmatched_evidence_file']).read_text().splitlines()),1) rows=[json.loads(line) for line in Path(manifest['file']).read_text().splitlines()] self.assertEqual(rows[2]['decision_events'],[]) self.assertNotIn('PRIVATE REVIEW BODY',json.dumps(rows)) db=self.root/'db.sqlite' ingest_file(db,manifest['file']);ingest_file(db,manifest['file']) with sqlite3.connect(db) as connection: self.assertEqual(connection.execute('SELECT count(*) FROM submissions').fetchone()[0],3) self.assertEqual(connection.execute('SELECT count(*) FROM decisions').fetchone()[0],2) self.assertEqual(connection.execute('PRAGMA foreign_key_check').fetchall(),[]) def test_identical_copies_coalesce_and_conflicting_copies_fail(self): paper=self.paper();decision=self.decision() path,sha=self.archive([paper,paper],[decision,decision]) manifest=collect(path,self.root/'out',[2026],sha)[2026] self.assertEqual(manifest['count'],1) self.assertEqual(manifest['archive_counts']['duplicate_decisions'],1) different=self.paper();different['content']['title']={'value':'Conflicting title'} path,sha=self.archive([paper,different],[decision]) with self.assertRaisesRegex(ValueError,'Conflicting copies'): collect(path,self.root/'bad',[2026],sha) self.assertFalse((self.root/'bad'/'openreview-snapshot-2026-main.jsonl').exists()) def test_checksum_failure_does_not_publish_output(self): path,sha=self.archive([self.paper()],[self.decision()]) with self.assertRaisesRegex(ValueError,'checksum mismatch'): collect(path,self.root/'out',[2026],'0'*64) self.assertFalse((self.root/'out').exists()) def test_legacy_invitation_and_plain_decision_value(self): note=self.decision();note['invitation']=note.pop('invitations')[0] note['content']['decision']='Desk Reject' self.assertEqual(decision_record(note,2026)[0]['status'],'desk_rejected') note['_paper_forum']='wrong' self.assertEqual(decision_record(note,2026)[1],'forum_mismatch') if __name__=='__main__':unittest.main()