| """Tests for the standalone Maris human training Space app.""" |
|
|
| from __future__ import annotations |
|
|
| import importlib |
| import json |
| import sys |
| import tempfile |
| from pathlib import Path |
|
|
| from fastapi.testclient import TestClient |
|
|
| REPO_ROOT = Path(__file__).resolve().parents[2] |
| if str(REPO_ROOT) not in sys.path: |
| sys.path.insert(0, str(REPO_ROOT)) |
|
|
| human_training_space_app = importlib.import_module("huggingface_human_training_space.app") |
|
|
|
|
| def test_index_contains_private_space_entry_and_role_sections() -> None: |
| client = TestClient(human_training_space_app.app) |
|
|
| response = client.get("/") |
|
|
| assert response.status_code == 200 |
| assert "Maris AI Human Training" in response.text |
| assert "Maris AI logo" in response.text |
| assert "Private Space" in response.text |
| assert "Workspace ir atvērts uzreiz" in response.text |
| assert 'id="login-form"' not in response.text |
| assert 'id="register-form"' not in response.text |
| assert 'id="register-role"' not in response.text |
| assert "owner" in response.text |
| assert "secretary" in response.text |
| assert "trainee" in response.text |
| assert "Platformas dokumentācija" in response.text |
| assert 'class="section active" id="workspace-shell"' in response.text |
| assert 'id="human_hub_model_id"' in response.text |
| assert 'id="human_continue_from_latest_artefact"' in response.text |
| assert 'id="human-template-select"' in response.text |
| assert 'id="human-publish-button"' in response.text |
| assert "Ātra starta vadība" in response.text |
| assert "Staging pārskats" in response.text |
|
|
|
|
| def test_runtime_endpoint_exposes_roles_and_docs() -> None: |
| client = TestClient(human_training_space_app.app) |
|
|
| response = client.get("/api/runtime") |
|
|
| assert response.status_code == 200 |
| body = response.json() |
| assert body["service"] == "maris-human-training-space" |
| assert set(body["roles"]) == {"owner", "secretary", "trainee", "user"} |
| assert any(item["title"] == "Onboarding guide" for item in body["documentation"]) |
| assert "customer-support-lv" in body["templates"] |
| assert body["templates"]["customer-support-lv"]["payload"]["profile_facts"] |
| assert body["training"]["defaults"]["hub_model_id"] == "MarisUK/maris-ai-text" |
| assert isinstance(body["training"]["model_choices"], list) |
| assert body["training"]["model_choices"][0]["id"] |
| assert body["training"]["model_choices"][0]["label"] |
| assert body["auth_required"] is False |
| assert body["private_session"]["user"]["role"] == "owner" |
|
|
|
|
| def test_maybe_start_automatic_training_starts_with_human_space_defaults( |
| monkeypatch, tmp_path: Path |
| ) -> None: |
| calls: list[dict[str, object]] = [] |
|
|
| monkeypatch.setenv("MARIS_HUMAN_TRAINING_AUTO_TRAIN", "true") |
| monkeypatch.setattr(human_training_space_app, "PERSISTENT_DIR", tmp_path) |
| monkeypatch.setattr( |
| human_training_space_app, |
| "has_completed_training_artifacts", |
| lambda output_dir: False, |
| ) |
| monkeypatch.setattr( |
| human_training_space_app, |
| "_start_training_process", |
| lambda request: ( |
| calls.append(request.model_dump()) or {"pid": 77, "log_path": "/tmp/human-train.log"} |
| ), |
| ) |
|
|
| human_training_space_app._maybe_start_automatic_training() |
|
|
| assert len(calls) == 1 |
| assert calls[0]["dataset_repo"] == human_training_space_app.DEFAULT_DATASET_REPO |
| assert calls[0]["model_repo"] == human_training_space_app.DEFAULT_HUB_MODEL_ID |
| assert calls[0]["model_preset"] == "balanced" |
| assert calls[0]["continue_from_latest_artifact"] is True |
|
|
|
|
| def test_register_and_login_flow(monkeypatch, tmp_path: Path) -> None: |
| client = TestClient(human_training_space_app.app) |
| monkeypatch.setattr( |
| human_training_space_app, |
| "USERS_FILE", |
| tmp_path / "human-training-users.json", |
| ) |
| human_training_space_app.SESSION_STORE.clear() |
|
|
| register_response = client.post( |
| "/api/auth/register", |
| json={ |
| "full_name": "Māris Ozols", |
| "email": "maris@example.com", |
| "password": "drosha-parole-123", |
| "role": "owner", |
| }, |
| ) |
|
|
| assert register_response.status_code == 200 |
| register_body = register_response.json() |
| assert register_body["user"]["role"] == "owner" |
| assert register_body["role_guide"]["label"] == "Owner" |
| assert register_body["token"] |
|
|
| login_response = client.post( |
| "/api/auth/login", |
| json={ |
| "email": "maris@example.com", |
| "password": "drosha-parole-123", |
| }, |
| ) |
|
|
| assert login_response.status_code == 200 |
| login_body = login_response.json() |
| assert login_body["user"]["email"] == "maris@example.com" |
| assert login_body["platform"]["examples"] |
|
|
|
|
| def test_register_with_storage_fallback(monkeypatch) -> None: |
| client = TestClient(human_training_space_app.app) |
| original_users_file = human_training_space_app.USERS_FILE |
| fallback_root = ( |
| Path(tempfile.gettempdir()) / human_training_space_app.USER_STORE_FALLBACK_DIRNAME |
| ) |
| fallback_file = fallback_root / original_users_file.name |
| if fallback_file.exists(): |
| fallback_file.unlink() |
| original_path_mkdir = Path.mkdir |
|
|
| def fail_default_data_mkdir(self: Path, *args, **kwargs) -> None: |
| if self == original_users_file.parent: |
| raise PermissionError("read-only") |
| return original_path_mkdir(self, *args, **kwargs) |
|
|
| monkeypatch.setattr(human_training_space_app, "USERS_FILE", original_users_file) |
| monkeypatch.setattr(Path, "mkdir", fail_default_data_mkdir) |
| human_training_space_app.SESSION_STORE.clear() |
|
|
| response = client.post( |
| "/api/auth/register", |
| json={ |
| "full_name": "Māris Ozols", |
| "email": "maris-fallback@example.com", |
| "password": "drosha-parole-123", |
| "role": "owner", |
| }, |
| ) |
|
|
| assert response.status_code == 200 |
| assert fallback_file.exists() |
| payload = json.loads(fallback_file.read_text(encoding="utf-8")) |
| assert payload["maris-fallback@example.com"]["role"] == "owner" |
|
|
|
|
| def test_login_rejects_invalid_password(monkeypatch, tmp_path: Path) -> None: |
| client = TestClient(human_training_space_app.app) |
| monkeypatch.setattr( |
| human_training_space_app, |
| "USERS_FILE", |
| tmp_path / "human-training-users.json", |
| ) |
| human_training_space_app.SESSION_STORE.clear() |
|
|
| client.post( |
| "/api/auth/register", |
| json={ |
| "full_name": "Māris Ozols", |
| "email": "maris@example.com", |
| "password": "drosha-parole-123", |
| "role": "owner", |
| }, |
| ) |
|
|
| response = client.post( |
| "/api/auth/login", |
| json={ |
| "email": "maris@example.com", |
| "password": "nepareiza-parole", |
| }, |
| ) |
|
|
| assert response.status_code == 401 |
| assert response.json()["detail"] == "Nepareizs e-pasts vai parole." |
|
|
|
|
| def test_human_training_build_allows_private_space_without_session( |
| monkeypatch, tmp_path: Path |
| ) -> None: |
| client = TestClient(human_training_space_app.app) |
| monkeypatch.setattr(human_training_space_app, "PERSISTENT_DIR", tmp_path) |
|
|
| response = client.post( |
| "/api/human-training/build", |
| json={ |
| "dataset_repo": "example-user/memory-dataset", |
| "hub_model_id": "example-user/custom-model", |
| "profile_facts": ["Man vajag profesionālu latviešu valodu."], |
| }, |
| ) |
|
|
| assert response.status_code == 200 |
| assert ( |
| response.json()["manifest"]["training_request"]["hub_model_id"] |
| == "example-user/custom-model" |
| ) |
|
|
|
|
| def test_human_training_build_requires_session_when_auth_enabled( |
| monkeypatch, tmp_path: Path |
| ) -> None: |
| client = TestClient(human_training_space_app.app) |
| monkeypatch.setattr(human_training_space_app, "AUTH_REQUIRED", True) |
| monkeypatch.setattr( |
| human_training_space_app, "USERS_FILE", tmp_path / "human-training-users.json" |
| ) |
| monkeypatch.setattr(human_training_space_app, "PERSISTENT_DIR", tmp_path) |
| human_training_space_app.SESSION_STORE.clear() |
|
|
| response = client.post( |
| "/api/human-training/build", |
| json={ |
| "dataset_repo": "example-user/memory-dataset", |
| "hub_model_id": "example-user/custom-model", |
| "profile_facts": ["Man vajag profesionālu latviešu valodu."], |
| }, |
| ) |
|
|
| assert response.status_code == 401 |
|
|
|
|
| def test_human_training_build_stages_manifest_for_private_space( |
| monkeypatch, tmp_path: Path |
| ) -> None: |
| client = TestClient(human_training_space_app.app) |
| monkeypatch.setattr(human_training_space_app, "PERSISTENT_DIR", tmp_path) |
|
|
| response = client.post( |
| "/api/human-training/build", |
| json={ |
| "dataset_repo": "example-user/memory-dataset", |
| "hub_model_id": "example-user/custom-model", |
| "continue_model_path": "runs/latest", |
| "profile_facts": ["Man vajag profesionālu latviešu valodu."], |
| }, |
| ) |
|
|
| assert response.status_code == 200 |
| body = response.json() |
| assert body["manifest"]["training_request"]["hub_model_id"] == "example-user/custom-model" |
| assert body["manifest"]["training_request"]["continue_model_path"] == "runs/latest" |
|
|
|
|
| def test_human_training_build_stages_manifest_for_logged_in_user_when_auth_enabled( |
| monkeypatch, tmp_path: Path |
| ) -> None: |
| client = TestClient(human_training_space_app.app) |
| monkeypatch.setattr(human_training_space_app, "AUTH_REQUIRED", True) |
| monkeypatch.setattr( |
| human_training_space_app, "USERS_FILE", tmp_path / "human-training-users.json" |
| ) |
| monkeypatch.setattr(human_training_space_app, "PERSISTENT_DIR", tmp_path) |
| human_training_space_app.SESSION_STORE.clear() |
|
|
| register_response = client.post( |
| "/api/auth/register", |
| json={ |
| "full_name": "Māris Ozols", |
| "email": "maris@example.com", |
| "password": "drosha-parole-123", |
| "role": "owner", |
| }, |
| ) |
| token = register_response.json()["token"] |
|
|
| response = client.post( |
| "/api/human-training/build", |
| headers={"X-Session-Token": token}, |
| json={ |
| "dataset_repo": "example-user/memory-dataset", |
| "hub_model_id": "example-user/custom-model", |
| "continue_model_path": "runs/latest", |
| "profile_facts": ["Man vajag profesionālu latviešu valodu."], |
| }, |
| ) |
|
|
| assert response.status_code == 200 |
| body = response.json() |
| assert body["manifest"]["training_request"]["hub_model_id"] == "example-user/custom-model" |
| assert body["manifest"]["training_request"]["continue_model_path"] == "runs/latest" |
|
|