Files
wehub-resource-sync 6ede33ccdb
Build and Push Docker Images / create_manifest (web, surfsense-web, , cpu) (push) Has been cancelled
Build and Push Docker Images / finalize_release (push) Has been cancelled
Obsidian Plugin Lint / lint (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_web, cpu, ./surfsense_web/Dockerfile, web, surfsense-web, ubuntu-24.04-arm, linux/arm64, arm64, , runner, false, cpu) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_web, cpu, ./surfsense_web/Dockerfile, web, surfsense-web, ubuntu-latest, linux/amd64, amd64, , runner, false, cpu) (push) Has been cancelled
Build and Push Docker Images / compute_version (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cpu, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-24.04-arm, linux/arm64, arm64, , production, false, cpu) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cpu, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-latest, linux/amd64, amd64, , production, false, cpu) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cu126, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-24.04-arm, linux/arm64, arm64, -cuda126, production, true, cuda126) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cu126, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-latest, linux/amd64, amd64, -cuda126, production, true, cuda126) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cu128, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-24.04-arm, linux/arm64, arm64, -cuda, production, true, cuda) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cu128, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-latest, linux/amd64, amd64, -cuda, production, true, cuda) (push) Has been cancelled
Build and Push Docker Images / verify_digests (push) Has been cancelled
Build and Push Docker Images / create_manifest (backend, surfsense-backend, , cpu) (push) Has been cancelled
Build and Push Docker Images / create_manifest (backend, surfsense-backend, -cuda, cuda) (push) Has been cancelled
Build and Push Docker Images / create_manifest (backend, surfsense-backend, -cuda126, cuda126) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:33:44 +08:00

124 lines
3.4 KiB
Python

"""Tests for DropboxClient delta-sync methods (get_latest_cursor, get_changes)."""
from unittest.mock import AsyncMock, MagicMock
import pytest
from app.connectors.dropbox.client import DropboxClient
pytestmark = pytest.mark.unit
def _make_client() -> DropboxClient:
"""Create a DropboxClient with a mocked DB session so no real DB needed."""
client = DropboxClient.__new__(DropboxClient)
client._session = MagicMock()
client._connector_id = 1
return client
# ---------- C1: get_latest_cursor ----------
async def test_get_latest_cursor_returns_cursor_string(monkeypatch):
client = _make_client()
fake_resp = MagicMock()
fake_resp.status_code = 200
fake_resp.json.return_value = {"cursor": "AAHbKxRZ9enq…"}
monkeypatch.setattr(client, "_request", AsyncMock(return_value=fake_resp))
cursor, error = await client.get_latest_cursor("/my-folder")
assert cursor == "AAHbKxRZ9enq…"
assert error is None
client._request.assert_called_once_with(
"/2/files/list_folder/get_latest_cursor",
{
"path": "/my-folder",
"recursive": False,
"include_non_downloadable_files": True,
},
)
# ---------- C2: get_changes returns entries and new cursor ----------
async def test_get_changes_returns_entries_and_cursor(monkeypatch):
client = _make_client()
fake_resp = MagicMock()
fake_resp.status_code = 200
fake_resp.json.return_value = {
"entries": [
{".tag": "file", "name": "new.txt", "id": "id:abc"},
{".tag": "deleted", "name": "old.txt"},
],
"cursor": "cursor-v2",
"has_more": False,
}
monkeypatch.setattr(client, "_request", AsyncMock(return_value=fake_resp))
entries, new_cursor, error = await client.get_changes("cursor-v1")
assert error is None
assert new_cursor == "cursor-v2"
assert len(entries) == 2
assert entries[0]["name"] == "new.txt"
assert entries[1][".tag"] == "deleted"
# ---------- C3: get_changes handles pagination ----------
async def test_get_changes_handles_pagination(monkeypatch):
client = _make_client()
page1 = MagicMock()
page1.status_code = 200
page1.json.return_value = {
"entries": [{".tag": "file", "name": "a.txt", "id": "id:a"}],
"cursor": "cursor-page2",
"has_more": True,
}
page2 = MagicMock()
page2.status_code = 200
page2.json.return_value = {
"entries": [{".tag": "file", "name": "b.txt", "id": "id:b"}],
"cursor": "cursor-final",
"has_more": False,
}
request_mock = AsyncMock(side_effect=[page1, page2])
monkeypatch.setattr(client, "_request", request_mock)
entries, new_cursor, error = await client.get_changes("cursor-v1")
assert error is None
assert new_cursor == "cursor-final"
assert len(entries) == 2
assert {e["name"] for e in entries} == {"a.txt", "b.txt"}
assert request_mock.call_count == 2
# ---------- C4: get_changes raises on 401 ----------
async def test_get_changes_returns_error_on_401(monkeypatch):
client = _make_client()
fake_resp = MagicMock()
fake_resp.status_code = 401
fake_resp.text = "Unauthorized"
monkeypatch.setattr(client, "_request", AsyncMock(return_value=fake_resp))
entries, new_cursor, error = await client.get_changes("old-cursor")
assert error is not None
assert "401" in error
assert entries == []
assert new_cursor is None