Files
paper-atlas/tests/test_aamas2025.py
T
codex-bot 13408869c2
Metadata integrity checks / metadata (push) Successful in 2m11s
Add reviewed AAMAS 2025 publisher metadata adapter
2026-11-09 20:50:29 +08:00

86 lines
4.8 KiB
Python

import hashlib
import json
from pathlib import Path
import tempfile
import unittest
from unittest.mock import patch
from paper_atlas.collectors import aamas, aamas2025
FIXTURE = Path(__file__).parent/'fixtures/aamas2025'
class Aamas2025Tests(unittest.TestCase):
def parse(self, html=None):
return aamas2025.parse_contents(html or (FIXTURE/'contents.html').read_text(),
{'observed_at': '2026-11-09T12:31:50+00:00', 'url': aamas2025.URLS['contents']})
def test_real_toc_preserves_every_title_author_and_affiliation(self):
expected = json.loads((FIXTURE/'expected-authors.json').read_text())
self.assertEqual(hashlib.sha256((FIXTURE/'contents.html').read_bytes()).hexdigest(), expected['source_sha256'])
rows, excluded = self.parse()
self.assertEqual((len(rows), len(excluded)), (476, 3))
by_url = {r['paper_url']: r for r in expected['entries']}
for row in rows:
original = by_url[row['metadata']['paper_url']]
self.assertEqual(row['title'], original['title'])
self.assertEqual(row['authors'], [a['name'] for a in original['authors']])
self.assertEqual(row['metadata']['author_affiliations'], [a['affiliation'] for a in original['authors']])
self.assertIsNone(row['abstract'])
self.assertEqual(row['identifiers'], {})
self.assertEqual(row['status'], 'accepted')
self.assertNotIn('decision_date', row)
self.assertTrue(all(t['namespace'] == 'aamas_proceedings:publication_format' for t in row['tags']))
def test_publication_format_and_track_are_separate(self):
rows, excluded = self.parse()
groups = {k: sum(r['track'] == k for r in rows) for k in {r['track'] for r in rows}}
self.assertEqual(groups, {'proceedings_unspecified': 409, 'blue_sky': 12, 'doctoral': 25, 'demo': 21, 'jaamas': 9})
competition = next(r for r in rows if r['external_id'] == '2025/p3000')
self.assertEqual(competition['year'], 2025)
self.assertEqual(competition['metadata']['publication_format'], 'competition_report')
self.assertEqual(competition['metadata']['section_heading'], 'Demo Track')
self.assertEqual(competition['track'], 'proceedings_unspecified')
self.assertEqual(len({r['external_id'] for r in rows}), 476)
self.assertEqual([r['publisher_page'] for r in excluded], [1, 3, 4])
def test_refuses_changed_edition_inventory_or_author_markup(self):
original = (FIXTURE/'contents.html').read_text()
for html in [original.replace('AAMAS 2025:', 'AAMAS 2026:', 1),
original.replace('../pdfs/p5.pdf', '../pdfs/p14.pdf', 1),
original.replace('Paper Track</font>', 'Workshop Track</font>'),
original.replace('Amir Abolfazli', '', 1),
original.replace('[COMP24]', '[CHANGED]', 1)]:
with self.subTest(change=html[:60]):
with self.assertRaises(ValueError): self.parse(html)
def test_frozen_collection_is_partial_and_never_fetches_pdfs(self):
with tempfile.TemporaryDirectory() as tmp, patch.object(aamas2025, 'fetch', side_effect=AssertionError('No HTTP in frozen mode')):
out = Path(tmp)/'out'
report = aamas.collect(2025, out, snapshots=FIXTURE)
self.assertEqual(report['records'], 476)
self.assertFalse(report['database_written'])
self.assertFalse(report['pdfs_downloaded'])
self.assertEqual(len(report['files']), 5)
for m in report['files']:
self.assertEqual(m['status'], 'partial')
self.assertEqual(m['missing_abstract_count'], m['count'])
self.assertEqual(hashlib.sha256(Path(m['file']).read_bytes()).hexdigest(), m['file_sha256'])
receipt = report['source_receipts'][0]['response_receipt']
self.assertEqual(receipt['at_precision'], 'filesystem_write_time_recovered')
self.assertTrue((out/'aamas-proceedings-2025.json').is_file())
def test_rejects_tampered_or_unsafe_frozen_response(self):
import shutil
for mode in ['tamper', 'path', 'status']:
with self.subTest(mode=mode), tempfile.TemporaryDirectory() as tmp:
root = Path(tmp); source = root/'source'; shutil.copytree(FIXTURE, source)
p = source/'receipts.json'; r = json.loads(p.read_text())
if mode == 'tamper': (source/'contents.html').write_text('bad')
elif mode == 'path': r['receipts'][0]['file'] = '../index.html'
else: r['receipts'][0]['http_status'] = 403
p.write_text(json.dumps(r))
with self.assertRaises(ValueError): aamas.collect(2025, root/'out', snapshots=source)
self.assertFalse((root/'out/aamas-proceedings-2025.json').exists())