Files
simplex-chat/packages/simplex-chat-python/tests/test_native_cache.py
T
sh e63c403623 simplex-chat-python: add python library (#6954)
* docs: simplex-chat-python design and implementation plan

* bots: Python wire types codegen

* simplex-chat-python: package scaffold

* simplex-chat-python: native libsimplex loader

* simplex-chat-python: async FFI wrappers

* simplex-chat-python: ChatApi with 49 api methods

* simplex-chat-python: Bot class with decorators and dispatch

* simplex-chat-python: install CLI, example bot, README

* simplex-chat-python: audit fixes

* bots: regenerate API docs and types

Catches up the markdown, TypeScript and Python codegen outputs with two
upstream schema changes:

- APIConnectPlan.connectionLink became optional (from sh/python-lib audit
  fixes); cmdString and EBNF syntax now reflect optional parameter.
- APIAddGroupRelays command and CRGroupRelaysAdded/CRGroupRelaysAddFailed
  responses added in #6917 (relay management). The TS and markdown outputs
  were regenerated when #6917 landed but the Python types module only got
  the new entries with this regeneration.

* core: refresh SQLite query plans after relay_inactive_at migration

The M20260507_relay_inactive_at migration (#6917 / #6952) shifted the
query plans that 'Save query plans' verifies. Regenerated via the test
that owns those snapshots; no behavioral change.

* bots: keep APIConnectPlan connectionLink as required parameter

The prior audit-fixes commit changed the syntax expression to `Optional ...`
because the Haskell field is `connectionLink :: Maybe AConnectionLink`.
That misrepresents the API contract: the `Maybe` is purely an internal
signal for link-parsing failure (the handler returns `CEInvalidConnReq`
on `Nothing`), not API-level optionality. Callers MUST always pass a
connection link.

Revert the syntax expression to `Param "connectionLink"` and add a
comment so the intent is preserved next time someone audits.

Regenerates COMMANDS.md, commands.ts and _commands.py to match.
2026-05-12 12:32:01 +01:00

93 lines
3.5 KiB
Python

import zipfile
from pathlib import Path
import pytest
from simplex_chat._native import _cache_root, _resolve_libs_dir, _download
def test_cache_root_linux(tmp_path, monkeypatch):
monkeypatch.setenv("XDG_CACHE_HOME", str(tmp_path))
monkeypatch.setattr("sys.platform", "linux")
assert _cache_root() == tmp_path / "simplex-chat"
def test_cache_root_macos(tmp_path, monkeypatch):
monkeypatch.setattr("sys.platform", "darwin")
monkeypatch.setattr("pathlib.Path.home", lambda: tmp_path)
assert _cache_root() == tmp_path / "Library" / "Caches" / "simplex-chat"
def test_override_via_env(tmp_path, monkeypatch):
# _resolve_libs_dir intentionally does not validate the override directory —
# it returns it verbatim; the eventual ctypes.CDLL call surfaces any mistake.
monkeypatch.setenv("SIMPLEX_LIBS_DIR", str(tmp_path))
monkeypatch.setattr("sys.platform", "linux")
assert _resolve_libs_dir("sqlite") == tmp_path
def test_resolve_downloads_when_missing(tmp_path, monkeypatch):
monkeypatch.setenv("XDG_CACHE_HOME", str(tmp_path))
monkeypatch.setattr("sys.platform", "linux")
monkeypatch.setattr("simplex_chat._native._platform_tag", lambda: "linux-x86_64")
called = {}
def fake_download(target_root: Path, backend: str) -> None:
called["target"] = target_root
called["backend"] = backend
target_root.mkdir(parents=True, exist_ok=True)
(target_root / "libsimplex.so").touch()
monkeypatch.setattr("simplex_chat._native._download", fake_download)
libs_dir = _resolve_libs_dir("sqlite")
assert libs_dir == tmp_path / "simplex-chat" / "v6.5.1" / "sqlite"
assert called["backend"] == "sqlite"
assert (libs_dir / "libsimplex.so").exists()
def test_resolve_uses_cache_on_second_call(tmp_path, monkeypatch):
monkeypatch.setenv("XDG_CACHE_HOME", str(tmp_path))
monkeypatch.setattr("sys.platform", "linux")
cached = tmp_path / "simplex-chat" / "v6.5.1" / "sqlite"
cached.mkdir(parents=True)
(cached / "libsimplex.so").touch()
# Should NOT call _download — use the cached file.
monkeypatch.setattr(
"simplex_chat._native._download", lambda *a: pytest.fail("download should not be called")
)
assert _resolve_libs_dir("sqlite") == cached
def test_postgres_on_macos_rejected(monkeypatch):
monkeypatch.setattr("sys.platform", "darwin")
monkeypatch.setattr("simplex_chat._native._platform_tag", lambda: "macos-aarch64")
with pytest.raises(RuntimeError, match="postgres.*linux-x86_64"):
_resolve_libs_dir("postgres")
def test_atomic_install(tmp_path, monkeypatch):
"""Build a fake libs zip, mock _stream_to_file, verify extraction + atomic rename."""
# Build zip: libs/libsimplex.so + libs/libHS-stub.so
src = tmp_path / "src" / "libs"
src.mkdir(parents=True)
(src / "libsimplex.so").write_text("fake-so")
(src / "libHS-stub.so").write_text("fake-hs")
zip_path = tmp_path / "fake-libs.zip"
with zipfile.ZipFile(zip_path, "w") as zf:
for f in src.iterdir():
zf.write(f, f"libs/{f.name}")
def fake_stream(url, dest, *, timeout=60.0):
import shutil
shutil.copy(zip_path, dest)
monkeypatch.setattr("simplex_chat._native._stream_to_file", fake_stream)
monkeypatch.setattr("simplex_chat._native._platform_tag", lambda: "linux-x86_64")
target = tmp_path / "out"
_download(target, "sqlite")
assert (target / "libsimplex.so").read_text() == "fake-so"
assert (target / "libHS-stub.so").read_text() == "fake-hs"