mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-09-23 16:42:21 +02:00
195 lines
7.6 KiB
Python
195 lines
7.6 KiB
Python
import sqlite3
|
|
from email.message import EmailMessage
|
|
|
|
|
|
def test_attachment_filename_is_part_of_ui_index_search(tmp_path, monkeypatch):
|
|
from routes import email_routes
|
|
|
|
db_path = tmp_path / "mail.db"
|
|
conn = sqlite3.connect(db_path)
|
|
conn.execute(
|
|
"""CREATE TABLE email_message_index (
|
|
owner TEXT, account_key TEXT, folder TEXT, uid TEXT, message_id TEXT,
|
|
subject TEXT, from_name TEXT, from_address TEXT, to_text TEXT, cc_text TEXT,
|
|
date_iso TEXT, date_display TEXT, date_epoch REAL, size INTEGER, flags TEXT,
|
|
has_attachments INTEGER, attachment_names TEXT, updated_at TEXT)"""
|
|
)
|
|
conn.execute(
|
|
"INSERT INTO email_message_index VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
|
|
("felix", "acct", "INBOX", "7", "<7@example>", "Quarterly files", "A", "a@example.com",
|
|
"felix@example.com", "", "", "", 1, 10, "", 1, "PDPUK-tax-return.pdf", "now"),
|
|
)
|
|
conn.commit()
|
|
conn.close()
|
|
monkeypatch.setattr(email_routes, "SCHEDULED_DB", db_path)
|
|
|
|
rows, total, _ = email_routes._email_index_search(
|
|
"felix", "acct", "INBOX", "PDPUK-tax", 20, global_search=False
|
|
)
|
|
|
|
assert total == 1
|
|
assert rows[0]["uid"] == "7"
|
|
|
|
|
|
def test_signature_only_message_has_no_visible_attachments():
|
|
from routes.email_helpers import _has_visible_attachments
|
|
|
|
msg = EmailMessage()
|
|
msg.set_content("Regards")
|
|
msg.add_related(
|
|
b"tiny-logo",
|
|
maintype="image",
|
|
subtype="png",
|
|
cid="logo",
|
|
filename="signature.png",
|
|
disposition="inline",
|
|
)
|
|
|
|
assert _has_visible_attachments(msg) is False
|
|
|
|
|
|
def test_header_only_related_message_is_not_marked_as_attached():
|
|
from routes.email_routes import _parse_email_list_record
|
|
|
|
raw = (
|
|
b"Content-Type: multipart/related; boundary=abc\r\n"
|
|
b"Subject: Signature only\r\nFrom: a@example.com\r\n\r\n"
|
|
)
|
|
parsed = _parse_email_list_record(b"1 (UID 9 FLAGS () RFC822.SIZE 123)", raw)
|
|
assert parsed["has_attachments"] is False
|
|
|
|
|
|
def test_real_attachment_remains_visible_alongside_signature():
|
|
from routes.email_helpers import _has_visible_attachments
|
|
|
|
msg = EmailMessage()
|
|
msg.set_content("See attached")
|
|
msg.add_attachment(b"report", maintype="application", subtype="pdf", filename="report.pdf")
|
|
msg.add_attachment(b"tiny-logo", maintype="image", subtype="png", filename="logo.png")
|
|
|
|
assert _has_visible_attachments(msg) is True
|
|
|
|
|
|
def test_remote_search_explicitly_checks_mime_filename_headers():
|
|
from routes.email_routes import _email_imap_search_criteria
|
|
|
|
criteria = _email_imap_search_criteria("report.pdf")
|
|
assert "HEADER Content-Disposition" in criteria
|
|
assert "HEADER Content-Type" in criteria
|
|
|
|
|
|
def test_forwarding_filters_signature_assets_and_mobile_export_stops_bubbling():
|
|
inbox = open("static/js/emailInbox.js", encoding="utf-8").read()
|
|
document = open("static/js/document.js", encoding="utf-8").read()
|
|
|
|
assert "const forwardedAttachments = mode === 'forward'" in inbox
|
|
assert "forwardedAttachments.map" in inbox
|
|
assert ": [];" in inbox[inbox.index("const forwardedAttachments"):inbox.index("content += '\\n---\\n'", inbox.index("const forwardedAttachments"))]
|
|
assert "showExportMenu(e, e.currentTarget.getBoundingClientRect())" in document
|
|
|
|
|
|
def test_attachment_open_spins_icon_only():
|
|
library = open("static/js/emailLibrary.js", encoding="utf-8").read()
|
|
start = library.index("reader.querySelectorAll('.email-attachment-open')")
|
|
end = library.index("reader.querySelectorAll('.email-attachment-download')", start)
|
|
handler = library[start:end]
|
|
|
|
assert "openBtn.appendChild(wp.element)" in handler
|
|
assert "email-attachment-open-label" not in handler
|
|
|
|
|
|
def test_move_document_creates_destination_before_adopting_it():
|
|
document = open("static/js/document.js", encoding="utf-8").read()
|
|
start = document.index("async function moveActiveDocumentToNewChat()")
|
|
end = document.index("\n function showDocTabMenu", start)
|
|
handler = document[start:end]
|
|
|
|
assert "_autoCreateSession({ adopt: false, forceNew: true })" in handler
|
|
assert "await _moveDocToSession(activeDocId, sessionId, { switchChat: true })" in handler
|
|
|
|
|
|
def test_deferred_attachment_check_shows_feedback_and_repairs_stale_card_icon():
|
|
library = open("static/js/emailLibrary.js", encoding="utf-8").read()
|
|
start = library.index("function _loadDeferredAttachmentsIntoReader")
|
|
end = library.index('\n// "Open in new tab"', start)
|
|
loader = library[start:end]
|
|
|
|
assert "Loading attachments…" in loader
|
|
assert "matchingEmail.has_attachments = visibleCurrent.length > 0" in loader
|
|
assert "querySelector('.email-card-attachment')?.remove()" in loader
|
|
|
|
|
|
def test_attachment_cache_backfill_preserves_message_id(tmp_path, monkeypatch):
|
|
from routes import email_routes
|
|
|
|
db_path = tmp_path / "mail.db"
|
|
conn = sqlite3.connect(db_path)
|
|
conn.execute(
|
|
"""CREATE TABLE email_attachment_metadata_cache (
|
|
owner TEXT, account_key TEXT, folder TEXT, uid TEXT, message_id TEXT,
|
|
attachments_json TEXT, updated_at TEXT,
|
|
PRIMARY KEY (owner, account_key, folder, uid))"""
|
|
)
|
|
conn.execute(
|
|
"""CREATE TABLE email_message_index (
|
|
owner TEXT, account_key TEXT, folder TEXT, uid TEXT,
|
|
has_attachments INTEGER, attachment_names TEXT, updated_at TEXT,
|
|
PRIMARY KEY (owner, account_key, folder, uid))"""
|
|
)
|
|
conn.execute(
|
|
"INSERT INTO email_message_index VALUES (?,?,?,?,?,?,?)",
|
|
("felix", "acct", "INBOX", "7", 1, "old.png", "before"),
|
|
)
|
|
conn.commit()
|
|
conn.close()
|
|
monkeypatch.setattr(email_routes, "SCHEDULED_DB", db_path)
|
|
|
|
signature = [{"filename": "signature.png", "size": 1000}]
|
|
email_routes._email_attachment_meta_cache_put(
|
|
"felix", "acct", "INBOX", "7", "<message@example>", signature
|
|
)
|
|
email_routes._email_attachment_meta_cache_put(
|
|
"felix", "acct", "INBOX", "7", "", signature
|
|
)
|
|
|
|
conn = sqlite3.connect(db_path)
|
|
assert conn.execute("SELECT message_id FROM email_attachment_metadata_cache").fetchone()[0] == "<message@example>"
|
|
assert conn.execute("SELECT has_attachments FROM email_message_index").fetchone()[0] == 0
|
|
conn.close()
|
|
|
|
|
|
def test_single_email_tag_has_no_more_control():
|
|
library = open("static/js/emailLibrary.js", encoding="utf-8").read()
|
|
|
|
assert 'class="email-tags-more email-tags-more-single"' not in library
|
|
|
|
|
|
def test_email_folder_and_filter_pickers_treat_their_buttons_as_inside_clicks():
|
|
library = open("static/js/emailLibrary.js", encoding="utf-8").read()
|
|
|
|
assert library.count(
|
|
"bindMenuDismiss(menu, finishClose, e => !picker.contains(e.target))"
|
|
) == 2
|
|
assert "bindMenuDismiss(menu, finishClose, e => picker.contains(e.target))" not in library
|
|
|
|
|
|
def test_empty_reply_has_two_editable_rows_and_reply_survives_compact_toolbar():
|
|
inbox = open("static/js/emailInbox.js", encoding="utf-8").read()
|
|
library = open("static/js/emailLibrary.js", encoding="utf-8").read()
|
|
|
|
assert "<p><br></p><p><br></p>\\n" in inbox
|
|
fit_start = library.index("function _fitReaderActions")
|
|
fit_end = library.index("const _readerActionFitObserver", fit_start)
|
|
fit = library[fit_start:fit_end]
|
|
assert '[data-act="ai-reply"], [data-act="reply-all"]' in fit
|
|
assert '[data-act="reply"]' not in fit
|
|
|
|
|
|
def test_email_toolbar_places_attachment_before_link():
|
|
document = open("static/js/document.js", encoding="utf-8").read()
|
|
toolbar_start = document.index('<div class="md-toolbar-items"')
|
|
toolbar_end = document.index('</div>', toolbar_start)
|
|
toolbar = document[toolbar_start:toolbar_end]
|
|
|
|
assert toolbar.index('id="md-toolbar-attach-btn"') < toolbar.index('data-md="link"')
|