Spaces:
Running
Running
Download tests/test_cold_start_fallback.py from siddhm11/ResearchIT: direct link, hf CLI and curl.
- Browser
- Download file 3.97 kB
-
https://huggingface.co/spaces/siddhm11/ResearchIT/resolve/main/tests/test_cold_start_fallback.py
- Command line
-
hf download hf://spaces/siddhm11/ResearchIT/tests/test_cold_start_fallback.py
-
curl -L -o test_cold_start_fallback.py https://huggingface.co/spaces/siddhm11/ResearchIT/resolve/main/tests/test_cold_start_fallback.py
3.97 kB
| """A reader who skips onboarding must still get a feed. | |
| Tier 0 was gated on the reader having picked categories, with no else branch, | |
| so skipping onboarding fell through the whole cascade to "Nothing here yet" -- | |
| permanently, unless they independently found search and saved something. 68 of | |
| the first 120 users saved exactly once, so the empty-state path is not a corner | |
| case, and CLAUDE.md §3.6 recorded this fallback as already done. | |
| """ | |
| from __future__ import annotations | |
| from unittest.mock import patch | |
| import pytest | |
| from app import db, turso_svc | |
| from app.config import DEFAULT_TRENDING_CATEGORIES | |
| from app.routers import recommendations as R | |
| class _NoSaves: | |
| positive_list: list[str] = [] | |
| negative_list: list[str] = [] | |
| def has_enough_for_recs(self) -> bool: | |
| return False | |
| def feed_db(tmp_path, monkeypatch): | |
| monkeypatch.setattr(db, "DB_PATH", str(tmp_path / "cold.db")) | |
| return db.DB_PATH | |
| def _trending_spy(n=12): | |
| seen: dict = {} | |
| async def fake(categories, limit=10): | |
| seen["categories"] = set(categories) | |
| seen["limit"] = limit | |
| return [{"arxiv_id": f"2401.{i:05d}"} for i in range(n)] | |
| return fake, seen | |
| async def _build(user_id="u-skip", categories=None): | |
| await db.init_db() | |
| if categories: | |
| await db.save_onboarding_categories(user_id, list(categories)) | |
| fake, seen = _trending_spy() | |
| with patch.object(turso_svc, "fetch_trending_by_categories", side_effect=fake): | |
| entry = await R._build_feed(user_id, _NoSaves(), "q-cold") | |
| return entry, seen | |
| async def test_skipping_onboarding_still_produces_a_feed(feed_db): | |
| """The regression. This used to return None.""" | |
| entry, _ = await _build() | |
| assert entry is not None, "a reader who skipped onboarding got nothing" | |
| assert entry["ranked"], "the fallback produced an empty ranking" | |
| assert entry["trending"] is True | |
| async def test_the_default_feed_spans_more_than_machine_learning(feed_db): | |
| """Someone who told us nothing should not be shown a CS-only site.""" | |
| _, seen = await _build() | |
| assert seen["categories"] == set(DEFAULT_TRENDING_CATEGORIES) | |
| prefixes = {c.split(".")[0] for c in seen["categories"]} | |
| assert len(prefixes) >= 4, f"too narrow a default: {prefixes}" | |
| async def test_the_two_cold_start_feeds_are_distinguishable_in_the_log(feed_db): | |
| """"Your categories" and "we had nothing to go on" are different feeds and | |
| must not share a source tag, or the exposure log cannot tell them apart.""" | |
| skipped, _ = await _build(user_id="u-skip") | |
| picked, _ = await _build(user_id="u-picked", categories=["ml"]) | |
| def source_of(entry): | |
| return next(iter(entry["tags"].values()))["candidate_source"] | |
| assert source_of(skipped) == "trending_default_fallback" | |
| assert source_of(picked) == "trending_category_fallback" | |
| async def test_a_reader_who_picked_categories_still_gets_only_those(feed_db): | |
| """The fallback must not widen a deliberate choice.""" | |
| _, seen = await _build(user_id="u-picked", categories=["ml"]) | |
| assert seen["categories"] == {"cs.LG", "stat.ML"} # the "ml" group | |
| async def test_both_cold_start_feeds_report_tier_zero(feed_db): | |
| """_serving_tier drives the progress UI and the exposure log's tier column; | |
| a source tag it does not know silently reports Tier 1.""" | |
| skipped, _ = await _build(user_id="u-skip") | |
| picked, _ = await _build(user_id="u-picked", categories=["ml"]) | |
| assert R._serving_tier(skipped) == 0 | |
| assert R._serving_tier(picked) == 0 | |
| async def test_the_fallback_still_carries_real_propensities(feed_db): | |
| """Tier 0 fills slots epsilon-greedily, and §3.4b forbids logging a | |
| degenerate 1.0 on a tier that has randomness.""" | |
| entry, _ = await _build() | |
| props = [t["propensity"] for t in entry["tags"].values()] | |
| assert props and all(0.0 < p <= 1.0 for p in props) | |
| assert any(p < 1.0 for p in props), "no exploration propensity recorded" | |