Files
NexusOS/tests/test_mail.py
T
janvanwan 42eaed647a feat: sync with upstream — v1.2.0, in-app updates, Projects, modules
Brings the public tree back in line with the development repo after several
weeks of drift caused by a stale publish include list.

New:
- In-app update path: GET /update/check compares the checkout against
  origin/main and POST /update/apply runs `ncp upgrade` detached (pull,
  rebuild, restart). The sidebar shows the version, checks on click, and
  offers an "update available" pill.
- Projects: a project workspace groups chats and RAG documents, with
  per-project instructions and document retrieval scoped to the active
  project. Replaces the standalone Documents page.
- modules/: auto-discovered feature plugins (mail, network) with their
  frontend counterparts and tests.
- Memory curation runs in-process (synapse/memory/curator.py) on the chat
  model when a conversation goes idle. The separate memory service on :8001
  is gone, along with the launcher lines that started it.

Also: the KDE theme, panel and Promethean terminal assets, the full test
suite, and VERSION 1.2.0.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-08-25 09:13:55 -05:00

76 lines
2.9 KiB
Python

"""Mail account config — offline (no live IMAP/SMTP)."""
from modules.mail import backend as mail
def test_account_roundtrip_and_password_masking(tmp_path, monkeypatch):
monkeypatch.setattr(mail, "_ACCOUNT_FILE", tmp_path / "acct.json")
assert mail.load_accounts() == []
pub = mail.save_account(None, {
"username": "me@icloud.com",
"password": "app-specific-pw",
"imap_host": "imap.mail.me.com",
"from_addr": "nexus@enderofwings.com",
})
account_id = pub["id"]
# public view exposes a flag, never the secret
assert pub["has_password"] is True
assert "password" not in pub
assert pub["configured"] is True
assert mail.is_configured(account_id) is True
# a blank password on update keeps the stored one (write-only field)
mail.save_account(account_id, {"from_name": "Nexus"})
assert mail.get_account(account_id)["password"] == "app-specific-pw"
assert mail.get_account(account_id)["from_name"] == "Nexus"
# defaults target iCloud
assert mail.get_account(account_id)["smtp_host"] == "smtp.mail.me.com"
def test_multiple_accounts_are_independent(tmp_path, monkeypatch):
monkeypatch.setattr(mail, "_ACCOUNT_FILE", tmp_path / "acct.json")
a = mail.save_account(None, {"username": "a@icloud.com", "password": "pw-a", "label": "Personal"})
b = mail.save_account(None, {"username": "b@icloud.com", "password": "pw-b", "label": "Work"})
assert a["id"] != b["id"]
accounts = mail.public_accounts()
assert {x["id"] for x in accounts} == {a["id"], b["id"]}
mail.delete_account(a["id"])
remaining = mail.public_accounts()
assert len(remaining) == 1
assert remaining[0]["id"] == b["id"]
assert mail.is_configured(a["id"]) is False
def test_creds_file_lives_outside_the_db(monkeypatch, tmp_path):
# Mail secrets must never ride in memory.db (which bin/sync.py dumps to git).
monkeypatch.setattr(mail, "_ACCOUNT_FILE", tmp_path / "acct.json")
mail.save_account(None, {"username": "u", "password": "p"})
assert (tmp_path / "acct.json").exists()
from synapse.memory.store import PersistentMemoryStore
assert "password" not in PersistentMemoryStore._SETTINGS_DEFAULTS
def test_legacy_single_account_file_migrates(tmp_path, monkeypatch):
import json
f = tmp_path / "acct.json"
f.write_text(json.dumps({
"imap_host": "imap.mail.me.com", "imap_port": 993,
"smtp_host": "smtp.mail.me.com", "smtp_port": 587,
"username": "legacy@icloud.com", "password": "old-pw",
"from_addr": "legacy@enderofwings.com", "from_name": "",
}))
monkeypatch.setattr(mail, "_ACCOUNT_FILE", f)
accounts = mail.load_accounts()
assert len(accounts) == 1
assert accounts[0]["username"] == "legacy@icloud.com"
assert "id" in accounts[0]
# migration is persisted — the file is rewritten in list form
assert json.loads(f.read_text())["accounts"][0]["username"] == "legacy@icloud.com"