You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Merge pull request #45 from Leon-Sander/repo_structure_refactor
Tests, CI, and src/ layout
- Added a contract-level test suite (20 tests) over the SQLite layer, utilities, and API request construction. See TESTING.md.
- CI runs the suite on Ubuntu and Windows (Python 3.10).
- Moved all Python source into `src/` for a clearer repo root.
### Breaking
- Run commands changed: `streamlit run src/app.py` and `python3 src/database_operations.py`. Docker/compose updated; update local clones and forks.
@@ -105,15 +107,32 @@ Running Ollama inside Docker is slow on Windows (system calls are translated bet
105
107
```
106
108
4.**Run**:
107
109
```bash
108
-
python3 database_operations.py # initializes the SQLite database
109
-
streamlit run app.py
110
+
python3 src/database_operations.py # initializes the SQLite database
111
+
streamlit run src/app.py
110
112
```
111
113
5.**Pull models** as described above.
112
114
113
115
## Configuration
114
116
115
117
Key settings live in `config.yaml` (Ollama base URL and embedding model, Whisper model, Chroma path/collection, chat-sessions DB path). Runtime options (endpoint, model, chunk size/overlap, retrieved chunks, chat memory) are adjustable in the sidebar and persisted per user.
116
118
119
+
## Testing
120
+
121
+
A small contract-level test suite covers the SQLite layer, utility functions, and API request construction (UI, audio, and real model calls stay manual by design):
122
+
123
+
```bash
124
+
pip install -r requirements-dev.txt
125
+
pytest
126
+
```
127
+
128
+
Or run it inside the app container (no local Python setup, matches the CI environment):
129
+
130
+
```bash
131
+
docker compose -f docker-compose_without_ollama.yml run --rm app sh -c "pip install --no-cache-dir -q pytest && pytest"
132
+
```
133
+
134
+
CI runs the suite on Ubuntu and Windows. See [TESTING.md](./TESTING.md) for the philosophy and what is deliberately not tested.
135
+
117
136
## Roadmap
118
137
119
138
-[ ] Additional model providers (Gemini, others)
@@ -124,6 +143,7 @@ Key settings live in `config.yaml` (Ollama base URL and embedding model, Whisper
124
143
<details>
125
144
<summary>Changelog (highlights)</summary>
126
145
146
+
-**2026-07** (v2.6.0): Contract-level test suite (21 tests) plus GitHub Actions CI on Ubuntu and Windows; moved all source into `src/` for a cleaner root. Breaking: run commands are now `streamlit run src/app.py`.
127
147
-**2025-05** (v2.5.0): file upload via the chat bar.
128
148
-**2024-09**: Model serving moved to the Ollama API; OpenAI API added.
This repo has a small, deliberate test suite: **21 contract tests** over the parts of the code whose behavior is stable by design. It is meant to stay cheap to maintain — including for forks.
4
+
5
+
## Philosophy: contracts, not structure
6
+
7
+
A test here pins a **behavioral promise** (what a function guarantees to its callers), never an implementation detail. The intended property: **a test only fails when behavior actually changed.** If you fork this repo, rewrite internals freely — a red test means you changed something user-visible, which is exactly when you want to know.
8
+
9
+
A few places accept **medium churn** deliberately, each documented inline where it occurs:
10
+
11
+
-`test_pdf_chat_stuffs_retrieved_context_into_prompt` patches the internal `load_vectordb` seam (the alternative is a real Chroma + Ollama instance).
12
+
-`test_command_dispatch` patches the internal `pull_model_in_background` seam (the alternative is asyncio + a live Ollama pull).
13
+
- The handler tests patch two values that src binds **at import time** (`chat_api_handler.config`, `chat_api_handler.openai_api_key`). A behavior-preserving refactor to call-time lookups would require updating the two fixtures at the top of `tests/test_chat_api_handler.py` — a two-line fix, accepted as the cost of avoiding real env vars and network.
|`tests/test_chat_api_handler.py` (8) | endpoint routing (ollama/openai/unknown), request bodies + auth headers, image wire formats (Ollama bare-b64 vs OpenAI data URL), RAG context stuffing, error-shape handling (string vs nested object) |
22
+
23
+
Counts are executed test cases, not test functions: `test_database_operations.py` has 9 test functions but one is parametrized over `["image", "audio"]`, so it runs 10 cases — 21 in total.
24
+
25
+
## What deliberately stays untested (and why)
26
+
27
+
-**UI structure and CSS** (`app.py`, `html_templates.py`) — Streamlit class names churn every release; tests would break without any behavior change.
28
+
-**Audio transcription** (`audio_handler.py`) — needs Whisper model downloads; the browser microphone path can't be unit-tested anyway.
29
+
-**Real model calls / PDF ingestion** — network- and model-dependent; the request *construction* is tested, the live call is not.
30
+
-**Trivial helpers** (`convert_ns_to_seconds`, `get_avatar`, timestamp formatting) — near-zero regression value for the count they add.
31
+
-**The manual smoke test remains**: launch the app, send a text message, record audio once. That is the irreducible browser part.
32
+
33
+
## Running
34
+
35
+
From the **repo root**:
36
+
37
+
```bash
38
+
pip install -r requirements-dev.txt # includes requirements.txt
39
+
pytest
40
+
```
41
+
42
+
CI runs the same suite on Ubuntu and Windows (Python 3.10) on every push/PR to `main` — which also doubles as a "does it still install on Windows" check.
43
+
44
+
## Gotchas when writing new tests
45
+
46
+
The src modules bind several things **at import time** — patch the right namespace:
47
+
48
+
| To fake | Patch | Why |
49
+
|---------|-------|-----|
50
+
| OpenAI key |`chat_api_handler.openai_api_key`|`os.getenv` ran at import; `monkeypatch.setenv` does nothing |
51
+
| Ollama base URL |`chat_api_handler.config`| module-level `load_config()` froze it |
52
+
| Vector DB |`chat_api_handler.load_vectordb`| from-import binding; patching `vectordb_handler` does nothing |
| Session state |`streamlit.session_state` → `FakeSessionState` (conftest) | never rely on bare-mode session_state |
55
+
56
+
Other invariants:
57
+
58
+
- An autouse `no_network` fixture replaces `requests.get/post` and blocks raw socket connects (which also covers `aiohttp` and `requests.Session`) — a test that reaches for the network fails loudly instead of silently hitting a real endpoint.
59
+
- DB tests build their own `DatabaseManager` on `tmp_path` and must `close()` it (Windows cannot delete open sqlite files). Never assert through the module-level singleton in `database_operations`.
60
+
-`tests/conftest.py` chdirs into a throwaway **sandbox** (with its own minimal `config.yaml` and `chat_sessions/`) before anything imports the src modules — their import-time side effects (config read, sqlite creation) land in the sandbox, the repo's real `config.yaml` and `chat_sessions/` are never read or written, and pytest works from any invocation directory.
0 commit comments