CoolFace
Apppublic

sahil-12kumar/IL_CMS_Tools

sourceHugging Faceupdated 6d agoView on Hugging Face
1likes
test_fetch_many.py71 linesDownload Raw Back to tests
1"""push_tags.fetch_many — the thread-parallel QID fetch every prompt builder2(AI Tagging / LO / Verify / Solutions prep) now runs on.3 4The network is mocked at pt.fetch so these tests exercise the executor wiring5(input-order output under out-of-order completion, per-QID error isolation, and6progress reported from the collector thread) without touching the CMS.7"""8import sys9import threading10import time11from pathlib import Path12 13import pytest14 15sys.path.insert(0, str(Path(__file__).resolve().parent.parent / 'backend' / 'ai_tagger'))16 17 18@pytest.fixture(scope='module')19def pt():20    import push_tags21    return push_tags22 23 24def test_fetch_many_returns_results_in_input_order(pt, monkeypatch):25    """Workers finish out of order (ILQ-1 is fastest); output must still be the26    input order so prompts built from it are stable."""27    def fake_fetch(h, qid):28        time.sleep(0.02 * int(qid[-1]))29        return {'question_id': qid, 'question': {'question_text': f'q{qid}'}}30    monkeypatch.setattr(pt, 'fetch', fake_fetch)31    out = pt.fetch_many({'h': 1}, ['ILQ-3', 'ILQ-1', 'ILQ-2'], workers=3)32    assert [o['qid'] for o in out] == ['ILQ-3', 'ILQ-1', 'ILQ-2']33    assert all('data' in o and 'error' not in o for o in out)34 35 36def test_fetch_many_isolates_per_qid_errors(pt, monkeypatch):37    def fake_fetch(h, qid):38        if qid == 'ILQ-BAD':39            raise RuntimeError('boom')40        return {'question_id': qid}41    monkeypatch.setattr(pt, 'fetch', fake_fetch)42    out = pt.fetch_many({'h': 1}, ['ILQ-1', 'ILQ-BAD', 'ILQ-2'], workers=3)43    assert [o['qid'] for o in out] == ['ILQ-1', 'ILQ-BAD', 'ILQ-2']44    assert out[1] == {'qid': 'ILQ-BAD', 'error': 'boom'}45    assert 'data' in out[0] and 'data' in out[2]46 47 48def test_fetch_many_reports_progress_from_the_caller_thread(pt, monkeypatch):49    """progress(done, total) fires on the collector (caller) thread only — the50    app's progress handlers write to a job dict under TAG_LOCK, so they must not51    be invoked from worker threads."""52    monkeypatch.setattr(pt, 'fetch', lambda h, qid: {'question_id': qid})53    seen, who = [], set()54 55    def prog(done, total):56        seen.append(done)57        who.add(threading.current_thread().name)58 59    pt.fetch_many({'h': 1}, ['ILQ-1', 'ILQ-2', 'ILQ-3'], workers=3, progress=prog)60    assert seen == [1, 2, 3]61    assert who == {'MainThread'}62 63 64def test_fetch_many_single_worker_and_empty(pt, monkeypatch):65    monkeypatch.setattr(pt, 'fetch', lambda h, qid: {'question_id': qid})66    assert pt.fetch_many({'h': 1}, ['ILQ-1', 'ILQ-2'], workers=1) == [67        {'qid': 'ILQ-1', 'data': {'question_id': 'ILQ-1'}},68        {'qid': 'ILQ-2', 'data': {'question_id': 'ILQ-2'}},69    ]70    assert pt.fetch_many({'h': 1}, []) == []71