File size: 3,242 Bytes
ffd36e0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
"""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'