This document describes the test architecture for FrameLab.
Goal: keep tests simple, fast, and useful for regression safety when adding/changing features.
- Offline by default
- Core test suite must not require real provider/API access.
- CI runs only deterministic offline tests.
- Contract-focused
- Verify message shape, parser behavior, fallback logic, and state defaults.
- Avoid overfitting to exact LLM wording.
- Minimal complexity
- Small test files mapped to app modules.
- Limited helper abstractions.
tests/test_conversation.pyto_data_urlmake_user_message(single-media backward compatibility + multi-media tagged payload pairs)messages_to_responses_input
tests/test_llm_streaming_parsers.py- usage normalization
- chat/responses delta extraction
- schema mismatch detection helper
tests/test_llm_streaming_fallback.py- Responses success path
- fallback to Chat Completions
- auto-disable behavior for known provider schema mismatch
tests/test_run_helpers.py- helper contracts (
truncate_words,validate_media_size(s), markdown plain-text conversion, transparency preview builders, media-tag helper summaries)
- helper contracts (
tests/test_app_state.py- session key defaults and non-overwrite behavior
tests/test_ui_smoke.pyusesstreamlit.testing.v1.AppTestto validate critical app flow:- Phase 1 visible by default
- Phase 2 hidden until
phase1_done - basic required-input error path for Analyze
tests/test_live_llm_smoke.pyis marked@pytest.mark.live- Not part of default CI/offline run.
- Purpose: quick sanity check against a real provider with minimal assertions.
- Requires explicit opt-in env flag:
FRAMELAB_ENABLE_LIVE_TESTS=1 - Auto-skips in CI/cloud environments.
- Pytest auto-loads local
.envviatests/conftest.py.
Live smoke provider settings resolve in this order:
TEST_PROVIDER(optional explicit override)- otherwise
config.toml→defaults.provider
Then model/base URL are resolved from env override first, then selected provider config:
TEST_MODEL→ providerdefault_modelTEST_BASE_URL→ providerbase_url
API key resolution for live smoke:
TEST_API_KEY- selected provider env key from
config.toml(e.g.OPENROUTER_API_KEY) LLM_API_KEY
If required values are missing, live smoke is skipped.
Default offline suite (recommended for development and CI):
uv run pytestRun offline explicitly (equivalent to default):
uv run pytest -q -m "not live"Run only live smoke (explicit local opt-in):
uv run pytest --liveSet FRAMELAB_ENABLE_LIVE_TESTS=1 in local .env (copied from .env.example) to enable live execution.
When changing behavior, keep regression safety strong:
- New feature → add/update at least one test.
- Bug fix → add regression test that covers the bug scenario.
- Any message/fallback/state contract change → update corresponding contract tests.
When changing media-related behavior, ensure tests cover:
- Single-media backward compatibility
- message payload still uses legacy compact media composition for one item.
- Multi-media tagged composition
- payload alternates
text(tag)+image_url/video_urlper item.
- payload alternates
- Tag helper behavior
- default tag generation (
@imageN/@videoN), duplicate-tag detection, and summary formatting.
- default tag generation (
- Transparency preview contracts
- single media keeps legacy chip count/shape.
- multi-media adds media-tags chip.
CI runs only offline suite:
uv run pytest
This keeps PR checks fast, deterministic, and provider-independent.