feat(storage): add Evernote destination (#862)
* feat(storage): add Evernote destination * style: apply ruff auto-fix - Auto-formatted code with ruff format - Applied ruff linting fixes with --fix Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> --------- Co-authored-by: Christian Krakau-Louis <christian@Christians-Mac-mini-7.local> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
8e744c076d
commit
4b46c4baf8
@@ -35,6 +35,7 @@ class TestValidateStorageConfigs:
|
||||
"google_drive",
|
||||
"onedrive",
|
||||
"email",
|
||||
"evernote",
|
||||
"paperless",
|
||||
"uptime_kuma",
|
||||
]
|
||||
@@ -83,6 +84,13 @@ class TestValidateStorageConfigs:
|
||||
assert "DEST_EMAIL_HOST is not configured" in result["email"]
|
||||
assert "DEST_EMAIL_DEFAULT_RECIPIENT is not configured" in result["email"]
|
||||
|
||||
def test_evernote_missing_token(self):
|
||||
"""Test validation when Evernote destination auth token is missing."""
|
||||
with patch("app.utils.config_validator.validators.settings") as mock_settings:
|
||||
mock_settings.evernote_auth_token = None
|
||||
result = validate_storage_configs()
|
||||
assert "EVERNOTE_AUTH_TOKEN is not configured" in result["evernote"]
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestValidateEmailConfig:
|
||||
|
||||
@@ -663,6 +663,7 @@ def _all_should_upload_false():
|
||||
"ftp",
|
||||
"sftp",
|
||||
"email",
|
||||
"evernote",
|
||||
"onedrive",
|
||||
"s3",
|
||||
"sharepoint",
|
||||
|
||||
@@ -7,6 +7,7 @@ import pytest
|
||||
from app.tasks.send_to_all import (
|
||||
_should_upload_to_dropbox,
|
||||
_should_upload_to_email,
|
||||
_should_upload_to_evernote,
|
||||
_should_upload_to_ftp,
|
||||
_should_upload_to_google_drive,
|
||||
_should_upload_to_icloud,
|
||||
@@ -128,6 +129,14 @@ class TestShouldUploadFunctions:
|
||||
|
||||
assert _should_upload_to_email() is True
|
||||
|
||||
@patch("app.tasks.send_to_all.settings")
|
||||
def test_should_upload_to_evernote_configured(self, mock_settings):
|
||||
"""Test Evernote upload check."""
|
||||
mock_settings.evernote_enabled = True
|
||||
mock_settings.evernote_auth_token = "token"
|
||||
|
||||
assert _should_upload_to_evernote() is True
|
||||
|
||||
@patch("app.tasks.send_to_all.settings")
|
||||
def test_should_upload_to_onedrive_configured(self, mock_settings):
|
||||
"""Test OneDrive upload check."""
|
||||
@@ -248,6 +257,14 @@ class TestShouldUploadEnabledFlag:
|
||||
|
||||
assert _should_upload_to_email() is False
|
||||
|
||||
@patch("app.tasks.send_to_all.settings")
|
||||
def test_evernote_disabled_with_credentials(self, mock_settings):
|
||||
"""Test Evernote upload is blocked when disabled even with valid credentials."""
|
||||
mock_settings.evernote_enabled = False
|
||||
mock_settings.evernote_auth_token = "token"
|
||||
|
||||
assert _should_upload_to_evernote() is False
|
||||
|
||||
@patch("app.tasks.send_to_all.settings")
|
||||
def test_onedrive_disabled_with_credentials(self, mock_settings):
|
||||
"""Test OneDrive upload is blocked when disabled even with valid credentials."""
|
||||
@@ -289,6 +306,7 @@ class TestGetConfiguredServicesFromValidator:
|
||||
"Dropbox": {"configured": True, "enabled": True},
|
||||
"NextCloud": {"configured": False, "enabled": True},
|
||||
"S3 Storage": {"configured": True, "enabled": True},
|
||||
"Evernote": {"configured": True, "enabled": True},
|
||||
}
|
||||
|
||||
result = get_configured_services_from_validator()
|
||||
@@ -296,6 +314,7 @@ class TestGetConfiguredServicesFromValidator:
|
||||
assert result["dropbox"] is True
|
||||
assert result["nextcloud"] is False
|
||||
assert result["s3"] is True
|
||||
assert result["evernote"] is True
|
||||
|
||||
@patch("app.tasks.send_to_all.get_provider_status")
|
||||
def test_handles_missing_providers(self, mock_get_status):
|
||||
|
||||
@@ -8,6 +8,7 @@ from unittest.mock import Mock, patch
|
||||
import pytest
|
||||
|
||||
from app.tasks.upload_to_email import upload_to_email
|
||||
from app.tasks.upload_to_evernote import upload_to_evernote
|
||||
from app.tasks.upload_to_ftp import upload_to_ftp
|
||||
from app.tasks.upload_to_google_drive import upload_to_google_drive
|
||||
from app.tasks.upload_to_onedrive import upload_to_onedrive
|
||||
@@ -447,6 +448,7 @@ def test_all_upload_tasks_have_consistent_signature(sample_text_file):
|
||||
(upload_to_webdav, "app.tasks.upload_to_webdav"),
|
||||
(upload_to_google_drive, "app.tasks.upload_to_google_drive"),
|
||||
(upload_to_email, "app.tasks.upload_to_email"),
|
||||
(upload_to_evernote, "app.tasks.upload_to_evernote"),
|
||||
]
|
||||
|
||||
import inspect
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
import hashlib
|
||||
import json
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from app.tasks.upload_to_evernote import _build_enml, upload_to_evernote
|
||||
|
||||
|
||||
class _FakeTypes:
|
||||
class Data:
|
||||
pass
|
||||
|
||||
class Resource:
|
||||
pass
|
||||
|
||||
class ResourceAttributes:
|
||||
def __init__(self, **kwargs):
|
||||
for key, value in kwargs.items():
|
||||
setattr(self, key, value)
|
||||
|
||||
class Note:
|
||||
pass
|
||||
|
||||
|
||||
class _FakeNoteStore:
|
||||
def __init__(self):
|
||||
self.calls = []
|
||||
|
||||
def createNote(self, auth_token, note):
|
||||
self.calls.append((auth_token, note))
|
||||
note.guid = "note-guid-123"
|
||||
return note
|
||||
|
||||
|
||||
_fake_note_store = _FakeNoteStore()
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def reset_fake_store():
|
||||
global _fake_note_store
|
||||
_fake_note_store = _FakeNoteStore()
|
||||
|
||||
|
||||
def test_build_enml_escapes_metadata():
|
||||
enml = _build_enml({"sender": "A&B <Corp>"}, "abc123", "application/pdf", include_metadata=True)
|
||||
|
||||
assert "A&B <Corp>" in enml
|
||||
assert '<en-media type="application/pdf" hash="abc123"/>' in enml
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_upload_to_evernote_creates_note_with_metadata_and_pdf(tmp_path):
|
||||
pdf_path = tmp_path / "invoice.pdf"
|
||||
pdf_bytes = b"%PDF-1.4 test content"
|
||||
pdf_path.write_bytes(pdf_bytes)
|
||||
pdf_path.with_suffix(".json").write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"title": "Invoice May",
|
||||
"absender": "Example GmbH",
|
||||
"tags": ["invoice", "finance"],
|
||||
"empty": "Unknown",
|
||||
}
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
with (
|
||||
patch("app.tasks.upload_to_evernote._get_note_store", return_value=(_fake_note_store, _FakeTypes)),
|
||||
patch("app.tasks.upload_to_evernote.log_task_progress"),
|
||||
patch("app.tasks.upload_to_evernote.settings") as mock_settings,
|
||||
):
|
||||
mock_settings.evernote_auth_token = "auth-token"
|
||||
mock_settings.evernote_sandbox = True
|
||||
mock_settings.evernote_notebook_guid = "notebook-guid"
|
||||
mock_settings.evernote_default_tags = "docuelevate,archive"
|
||||
mock_settings.evernote_include_metadata = True
|
||||
|
||||
result = upload_to_evernote.apply(args=[str(pdf_path)], kwargs={"file_id": 7}).get()
|
||||
|
||||
created_note = _fake_note_store.calls[0][1]
|
||||
resource = created_note.resources[0]
|
||||
|
||||
assert result["status"] == "Completed"
|
||||
assert result["evernote_note_guid"] == "note-guid-123"
|
||||
assert created_note.title == "Invoice May"
|
||||
assert created_note.notebookGuid == "notebook-guid"
|
||||
assert created_note.tagNames == ["docuelevate", "archive", "invoice", "finance"]
|
||||
assert "Example GmbH" in created_note.content
|
||||
assert "empty" not in created_note.content
|
||||
assert f'hash="{hashlib.md5(pdf_bytes).hexdigest()}"' in created_note.content # noqa: S324
|
||||
assert resource.mime == "application/pdf"
|
||||
assert resource.attributes.fileName == "invoice.pdf"
|
||||
assert resource.data.body == pdf_bytes
|
||||
assert resource.data.bodyHash == hashlib.md5(pdf_bytes).digest() # noqa: S324
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_upload_to_evernote_requires_token(tmp_path):
|
||||
pdf_path = tmp_path / "document.pdf"
|
||||
pdf_path.write_bytes(b"%PDF-1.4")
|
||||
|
||||
with (
|
||||
patch("app.tasks.upload_to_evernote.log_task_progress"),
|
||||
patch("app.tasks.upload_to_evernote.settings", SimpleNamespace(evernote_auth_token=None)),
|
||||
):
|
||||
result = upload_to_evernote.apply(args=[str(pdf_path)])
|
||||
|
||||
assert result.failed()
|
||||
assert isinstance(result.result, ValueError)
|
||||
assert "EVERNOTE_AUTH_TOKEN" in str(result.result)
|
||||
Reference in New Issue
Block a user