Spaces:
Running
Running
| """HF-backed API tests for DocVault.""" | |
| from __future__ import annotations | |
| import io | |
| import os | |
| import unittest | |
| from unittest.mock import patch | |
| os.environ.setdefault("STORAGE_MODE", "HF") | |
| os.environ.setdefault("HF_TOKEN", "hf_test_token") | |
| os.environ.setdefault("HF_REPO_ID", "test-user/docvault-storage") | |
| os.environ.setdefault("HF_REPO_TYPE", "dataset") | |
| os.environ.setdefault("SECRET_KEY", "test-secret") | |
| from server.app import create_app | |
| from server.storage import factory | |
| class FakeCommit: | |
| def __init__(self, commit_id: str, title: str): | |
| self.commit_id = commit_id | |
| self.title = title | |
| self.message = title | |
| self.created_at = __import__("datetime").datetime.now(__import__("datetime").timezone.utc) | |
| self.authors = ["test"] | |
| class FakeHfApi: | |
| def __init__(self, token=None): | |
| self.token = token | |
| self.files = {} | |
| self.commits = [] | |
| self.repo_created = False | |
| def repo_info(self, repo_id, repo_type): | |
| if not self.repo_created: | |
| raise RuntimeError("repo missing") | |
| return {"repo_id": repo_id, "repo_type": repo_type} | |
| def create_repo(self, repo_id, repo_type, private=True, exist_ok=True): | |
| self.repo_created = True | |
| return {"repo_id": repo_id, "repo_type": repo_type} | |
| def list_repo_files(self, repo_id, repo_type): | |
| return sorted(self.files.keys()) | |
| def create_commit(self, repo_id, repo_type, operations, commit_message): | |
| for operation in operations: | |
| operation_type = operation.__class__.__name__ | |
| if operation_type == "CommitOperationAdd": | |
| self.files[operation.path_in_repo] = bytes(operation.path_or_fileobj) | |
| elif operation_type == "CommitOperationDelete": | |
| self.files.pop(operation.path_in_repo, None) | |
| elif operation_type == "CommitOperationCopy": | |
| self.files[operation.path_in_repo] = self.files[operation.src_path_in_repo] | |
| self.commits.append(FakeCommit(f"commit-{len(self.commits) + 1}", commit_message)) | |
| return {"commit_message": commit_message} | |
| def delete_file(self, path_in_repo, repo_id, repo_type, commit_message): | |
| self.files.pop(path_in_repo, None) | |
| self.commits.append(FakeCommit(f"commit-{len(self.commits) + 1}", commit_message)) | |
| def list_repo_commits(self, repo_id, repo_type): | |
| return list(reversed(self.commits)) | |
| class DocVaultApiTestCase(unittest.TestCase): | |
| def setUp(self): | |
| self.api = FakeHfApi() | |
| self.hf_patcher = patch("server.storage.hf.HfApi", return_value=self.api) | |
| self.hf_patcher.start() | |
| factory._storage_instance = None | |
| self.app = create_app() | |
| self.client = self.app.test_client() | |
| self.headers = {"X-User-ID": "test_user"} | |
| def tearDown(self): | |
| factory._storage_instance = None | |
| self.hf_patcher.stop() | |
| def test_health_check(self): | |
| response = self.client.get("/api/health") | |
| self.assertEqual(response.status_code, 200) | |
| payload = response.get_json() | |
| self.assertEqual(payload, { | |
| "storage": "HF", | |
| "repo": "test-user/docvault-storage", | |
| "status": "ok", | |
| }) | |
| def test_create_folder(self): | |
| response = self.client.post( | |
| "/api/create-folder", | |
| json={"folder_path": "Docs"}, | |
| headers=self.headers, | |
| ) | |
| self.assertEqual(response.status_code, 201) | |
| payload = response.get_json() | |
| self.assertTrue(payload["success"]) | |
| self.assertEqual(payload["folder"]["path"], "Docs") | |
| self.assertIn("test_user/Docs/.gitkeep", self.api.files) | |
| def test_upload_file(self): | |
| response = self.client.post( | |
| "/api/upload", | |
| data={"folder_path": "Docs", "file": (io.BytesIO(b"hello"), "file.txt")}, | |
| headers=self.headers, | |
| ) | |
| self.assertEqual(response.status_code, 201) | |
| payload = response.get_json() | |
| self.assertTrue(payload["success"]) | |
| self.assertEqual(payload["file"]["path"], "Docs/file.txt") | |
| self.assertIn("test_user/Docs/file.txt", self.api.files) | |
| def test_duplicate_upload_is_renamed(self): | |
| first = self.client.post( | |
| "/api/upload", | |
| data={"folder_path": "Docs", "file": (io.BytesIO(b"a"), "file.txt")}, | |
| headers=self.headers, | |
| ) | |
| second = self.client.post( | |
| "/api/upload", | |
| data={"folder_path": "Docs", "file": (io.BytesIO(b"b"), "file.txt")}, | |
| headers=self.headers, | |
| ) | |
| self.assertEqual(first.status_code, 201) | |
| self.assertEqual(second.status_code, 201) | |
| self.assertEqual(second.get_json()["file"]["name"], "file_1.txt") | |
| self.assertIn("test_user/Docs/file_1.txt", self.api.files) | |
| def test_rename_folder(self): | |
| self.client.post("/api/create-folder", json={"folder_path": "Old"}, headers=self.headers) | |
| self.client.post( | |
| "/api/upload", | |
| data={"folder_path": "Old", "file": (io.BytesIO(b"content"), "note.txt")}, | |
| headers=self.headers, | |
| ) | |
| response = self.client.post( | |
| "/api/rename", | |
| json={"item_path": "Old", "new_name": "New"}, | |
| headers=self.headers, | |
| ) | |
| self.assertEqual(response.status_code, 200) | |
| payload = response.get_json() | |
| self.assertTrue(payload["success"]) | |
| self.assertIn("test_user/New/note.txt", self.api.files) | |
| self.assertNotIn("test_user/Old/note.txt", self.api.files) | |
| def test_delete_file(self): | |
| self.client.post( | |
| "/api/upload", | |
| data={"folder_path": "", "file": (io.BytesIO(b"content"), "note.txt")}, | |
| headers=self.headers, | |
| ) | |
| response = self.client.post( | |
| "/api/delete", | |
| json={"path": "note.txt", "type": "file"}, | |
| headers=self.headers, | |
| ) | |
| self.assertEqual(response.status_code, 200) | |
| self.assertTrue(response.get_json()["success"]) | |
| self.assertNotIn("test_user/note.txt", self.api.files) | |
| def test_delete_folder(self): | |
| self.client.post("/api/create-folder", json={"folder_path": "Trash"}, headers=self.headers) | |
| self.client.post( | |
| "/api/upload", | |
| data={"folder_path": "Trash", "file": (io.BytesIO(b"content"), "note.txt")}, | |
| headers=self.headers, | |
| ) | |
| response = self.client.post( | |
| "/api/delete", | |
| json={"path": "Trash", "type": "folder"}, | |
| headers=self.headers, | |
| ) | |
| self.assertEqual(response.status_code, 200) | |
| self.assertTrue(response.get_json()["success"]) | |
| self.assertFalse(any(path.startswith("test_user/Trash/") for path in self.api.files)) | |
| if __name__ == "__main__": | |
| unittest.main() | |