Big changes
This commit is contained in:
parent
4240ad38e2
commit
e3f0d63fec
99 changed files with 8804 additions and 1731 deletions
252
tests/integration/test_data_repair_jobs.py
Normal file
252
tests/integration/test_data_repair_jobs.py
Normal file
|
|
@ -0,0 +1,252 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
from sqlalchemy import text
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.services.domains.dbops import run_publication_link_repair
|
||||
from tests.integration.helpers import insert_user
|
||||
|
||||
|
||||
async def _insert_scholar_profile(
|
||||
db_session: AsyncSession,
|
||||
*,
|
||||
user_id: int,
|
||||
scholar_id: str,
|
||||
display_name: str,
|
||||
baseline_completed: bool = False,
|
||||
) -> int:
|
||||
result = await db_session.execute(
|
||||
text(
|
||||
"""
|
||||
INSERT INTO scholar_profiles (user_id, scholar_id, display_name, is_enabled, baseline_completed)
|
||||
VALUES (:user_id, :scholar_id, :display_name, true, :baseline_completed)
|
||||
RETURNING id
|
||||
"""
|
||||
),
|
||||
{
|
||||
"user_id": user_id,
|
||||
"scholar_id": scholar_id,
|
||||
"display_name": display_name,
|
||||
"baseline_completed": baseline_completed,
|
||||
},
|
||||
)
|
||||
return int(result.scalar_one())
|
||||
|
||||
|
||||
async def _insert_publication(
|
||||
db_session: AsyncSession,
|
||||
*,
|
||||
fingerprint: str,
|
||||
title_raw: str,
|
||||
title_normalized: str,
|
||||
citation_count: int,
|
||||
) -> int:
|
||||
result = await db_session.execute(
|
||||
text(
|
||||
"""
|
||||
INSERT INTO publications (fingerprint_sha256, title_raw, title_normalized, citation_count)
|
||||
VALUES (:fingerprint, :title_raw, :title_normalized, :citation_count)
|
||||
RETURNING id
|
||||
"""
|
||||
),
|
||||
{
|
||||
"fingerprint": fingerprint,
|
||||
"title_raw": title_raw,
|
||||
"title_normalized": title_normalized,
|
||||
"citation_count": citation_count,
|
||||
},
|
||||
)
|
||||
return int(result.scalar_one())
|
||||
|
||||
|
||||
async def _insert_scholar_publication_link(
|
||||
db_session: AsyncSession,
|
||||
*,
|
||||
scholar_profile_id: int,
|
||||
publication_id: int,
|
||||
) -> None:
|
||||
await db_session.execute(
|
||||
text(
|
||||
"""
|
||||
INSERT INTO scholar_publications (scholar_profile_id, publication_id, is_read)
|
||||
VALUES (:scholar_profile_id, :publication_id, false)
|
||||
"""
|
||||
),
|
||||
{
|
||||
"scholar_profile_id": scholar_profile_id,
|
||||
"publication_id": publication_id,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
async def _insert_queue_item(
|
||||
db_session: AsyncSession,
|
||||
*,
|
||||
user_id: int,
|
||||
scholar_profile_id: int,
|
||||
reason: str,
|
||||
) -> None:
|
||||
await db_session.execute(
|
||||
text(
|
||||
"""
|
||||
INSERT INTO ingestion_queue_items (user_id, scholar_profile_id, reason)
|
||||
VALUES (:user_id, :scholar_profile_id, :reason)
|
||||
"""
|
||||
),
|
||||
{
|
||||
"user_id": user_id,
|
||||
"scholar_profile_id": scholar_profile_id,
|
||||
"reason": reason,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
async def _count_rows(
|
||||
db_session: AsyncSession,
|
||||
*,
|
||||
sql: str,
|
||||
params: dict[str, int],
|
||||
) -> int:
|
||||
result = await db_session.execute(text(sql), params)
|
||||
return int(result.scalar_one())
|
||||
|
||||
|
||||
async def _seed_apply_case(db_session: AsyncSession, *, user_id: int) -> tuple[int, int]:
|
||||
scholar_profile_id = await _insert_scholar_profile(
|
||||
db_session,
|
||||
user_id=user_id,
|
||||
scholar_id="repairApply01",
|
||||
display_name="Repair Apply",
|
||||
baseline_completed=True,
|
||||
)
|
||||
publication_id = await _insert_publication(
|
||||
db_session,
|
||||
fingerprint=f"{(user_id + 1):064x}",
|
||||
title_raw="Repair Apply Paper",
|
||||
title_normalized="repair apply paper",
|
||||
citation_count=9,
|
||||
)
|
||||
await _insert_scholar_publication_link(
|
||||
db_session,
|
||||
scholar_profile_id=scholar_profile_id,
|
||||
publication_id=publication_id,
|
||||
)
|
||||
await _insert_queue_item(
|
||||
db_session,
|
||||
user_id=user_id,
|
||||
scholar_profile_id=scholar_profile_id,
|
||||
reason="manual_repair_test",
|
||||
)
|
||||
await db_session.commit()
|
||||
return scholar_profile_id, publication_id
|
||||
|
||||
|
||||
async def _assert_apply_effects(
|
||||
db_session: AsyncSession,
|
||||
*,
|
||||
scholar_profile_id: int,
|
||||
publication_id: int,
|
||||
) -> None:
|
||||
links_count = await _count_rows(
|
||||
db_session,
|
||||
sql="SELECT count(*) FROM scholar_publications WHERE scholar_profile_id = :scholar_profile_id",
|
||||
params={"scholar_profile_id": scholar_profile_id},
|
||||
)
|
||||
queue_count = await _count_rows(
|
||||
db_session,
|
||||
sql="SELECT count(*) FROM ingestion_queue_items WHERE scholar_profile_id = :scholar_profile_id",
|
||||
params={"scholar_profile_id": scholar_profile_id},
|
||||
)
|
||||
baseline_completed = await _count_rows(
|
||||
db_session,
|
||||
sql=(
|
||||
"SELECT count(*) FROM scholar_profiles "
|
||||
"WHERE id = :scholar_profile_id AND baseline_completed = false"
|
||||
),
|
||||
params={"scholar_profile_id": scholar_profile_id},
|
||||
)
|
||||
publication_count = await _count_rows(
|
||||
db_session,
|
||||
sql="SELECT count(*) FROM publications WHERE id = :publication_id",
|
||||
params={"publication_id": publication_id},
|
||||
)
|
||||
assert links_count == 0
|
||||
assert queue_count == 0
|
||||
assert baseline_completed == 1
|
||||
assert publication_count == 0
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
@pytest.mark.db
|
||||
@pytest.mark.asyncio
|
||||
async def test_publication_link_repair_dry_run_records_job_without_mutation(
|
||||
db_session: AsyncSession,
|
||||
) -> None:
|
||||
user_id = await insert_user(db_session, email="repair-dry@example.com", password="api-password")
|
||||
scholar_profile_id = await _insert_scholar_profile(
|
||||
db_session,
|
||||
user_id=user_id,
|
||||
scholar_id="repairDry001",
|
||||
display_name="Repair Dry",
|
||||
)
|
||||
publication_id = await _insert_publication(
|
||||
db_session,
|
||||
fingerprint=f"{user_id:064x}",
|
||||
title_raw="Repair Dry Paper",
|
||||
title_normalized="repair dry paper",
|
||||
citation_count=1,
|
||||
)
|
||||
await _insert_scholar_publication_link(
|
||||
db_session,
|
||||
scholar_profile_id=scholar_profile_id,
|
||||
publication_id=publication_id,
|
||||
)
|
||||
await db_session.commit()
|
||||
|
||||
result = await run_publication_link_repair(
|
||||
db_session,
|
||||
user_id=user_id,
|
||||
dry_run=True,
|
||||
requested_by="test-suite",
|
||||
)
|
||||
|
||||
assert result["status"] == "completed"
|
||||
assert bool(result["summary"]["dry_run"]) is True
|
||||
assert int(result["summary"]["links_deleted"]) == 0
|
||||
|
||||
links_count = await _count_rows(
|
||||
db_session,
|
||||
sql="SELECT count(*) FROM scholar_publications WHERE scholar_profile_id = :scholar_profile_id",
|
||||
params={"scholar_profile_id": scholar_profile_id},
|
||||
)
|
||||
assert links_count == 1
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
@pytest.mark.db
|
||||
@pytest.mark.asyncio
|
||||
async def test_publication_link_repair_apply_clears_links_and_can_gc_orphans(
|
||||
db_session: AsyncSession,
|
||||
) -> None:
|
||||
user_id = await insert_user(db_session, email="repair-apply@example.com", password="api-password")
|
||||
scholar_profile_id, publication_id = await _seed_apply_case(db_session, user_id=user_id)
|
||||
|
||||
result = await run_publication_link_repair(
|
||||
db_session,
|
||||
user_id=user_id,
|
||||
dry_run=False,
|
||||
gc_orphan_publications=True,
|
||||
requested_by="test-suite",
|
||||
)
|
||||
|
||||
assert result["status"] == "completed"
|
||||
assert bool(result["summary"]["dry_run"]) is False
|
||||
assert int(result["summary"]["links_deleted"]) == 1
|
||||
assert int(result["summary"]["queue_items_deleted"]) == 1
|
||||
assert int(result["summary"]["scholars_reset"]) == 1
|
||||
await _assert_apply_effects(
|
||||
db_session,
|
||||
scholar_profile_id=scholar_profile_id,
|
||||
publication_id=publication_id,
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue