"""Tests for env-only account configuration. Two properties matter more than the parsing details: a malformed or missing setting is a startup failure rather than a quiet half-configuration, and the approved folder scope (Decyzja (e)) is the default so a working config is short. """ from __future__ import annotations from datetime import date import pytest from mail_imap_sync.config import ( ConfigError, account_from_env, accounts_from_env, archive_root_from_env, ) BASE = { "MAIL_ACCOUNTS": "gmail,fastmail", "MAIL_GMAIL_USER": "u@gmail.com", "MAIL_GMAIL_APP_PASSWORD": "pw1", "MAIL_FASTMAIL_USER": "u@fastmail.com", "MAIL_FASTMAIL_APP_PASSWORD": "pw2", } def env(**overrides) -> dict: merged = dict(BASE) merged.update(overrides) return {k: v for k, v in merged.items() if v is not None} class TestDefaults: def test_gmail_defaults_to_the_special_use_all_folder(self): account = account_from_env("gmail", env()) assert account.special_use == ("\\All",) assert account.folders == () def test_fastmail_defaults_to_inbox_archive_sent(self): account = account_from_env("fastmail", env()) assert account.folders == ("INBOX", "Archive", "Sent") assert account.special_use == () def test_spam_trash_and_drafts_are_not_in_any_default_scope(self): for name in ("gmail", "fastmail"): scope = account_from_env(name, env()).folders assert not {"Spam", "Trash", "Drafts"} & set(scope) def test_known_hosts_are_defaults_not_requirements(self): assert account_from_env("gmail", env()).host == "imap.gmail.com" assert account_from_env("gmail", env(MAIL_GMAIL_HOST="mail.example.com")).host \ == "mail.example.com" def test_first_tick_defaults_to_new_only(self): assert account_from_env("fastmail", env()).initial_mode == "new-only" class TestOverrides: def test_folders_override_replaces_the_default_list(self): account = account_from_env("fastmail", env(MAIL_FASTMAIL_FOLDERS="INBOX,Projects")) assert account.folders == ("INBOX", "Projects") def test_empty_override_means_none_not_fall_back_to_default(self): account = account_from_env("gmail", env(MAIL_GMAIL_SPECIAL_USE="", MAIL_GMAIL_FOLDERS="INBOX")) assert account.special_use == () assert account.folders == ("INBOX",) def test_since_mode_parses_the_date(self): account = account_from_env("gmail", env(MAIL_GMAIL_INITIAL_MODE="since", MAIL_GMAIL_INITIAL_SINCE="2026-06-15")) assert account.initial_since == date(2026, 6, 15) def test_port_override(self): assert account_from_env("gmail", env(MAIL_GMAIL_PORT="1993")).port == 1993 def test_archive_root_override(self): assert str(archive_root_from_env({"MAIL_ARCHIVE_ROOT": "/srv/mail"})) == "/srv/mail" class TestFailsLoudly: def test_missing_accounts_list(self): with pytest.raises(ConfigError, match="MAIL_ACCOUNTS"): accounts_from_env({}) def test_missing_password(self): with pytest.raises(ConfigError, match="APP_PASSWORD"): account_from_env("gmail", env(MAIL_GMAIL_APP_PASSWORD=None)) def test_missing_user(self): with pytest.raises(ConfigError, match="USER"): account_from_env("gmail", env(MAIL_GMAIL_USER=None)) def test_unknown_account_without_a_host(self): with pytest.raises(ConfigError, match="HOST"): account_from_env("mailbox-org", {"MAIL_MAILBOX_ORG_USER": "u", "MAIL_MAILBOX_ORG_APP_PASSWORD": "p"}) def test_unknown_account_with_a_host_but_no_scope(self): with pytest.raises(ConfigError, match="no scope"): account_from_env("mailbox-org", {"MAIL_MAILBOX_ORG_USER": "u", "MAIL_MAILBOX_ORG_APP_PASSWORD": "p", "MAIL_MAILBOX_ORG_HOST": "imap.mailbox.org"}) def test_bad_port(self): with pytest.raises(ConfigError, match="PORT"): account_from_env("gmail", env(MAIL_GMAIL_PORT="993x")) def test_bad_initial_mode(self): with pytest.raises(ConfigError, match="INITIAL_MODE"): account_from_env("gmail", env(MAIL_GMAIL_INITIAL_MODE="yesterday")) def test_since_mode_without_a_date(self): with pytest.raises(ConfigError, match="initial_since"): account_from_env("gmail", env(MAIL_GMAIL_INITIAL_MODE="since")) def test_bad_since_date(self): with pytest.raises(ConfigError, match="not a date"): account_from_env("gmail", env(MAIL_GMAIL_INITIAL_MODE="since", MAIL_GMAIL_INITIAL_SINCE="15/06/2026")) def test_duplicate_account_name(self): with pytest.raises(ConfigError, match="twice"): accounts_from_env(env(MAIL_ACCOUNTS="gmail,gmail")) class TestAccountsFromEnv: def test_returns_accounts_in_the_listed_order(self): accounts = accounts_from_env(env(MAIL_ACCOUNTS="fastmail,gmail")) assert [a.name for a in accounts] == ["fastmail", "gmail"] def test_a_single_account_is_a_valid_config(self): accounts = accounts_from_env(env(MAIL_ACCOUNTS="gmail")) assert [a.name for a in accounts] == ["gmail"] def test_account_name_is_the_envelope_source(self): assert accounts_from_env(env())[0].name == "gmail"