Repository navigation
fix: handle full collection sync - #160
Conversation
e9e8907 to
c5f071b
Compare
lervag
left a comment
There was a problem hiding this comment.
Thanks, I appreciate the PR, it addresses a real bug!
On tests/test_sync.py: Thanks for adding these. I'd like to redirect the effort though, because as written they don't constrain much.
The tests construct the object under test with Anki.__new__(Anki) and assign _profile and col by hand, then assert exact call sequences against a hand-written FakeCollection. That describes the implementation rather than the behaviour: assert collection.operations == [("normal", True), ("backup", ...), ("close", None), ...] breaks on any harmless reordering, while real defects pass straight through. There are two real defects in this PR that passes all seven tests:
-
The confirmation prompt is never visible. It's issued inside the live Progress block, so rich erases it on the next refresh (~100ms). Under a pty you get a spinner and an apparently hung program. Pressing Enter takes the default and aborts. The tests can't see this because
console.confirmis monkeypatched away. That is, the test substitutes exactly the part that's broken. -
sync()now raisesAbortwith no profile or sync key, which__exit__reaches viaauto_sync. With"auto_sync": truein~/.config/apy/apy.json, this branch has 25 failed tests. CI missed it because CI has noapy.jsonconfig. And yes, this is a pre-existing gap in the test setup which I'll fix separately.
What I'd find more valuable:
- Keep the behavioural tests and drop the sequence ones.
test_sync_without_key_fails_instead_of_reporting_successandtest_sync_conflict_can_be_cancelledencode the actual promise of #159 and are worth having. For the rest, the membership style you already used intest_sync_uploads_when_only_full_upload_is_allowed(assert ("full", ...) in collection.operations) says what matters without freezing the call order. - For real coverage, anki ships a sync server:
SYNC_BASE=... SYNC_USER1=user:pass python -m anki.syncserver. I checked, it starts and listens on a local port. A fixture that spawns it would let you drive the exact scenario in the issue through the real backend, with no fake to keep in sync with anki's API.
I'll let the second "real coverage" item be optional here, since I can understand it may feel like a larger task. But if you want, feel free to address it.
|
@lervag Thanks for the thorough review! I'll get around to updating the PR in the next few days. |
I see you've pushed some updates now; let me know when you want me to take another look. And don't hesitate to ask for input or assistance! |
a3fff1c to
92ed7c2
Compare
|
Sorry for the delay. Rebased onto master and pushed the changes from your review. Auto-sync without a profile or sync key now prints a warning and skips the sync, and only an explicit I also took your suggestion to test against Ready for another look when you have time. |
Replace the fake-collection tests for full download, full upload and cancelled conflicts with tests that drive apy against anki's bundled sync server. Keep the unit tests for cases the server cannot easily trigger, and assert spinner visibility instead of call sequences.
Restarting a stopped rich Progress moves the cursor up by its previous height and erases the prompt and the user's answer, leaving stale spinner rows above it. Hide the tasks before stopping so the stopped display has no height, and show them again after the prompt.
832fd63 to
b0edd30
Compare
|
Great, thanks! And thanks for the nice contribution! |
Summary
Safety
Full uploads and downloads require confirmation. Downloads create and await a local backup before replacing the collection. The collection is reopened after a failed full transfer and closed if automatic sync aborts.
Tests
uv run ruff format --checkuv run ruff checkuv run pyrefly check --baseline pyrefly-baseline.jsonuv run pytest(38 passed)git diff --checkCloses #159