from __future__ import annotations from unittest.mock import AsyncMock import httpx import pytest from fastapi import Request, Response from fastapi.responses import StreamingResponse from investibook.api import files as files_api def request() -> Request: return Request({"type": "http", "method": "GET", "path": "/", "headers": []}) @pytest.fixture def run9_workspace(monkeypatch): class Provider: name = "run9" preview_max_bytes = 1024 storage = None async def open_workspace_storage(self, **kwargs): assert kwargs == {"workspace_id": "ws-1", "owner_user_id": "user-1"} return self.storage provider = Provider() monkeypatch.setattr( files_api, "sandbox_provider_for_workspace", lambda workspace_id, *, user_id=None: provider, ) return provider @pytest.mark.asyncio async def test_remote_file_tree_requires_owner_and_returns_metadata( monkeypatch, run9_workspace ): owner_check = AsyncMock() monkeypatch.setattr(files_api, "require_workspace_owner", owner_check) class Storage: async def list_files(self): return [{"name": "evidences", "type": "directory", "children": []}] async def aclose(self): return None run9_workspace.storage = Storage() api_response = Response() result = await files_api.list_workspace_files( request(), "ws-1", api_response, current_user={"id": "user-1"} ) owner_check.assert_awaited_once_with("ws-1", {"id": "user-1"}) assert result == { "files": [{"name": "evidences", "type": "directory", "children": []}] } assert api_response.headers["cache-control"] == "no-store" @pytest.mark.asyncio async def test_remote_file_content_is_streamed_without_local_file( monkeypatch, run9_workspace ): monkeypatch.setattr(files_api, "require_workspace_owner", AsyncMock()) class Storage: async def open_file(self, path): assert path == "evidences/report.json" return httpx.Response( 200, headers={"Content-Length": "7"}, request=httpx.Request("GET", "https://run9.test/file"), ) async def iter_file(self, response): yield b'{"a":1}' async def aclose(self): return None run9_workspace.storage = Storage() response = await files_api.get_workspace_file( request(), "ws-1", "evidences/report.json", current_user={"id": "user-1"}, ) assert isinstance(response, StreamingResponse) assert response.headers["content-length"] == "7" assert response.headers["cache-control"] == "no-store" chunks = [chunk async for chunk in response.body_iterator] assert chunks == [b'{"a":1}'] @pytest.mark.asyncio async def test_local_file_content_disables_browser_cache(monkeypatch, tmp_path): monkeypatch.setattr(files_api, "sandbox_provider_for_workspace", lambda *_args: None) monkeypatch.setattr(files_api, "require_workspace_access", AsyncMock()) monkeypatch.setattr(files_api, "WORKSPACES_DIR", tmp_path) evidence_dir = tmp_path / "ws-1" / "evidences" evidence_dir.mkdir(parents=True) (evidence_dir / "report.md").write_text("latest content", encoding="utf-8") response = await files_api.get_workspace_file( request(), "ws-1", "evidences/report.md", download=False, current_user={"id": "user-1"}, ) assert response.headers["cache-control"] == "no-store" assert response.headers["pragma"] == "no-cache" assert response.headers["expires"] == "0"