fix: wire dry-run source state policy

This commit is contained in:
2026-09-11 00:05:26 +02:00
committed by Antoine Van Elstraete
parent 642bbf16c1
commit 145dace289
2 changed files with 72 additions and 3 deletions

View File

@@ -126,6 +126,7 @@ class PipelineRunner:
:rtype: PipelineRunner
"""
effective_dry_run = settings.app.dry_run if dry_run is None else dry_run
persistence_enabled = not effective_dry_run
theoretical_provider = get_theoretical_provider(
settings.app.theoretical_agenda_path,
settings.app.school_holidays_path,
@@ -136,7 +137,9 @@ class PipelineRunner:
AgendaComparator(theoretical_provider) if theoretical_provider is not None else None
)
blog_client = BlogRSSClient(settings.blog.rss_url) if settings.blog.enabled else None
blog_state = BlogRSSState() if settings.blog.enabled else None
blog_state = (
BlogRSSState(persistence_enabled=persistence_enabled) if settings.blog.enabled else None
)
return cls(
settings=settings,
pronote_fetcher=PronoteFetcher(
@@ -144,7 +147,9 @@ class PipelineRunner:
PronoteClient(
settings.pronote,
auth_state=(
PronoteAuthState() if settings.pronote.auth_mode == "qr_token" else None
PronoteAuthState(persistence_enabled=persistence_enabled)
if settings.pronote.auth_mode == "qr_token"
else None
),
),
),

View File

@@ -8,7 +8,13 @@ from typing import Any, cast
import pytest
from pydantic import SecretStr
from pronote_sync.config.settings import AISettings, AppSettings, PronoteSettings, Settings
from pronote_sync.config.settings import (
AISettings,
AppSettings,
BlogSettings,
PronoteSettings,
Settings,
)
from pronote_sync.errors import PipelineCriticalError, PipelineWarning, PronoteAuthRotationError
from pronote_sync.models.agenda import Lesson, LessonStatus, SchoolEvent
from pronote_sync.models.blog import BlogArticle
@@ -449,6 +455,64 @@ def test_from_settings_qr_token_mode_passes_auth_state_instance(
assert isinstance(constructed[0][1], PronoteAuthState)
@pytest.mark.parametrize("dry_run", [False, True])
def test_from_settings_configures_source_state_persistence_for_dry_run(
monkeypatch: pytest.MonkeyPatch,
dry_run: bool,
) -> None:
"""Composition disables source-state persistence only in dry-run mode."""
import pronote_sync.pipeline.run as run_module
blog_persistence: list[bool] = []
auth_persistence: list[bool] = []
class RecordingBlogState:
"""Blog state factory recording its persistence configuration."""
def __init__(self, *, persistence_enabled: bool = True) -> None:
"""Record the requested persistence setting.
:param persistence_enabled: Whether disk writes are enabled.
"""
blog_persistence.append(persistence_enabled)
class RecordingAuthState:
"""Authentication state factory recording its persistence configuration."""
def __init__(self, *, persistence_enabled: bool = True) -> None:
"""Record the requested persistence setting.
:param persistence_enabled: Whether disk writes are enabled.
"""
auth_persistence.append(persistence_enabled)
class RecordingClient:
"""Pronote client constructor accepting the injected auth state."""
def __init__(self, settings: PronoteSettings, *, auth_state: object) -> None:
"""Accept the composition-root dependencies.
:param settings: Pronote settings.
:param auth_state: Injected authentication state.
"""
del settings, auth_state
monkeypatch.setattr(run_module, "BlogRSSState", RecordingBlogState)
monkeypatch.setattr(run_module, "PronoteAuthState", RecordingAuthState)
monkeypatch.setattr(run_module, "PronoteClient", RecordingClient)
PipelineRunner.from_settings(
Settings(
app=AppSettings(dry_run=dry_run),
blog=BlogSettings(enabled=True),
pronote=PronoteSettings(auth_mode="qr_token"),
)
)
assert blog_persistence == [not dry_run]
assert auth_persistence == [not dry_run]
def test_runner_reuses_ical_download_and_parse_within_one_run(
monkeypatch: pytest.MonkeyPatch,
pipeline_inputs: tuple[Lesson, Homework],