test(python): don't reuse accounts

It is easier to create new chatmail accounts
than reusing existing accounts and trying to clean
them via IMAP.
Cleaning an account via IMAP takes around 200 ms.
This commit is contained in:
link2xt
2024-04-08 16:20:04 +00:00
parent c1d251010f
commit f3135f5586

View File

@@ -143,82 +143,31 @@ def testprocess(request):
class TestProcess: class TestProcess:
"""A pytest session-scoped instance to help with managing "live" account configurations.""" """A pytest session-scoped instance to help with managing "live" account configurations."""
_addr2files: Dict[str, Dict[pathlib.Path, bytes]]
def __init__(self, pytestconfig) -> None: def __init__(self, pytestconfig) -> None:
self.pytestconfig = pytestconfig self.pytestconfig = pytestconfig
self._addr2files = {} self._addr2files = {}
self._configlist: List[Dict[str, str]] = []
def get_liveconfig_producer(self): def get_liveconfig_producer(self):
"""provide live account configs, cached on a per-test-process scope """Provide live account configs"""
so that test functions can re-use already known live configs.
"""
chatmail_opt = self.pytestconfig.getoption("--chatmail") chatmail_opt = self.pytestconfig.getoption("--chatmail")
if chatmail_opt: if chatmail_opt:
# Use a chatmail instance. # Use a chatmail instance.
domain = chatmail_opt domain = chatmail_opt
MAX_LIVE_CREATED_ACCOUNTS = 10 MAX_LIVE_CREATED_ACCOUNTS = 10
for index in range(MAX_LIVE_CREATED_ACCOUNTS): for index in range(MAX_LIVE_CREATED_ACCOUNTS):
try: part = "".join(random.choices("2345789acdefghjkmnpqrstuvwxyz", k=6))
yield self._configlist[index] username = f"ci-{part}"
except IndexError: password = f"{username}${username}"
part = "".join(random.choices("2345789acdefghjkmnpqrstuvwxyz", k=6)) addr = f"{username}@{domain}"
username = f"ci-{part}" config = {"addr": addr, "mail_pw": password}
password = f"{username}${username}" print("newtmpuser {}: addr={}".format(index, config["addr"]))
addr = f"{username}@{domain}" yield config
config = {"addr": addr, "mail_pw": password}
print("newtmpuser {}: addr={}".format(index, config["addr"]))
self._configlist.append(config)
yield config
pytest.fail(f"more than {MAX_LIVE_CREATED_ACCOUNTS} live accounts requested.") pytest.fail(f"more than {MAX_LIVE_CREATED_ACCOUNTS} live accounts requested.")
else: else:
pytest.skip( pytest.skip(
"specify CHATMAIL_DOMAIN or --chatmail to provide live accounts", "specify CHATMAIL_DOMAIN or --chatmail to provide live accounts",
) )
def cache_maybe_retrieve_configured_db_files(self, cache_addr, db_target_path):
db_target_path = pathlib.Path(db_target_path)
assert not db_target_path.exists()
try:
filescache = self._addr2files[cache_addr]
except KeyError:
print("CACHE FAIL for", cache_addr)
return False
else:
print("CACHE HIT for", cache_addr)
targetdir = db_target_path.parent
write_dict_to_dir(filescache, targetdir)
return True
def cache_maybe_store_configured_db_files(self, acc):
addr = acc.get_config("addr")
assert acc.is_configured()
# don't overwrite existing entries
if addr not in self._addr2files:
print("storing cache for", addr)
basedir = pathlib.Path(acc.get_blobdir()).parent
self._addr2files[addr] = create_dict_from_files_in_path(basedir)
return True
def create_dict_from_files_in_path(base):
cachedict = {}
for path in base.glob("**/*"):
if path.is_file():
cachedict[path.relative_to(base)] = path.read_bytes()
return cachedict
def write_dict_to_dir(dic, target_dir):
assert dic
for relpath, content in dic.items():
path = target_dir.joinpath(relpath)
if not path.parent.exists():
os.makedirs(path.parent)
path.write_bytes(content)
@pytest.fixture() @pytest.fixture()
def data(request): def data(request):
@@ -275,7 +224,6 @@ class ACSetup:
def __init__(self, testprocess, init_time) -> None: def __init__(self, testprocess, init_time) -> None:
self._configured_events = Queue() self._configured_events = Queue()
self._account2state: Dict[Account, str] = {} self._account2state: Dict[Account, str] = {}
self._imap_cleaned: Set[str] = set()
self.testprocess = testprocess self.testprocess = testprocess
self.init_time = init_time self.init_time = init_time
@@ -359,17 +307,6 @@ class ACSetup:
assert acc.is_configured() assert acc.is_configured()
if not hasattr(acc, "direct_imap"): if not hasattr(acc, "direct_imap"):
acc.direct_imap = DirectImap(acc) acc.direct_imap = DirectImap(acc)
addr = acc.get_config("addr")
if addr not in self._imap_cleaned:
imap = acc.direct_imap
for folder in imap.list_folders():
if folder.lower() == "inbox" or folder.lower() == "deltachat":
assert imap.select_folder(folder)
imap.delete("1:*", expunge=True)
else:
imap.conn.folder.delete(folder)
acc.log(f"imap cleaned for addr {addr}")
self._imap_cleaned.add(addr)
class ACFactory: class ACFactory:
@@ -431,20 +368,13 @@ class ACFactory:
assert "addr" in configdict and "mail_pw" in configdict assert "addr" in configdict and "mail_pw" in configdict
return configdict return configdict
def _get_cached_account(self, addr) -> Optional[Account]:
if addr in self.testprocess._addr2files:
return self._getaccount(addr)
return None
def get_unconfigured_account(self, closed=False) -> Account: def get_unconfigured_account(self, closed=False) -> Account:
return self._getaccount(closed=closed) return self._getaccount(closed=closed)
def _getaccount(self, try_cache_addr=None, closed=False) -> Account: def _getaccount(self, closed=False) -> Account:
logid = f"ac{len(self._accounts) + 1}" logid = f"ac{len(self._accounts) + 1}"
# we need to use fixed database basename for maybe_cache_* functions to work # we need to use fixed database basename for maybe_cache_* functions to work
path = self.tmpdir.mkdir(logid).join("dc.db") path = self.tmpdir.mkdir(logid).join("dc.db")
if try_cache_addr:
self.testprocess.cache_maybe_retrieve_configured_db_files(try_cache_addr, path)
ac = Account(path.strpath, logging=self._logging, closed=closed) ac = Account(path.strpath, logging=self._logging, closed=closed)
ac._logid = logid # later instantiated FFIEventLogger needs this ac._logid = logid # later instantiated FFIEventLogger needs this
ac._evtracker = ac.add_account_plugin(FFIEventTracker(ac)) ac._evtracker = ac.add_account_plugin(FFIEventTracker(ac))
@@ -493,7 +423,7 @@ class ACFactory:
self._acsetup.init_logging(ac) self._acsetup.init_logging(ac)
return ac return ac
def new_online_configuring_account(self, cloned_from=None, cache=False, **kwargs) -> Account: def new_online_configuring_account(self, cloned_from=None, **kwargs) -> Account:
if cloned_from is None: if cloned_from is None:
configdict = self.get_next_liveconfig() configdict = self.get_next_liveconfig()
else: else:
@@ -505,12 +435,6 @@ class ACFactory:
"smtp_certificate_checks": cloned_from.get_config("smtp_certificate_checks"), "smtp_certificate_checks": cloned_from.get_config("smtp_certificate_checks"),
} }
configdict.update(kwargs) configdict.update(kwargs)
ac = self._get_cached_account(addr=configdict["addr"]) if cache else None
if ac is not None:
# make sure we consume a preconfig key, as if we had created a fresh account
self._preconfigured_keys.pop(0)
self._acsetup.add_configured(ac)
return ac
ac = self.prepare_account_from_liveconfig(configdict) ac = self.prepare_account_from_liveconfig(configdict)
self._acsetup.start_configure(ac) self._acsetup.start_configure(ac)
return ac return ac
@@ -536,11 +460,8 @@ class ACFactory:
print("all accounts online") print("all accounts online")
def get_online_accounts(self, num): def get_online_accounts(self, num):
accounts = [self.new_online_configuring_account(cache=True) for i in range(num)] accounts = [self.new_online_configuring_account() for i in range(num)]
self.bring_accounts_online() self.bring_accounts_online()
# we cache fully configured and started accounts
for acc in accounts:
self.testprocess.cache_maybe_store_configured_db_files(acc)
return accounts return accounts
def run_bot_process(self, module, ffi=True): def run_bot_process(self, module, ffi=True):