Spaces:
Sleeping
Sleeping
| """Tests for `import_checkpoints --replace` — refresh existing checkpoints in | |
| place (e.g. swapping hand-authored YouTube segments for real chapter markers), | |
| without ever blanking a resource.""" | |
| from io import StringIO | |
| import pytest | |
| from django.core.management import call_command | |
| from apps.resources import importers | |
| from apps.resources.models import Resource, ResourceCheckpoint | |
| pytestmark = pytest.mark.django_db | |
| def _video(n_existing): | |
| r = Resource.objects.create( | |
| title='Learn X', provider='YouTube', | |
| url='https://www.youtube.com/watch?v=abc123', | |
| difficulty_level='BEGINNER', duration=300, type='VIDEO', | |
| ) | |
| for i in range(1, n_existing + 1): | |
| ResourceCheckpoint.objects.create( | |
| resource=r, order_index=i, title=f'manual {i}', source='manual', | |
| ) | |
| return r | |
| def _stub_extract(monkeypatch, titles, source='youtube_api'): | |
| def fake(url, **kwargs): | |
| return importers.ExtractResult( | |
| checkpoints=[ | |
| importers.ExtractedCheckpoint(order_index=i, title=t) | |
| for i, t in enumerate(titles, start=1) | |
| ], | |
| source=source, provider='youtube', | |
| ) | |
| # patch where the command looked it up | |
| from apps.resources.management.commands import import_checkpoints as cmd | |
| monkeypatch.setattr(cmd, 'extract_checkpoints', fake) | |
| def _run(*args): | |
| out, err = StringIO(), StringIO() | |
| call_command('import_checkpoints', *args, stdout=out, stderr=err) | |
| return out.getvalue() + err.getvalue() | |
| def test_replace_swaps_when_extraction_succeeds(monkeypatch): | |
| r = _video(5) | |
| _stub_extract(monkeypatch, ['Intro', 'Setup', 'Deep Dive']) | |
| _run('--type', 'VIDEO', '--replace', '--apply') | |
| rows = list(r.checkpoints.order_by('order_index')) | |
| assert [c.title for c in rows] == ['Intro', 'Setup', 'Deep Dive'] | |
| assert all(c.source == 'youtube_api' for c in rows) | |
| # the 5 old manual rows are gone (replaced, not appended) | |
| assert r.checkpoints.count() == 3 | |
| def test_replace_keeps_existing_when_extraction_under_two(monkeypatch): | |
| r = _video(5) | |
| _stub_extract(monkeypatch, ['only one']) # <2 → must not blank | |
| _run('--type', 'VIDEO', '--replace', '--apply') | |
| rows = list(r.checkpoints.order_by('order_index')) | |
| assert [c.title for c in rows] == ['manual 1', 'manual 2', 'manual 3', | |
| 'manual 4', 'manual 5'] | |
| assert all(c.source == 'manual' for c in rows) | |
| def test_replace_requires_apply(monkeypatch): | |
| r = _video(5) | |
| _stub_extract(monkeypatch, ['Intro', 'Setup']) | |
| out = _run('--type', 'VIDEO', '--replace') # no --apply | |
| assert 'requires --apply' in out | |
| # untouched | |
| assert r.checkpoints.count() == 5 | |
| assert r.checkpoints.first().source == 'manual' | |
| def test_default_run_ignores_resources_that_have_checkpoints(monkeypatch): | |
| """Without --replace, a resource that already has checkpoints is skipped | |
| (the n=0 backfill gate), so existing rows are never touched.""" | |
| r = _video(5) | |
| _stub_extract(monkeypatch, ['Intro', 'Setup', 'Deep Dive']) | |
| _run('--type', 'VIDEO', '--apply') # no --replace | |
| assert r.checkpoints.count() == 5 | |
| assert r.checkpoints.first().source == 'manual' | |