"""Offline owned exports; no Hermes/LiveKit/deployment calls.""" import pytest from fastapi import FastAPI, HTTPException from fastapi.testclient import TestClient from control.accounts import Accounts from control.app import Settings from control.chat_exports import create_export_router, export_conversation from control.chat_store import ChatStore @pytest.fixture def env(tmp_path): accounts = Accounts(str(tmp_path / 'accounts.db')) users = [accounts.create(name, name, 'user', 'correct-horse-password') for name in ('alice', 'bob')] tokens = [accounts.issue_session(u['id'], 3600, u['password_hash'])[0] for u in users] store = ChatStore(accounts) conversation = store.create_conversation(users[0]['id'], title='Private') store.add_message(users[0]['id'], conversation['id'], 'user', 'Original question') store.add_message(users[0]['id'], conversation['id'], 'assistant', ' [Source](https://example.test)') app = FastAPI() app.state.accounts = accounts app.state.settings = Settings() app.state.chat_store = store app.include_router(create_export_router()) with TestClient(app) as client: yield accounts, store, client, users, tokens, conversation accounts.close() def test_full_projection_and_safe_markdown(env): accounts, store, client, users, tokens, conversation = env headers = {'Authorization': 'Bearer ' + tokens[0]} url = '/api/conversations/' + conversation['id'] + '/export' response = client.get(url, headers=headers) assert response.status_code == 200 document = response.json() assert len(document['messages']) == 2 assert document['messages'][0]['content'] == 'Original question' assert 'hermes_session_id' not in response.text assert response.headers['cache-control'] == 'no-store' assert response.headers['content-disposition'].startswith('attachment;') markdown = client.get(url + '?format=markdown', headers=headers) assert markdown.status_code == 200 assert markdown.headers['content-type'].startswith('text/plain') assert '[Source](https://example.test)' in markdown.text assert '' in markdown.text assert client.get(url + '?format=html', headers=headers).status_code == 422 def test_owned_revoked_deleted_and_bounded(env): accounts, store, client, users, tokens, conversation = env url = '/api/conversations/' + conversation['id'] + '/export' assert client.get(url).status_code == 401 assert client.get(url, headers={'Authorization': 'Bearer ' + tokens[1]}).status_code == 404 with pytest.raises(HTTPException) as error: export_conversation(store, users[0]['id'], conversation['id'], byte_limit=4) assert error.value.status_code == 413 accounts.revoke(tokens[0]) assert client.get(url, headers={'Authorization': 'Bearer ' + tokens[0]}).status_code == 401 store.delete_conversation(users[0]['id'], conversation['id']) with pytest.raises(HTTPException) as error: export_conversation(store, users[0]['id'], conversation['id']) assert error.value.status_code == 404