feat: complete Git Repo Manager MVP implementation

Backend (Phase 1-6):
- Pydantic schemas for request/response validation
- Service layer (SSH Key, Server, Repo, Sync)
- API routes with authentication
- FastAPI main application with lifespan management
- ORM models (SshKey, Server, Repo, SyncLog)

Frontend (Phase 7):
- Vue 3 + Element Plus + Pinia + Vue Router
- API client with Axios and interceptors
- State management stores
- All page components (Dashboard, Servers, Repos, SyncLogs, SshKeys, Settings)

Deployment (Phase 8):
- README with quick start guide
- Startup script (start.sh)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
panw
2026-03-30 16:30:13 +08:00
parent 960056c88c
commit 44921c5646
46 changed files with 6533 additions and 2 deletions

View File

@@ -0,0 +1,5 @@
"""
Tests for API routes.
This package contains tests for all FastAPI route handlers.
"""

View File

@@ -0,0 +1,495 @@
"""
Tests for Servers API routes.
Tests the following endpoints:
- POST /api/servers
- GET /api/servers
- GET /api/servers/{id}
- PUT /api/servers/{id}
- DELETE /api/servers/{id}
"""
import pytest
from fastapi.testclient import TestClient
# Valid SSH private key for testing servers
VALID_SSH_KEY = """-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACB5WfkA8wgP/KvdGFNC1ZCbBmjZnKpM/LOXRDJS7NfRAQAAAJC9AVH1vQFR
AAAAAtzc2gtZWQyNTUxOQAAACB5WfkA8wgP/KvdGFNC1ZCbBmjZnKpM/LOXRDJS7NfRAQA
AAECB5WfkA8wgP/KvdGFNC1ZCbBmjZnKpM/LOXRDJS7NfRAQAAAHHZpXRvaXRvQVJNVjJH
AAAAFGZpbGVzeXN0ZW0uY2ZnAAAAAQAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAA/////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAEAAAAEWNvbW1lbnQ6IHRlc3Qga2V5AQIDBAUHBg=="""
class TestCreateServer:
"""Tests for POST /api/servers endpoint."""
def test_create_server_success(self, client: TestClient):
"""Test creating a new server successfully."""
# First create an SSH key
ssh_response = client.post(
"/api/ssh-keys",
json={"name": "test-key", "private_key": VALID_SSH_KEY},
headers={"Authorization": "Bearer test-token"}
)
ssh_key_id = ssh_response.json()["data"]["id"]
# Create server
response = client.post(
"/api/servers",
json={
"name": "test-server",
"url": "https://gitea.example.com",
"api_token": "test-api-token-123",
"ssh_key_id": ssh_key_id
},
headers={"Authorization": "Bearer test-token"}
)
assert response.status_code == 201
data = response.json()
assert data["code"] == 0
assert data["message"] == "Server created successfully"
assert data["data"]["name"] == "test-server"
assert data["data"]["url"] == "https://gitea.example.com"
assert data["data"]["ssh_key_id"] == ssh_key_id
assert data["data"]["id"] > 0
assert data["data"]["status"] == "untested"
assert data["data"]["sync_enabled"] is False
assert data["data"]["created_at"] > 0
def test_create_server_with_sync_enabled(self, client: TestClient):
"""Test creating a server with sync enabled."""
# Create SSH key
ssh_response = client.post(
"/api/ssh-keys",
json={"name": "test-key-2", "private_key": VALID_SSH_KEY},
headers={"Authorization": "Bearer test-token"}
)
ssh_key_id = ssh_response.json()["data"]["id"]
# Create server with sync enabled
response = client.post(
"/api/servers",
json={
"name": "sync-server",
"url": "https://gitea.example.com",
"api_token": "test-api-token",
"ssh_key_id": ssh_key_id,
"sync_enabled": True,
"schedule_cron": "0 */6 * * *"
},
headers={"Authorization": "Bearer test-token"}
)
assert response.status_code == 201
data = response.json()
assert data["data"]["sync_enabled"] is True
assert data["data"]["schedule_cron"] == "0 */6 * * *"
def test_create_server_duplicate_name(self, client: TestClient):
"""Test creating a server with duplicate name fails."""
# Create SSH key
ssh_response = client.post(
"/api/ssh-keys",
json={"name": "test-key-3", "private_key": VALID_SSH_KEY},
headers={"Authorization": "Bearer test-token"}
)
ssh_key_id = ssh_response.json()["data"]["id"]
# Create first server
client.post(
"/api/servers",
json={
"name": "duplicate-server",
"url": "https://gitea.example.com",
"api_token": "test-token",
"ssh_key_id": ssh_key_id
},
headers={"Authorization": "Bearer test-token"}
)
# Try to create duplicate
response = client.post(
"/api/servers",
json={
"name": "duplicate-server",
"url": "https://gitea.example.com",
"api_token": "test-token",
"ssh_key_id": ssh_key_id
},
headers={"Authorization": "Bearer test-token"}
)
assert response.status_code == 400
assert "already exists" in response.json()["detail"]
def test_create_server_invalid_ssh_key_id(self, client: TestClient):
"""Test creating a server with non-existent SSH key fails."""
response = client.post(
"/api/servers",
json={
"name": "test-server",
"url": "https://gitea.example.com",
"api_token": "test-token",
"ssh_key_id": 99999 # Non-existent
},
headers={"Authorization": "Bearer test-token"}
)
assert response.status_code == 400
assert "not found" in response.json()["detail"]
def test_create_server_no_auth(self, client: TestClient):
"""Test creating a server without authentication fails."""
response = client.post(
"/api/servers",
json={
"name": "test-server",
"url": "https://gitea.example.com",
"api_token": "test-token",
"ssh_key_id": 1
}
)
assert response.status_code == 401
class TestListServers:
"""Tests for GET /api/servers endpoint."""
def test_list_servers_empty(self, client: TestClient):
"""Test listing servers when none exist."""
response = client.get(
"/api/servers",
headers={"Authorization": "Bearer test-token"}
)
assert response.status_code == 200
data = response.json()
assert data["code"] == 0
assert data["data"] == []
assert "0 server" in data["message"]
def test_list_servers_with_items(self, client: TestClient):
"""Test listing servers when some exist."""
# Create SSH key
ssh_response = client.post(
"/api/ssh-keys",
json={"name": "test-key-list", "private_key": VALID_SSH_KEY},
headers={"Authorization": "Bearer test-token"}
)
ssh_key_id = ssh_response.json()["data"]["id"]
# Create some servers
client.post(
"/api/servers",
json={
"name": "server-1",
"url": "https://gitea1.example.com",
"api_token": "token1",
"ssh_key_id": ssh_key_id
},
headers={"Authorization": "Bearer test-token"}
)
client.post(
"/api/servers",
json={
"name": "server-2",
"url": "https://gitea2.example.com",
"api_token": "token2",
"ssh_key_id": ssh_key_id
},
headers={"Authorization": "Bearer test-token"}
)
response = client.get(
"/api/servers",
headers={"Authorization": "Bearer test-token"}
)
assert response.status_code == 200
data = response.json()
assert data["code"] == 0
assert len(data["data"]) == 2
assert "api_token" not in data["data"][0] # Should not expose token
def test_list_servers_no_auth(self, client: TestClient):
"""Test listing servers without authentication fails."""
response = client.get("/api/servers")
assert response.status_code == 401
class TestGetServer:
"""Tests for GET /api/servers/{id} endpoint."""
def test_get_server_success(self, client: TestClient):
"""Test getting a specific server successfully."""
# Create SSH key and server
ssh_response = client.post(
"/api/ssh-keys",
json={"name": "test-key-get", "private_key": VALID_SSH_KEY},
headers={"Authorization": "Bearer test-token"}
)
ssh_key_id = ssh_response.json()["data"]["id"]
server_response = client.post(
"/api/servers",
json={
"name": "get-test-server",
"url": "https://gitea.example.com",
"api_token": "test-token",
"ssh_key_id": ssh_key_id
},
headers={"Authorization": "Bearer test-token"}
)
server_id = server_response.json()["data"]["id"]
# Get the server
response = client.get(
f"/api/servers/{server_id}",
headers={"Authorization": "Bearer test-token"}
)
assert response.status_code == 200
data = response.json()
assert data["code"] == 0
assert data["data"]["id"] == server_id
assert data["data"]["name"] == "get-test-server"
assert "api_token" not in data["data"]
def test_get_server_not_found(self, client: TestClient):
"""Test getting a non-existent server fails."""
response = client.get(
"/api/servers/99999",
headers={"Authorization": "Bearer test-token"}
)
assert response.status_code == 404
def test_get_server_no_auth(self, client: TestClient):
"""Test getting a server without authentication fails."""
response = client.get("/api/servers/1")
assert response.status_code == 401
class TestUpdateServer:
"""Tests for PUT /api/servers/{id} endpoint."""
def test_update_server_name(self, client: TestClient):
"""Test updating a server's name."""
# Create SSH key and server
ssh_response = client.post(
"/api/ssh-keys",
json={"name": "test-key-update", "private_key": VALID_SSH_KEY},
headers={"Authorization": "Bearer test-token"}
)
ssh_key_id = ssh_response.json()["data"]["id"]
server_response = client.post(
"/api/servers",
json={
"name": "old-name",
"url": "https://gitea.example.com",
"api_token": "test-token",
"ssh_key_id": ssh_key_id
},
headers={"Authorization": "Bearer test-token"}
)
server_id = server_response.json()["data"]["id"]
# Update name
response = client.put(
f"/api/servers/{server_id}",
json={"name": "new-name"},
headers={"Authorization": "Bearer test-token"}
)
assert response.status_code == 200
data = response.json()
assert data["code"] == 0
assert data["data"]["name"] == "new-name"
def test_update_server_multiple_fields(self, client: TestClient):
"""Test updating multiple server fields."""
# Create SSH key and server
ssh_response = client.post(
"/api/ssh-keys",
json={"name": "test-key-multi", "private_key": VALID_SSH_KEY},
headers={"Authorization": "Bearer test-token"}
)
ssh_key_id = ssh_response.json()["data"]["id"]
server_response = client.post(
"/api/servers",
json={
"name": "test-server",
"url": "https://gitea.example.com",
"api_token": "test-token",
"ssh_key_id": ssh_key_id
},
headers={"Authorization": "Bearer test-token"}
)
server_id = server_response.json()["data"]["id"]
# Update multiple fields
response = client.put(
f"/api/servers/{server_id}",
json={
"sync_enabled": True,
"schedule_cron": "0 */6 * * *",
"status": "testing"
},
headers={"Authorization": "Bearer test-token"}
)
assert response.status_code == 200
data = response.json()
assert data["data"]["sync_enabled"] is True
assert data["data"]["schedule_cron"] == "0 */6 * * *"
assert data["data"]["status"] == "testing"
def test_update_server_api_token(self, client: TestClient):
"""Test updating a server's API token."""
# Create SSH key and server
ssh_response = client.post(
"/api/ssh-keys",
json={"name": "test-key-token", "private_key": VALID_SSH_KEY},
headers={"Authorization": "Bearer test-token"}
)
ssh_key_id = ssh_response.json()["data"]["id"]
server_response = client.post(
"/api/servers",
json={
"name": "test-server",
"url": "https://gitea.example.com",
"api_token": "old-token",
"ssh_key_id": ssh_key_id
},
headers={"Authorization": "Bearer test-token"}
)
server_id = server_response.json()["data"]["id"]
# Update API token
response = client.put(
f"/api/servers/{server_id}",
json={"api_token": "new-token"},
headers={"Authorization": "Bearer test-token"}
)
assert response.status_code == 200
def test_update_server_not_found(self, client: TestClient):
"""Test updating a non-existent server fails."""
response = client.put(
"/api/servers/99999",
json={"name": "new-name"},
headers={"Authorization": "Bearer test-token"}
)
assert response.status_code == 404
def test_update_server_no_fields(self, client: TestClient):
"""Test updating a server with no fields fails."""
# Create SSH key and server
ssh_response = client.post(
"/api/ssh-keys",
json={"name": "test-key-nofield", "private_key": VALID_SSH_KEY},
headers={"Authorization": "Bearer test-token"}
)
ssh_key_id = ssh_response.json()["data"]["id"]
server_response = client.post(
"/api/servers",
json={
"name": "test-server",
"url": "https://gitea.example.com",
"api_token": "test-token",
"ssh_key_id": ssh_key_id
},
headers={"Authorization": "Bearer test-token"}
)
server_id = server_response.json()["data"]["id"]
# Update with empty body
response = client.put(
f"/api/servers/{server_id}",
json={},
headers={"Authorization": "Bearer test-token"}
)
assert response.status_code == 400
def test_update_server_no_auth(self, client: TestClient):
"""Test updating a server without authentication fails."""
response = client.put(
"/api/servers/1",
json={"name": "new-name"}
)
assert response.status_code == 401
class TestDeleteServer:
"""Tests for DELETE /api/servers/{id} endpoint."""
def test_delete_server_success(self, client: TestClient):
"""Test deleting a server successfully."""
# Create SSH key and server
ssh_response = client.post(
"/api/ssh-keys",
json={"name": "test-key-del", "private_key": VALID_SSH_KEY},
headers={"Authorization": "Bearer test-token"}
)
ssh_key_id = ssh_response.json()["data"]["id"]
server_response = client.post(
"/api/servers",
json={
"name": "delete-test-server",
"url": "https://gitea.example.com",
"api_token": "test-token",
"ssh_key_id": ssh_key_id
},
headers={"Authorization": "Bearer test-token"}
)
server_id = server_response.json()["data"]["id"]
# Delete the server
response = client.delete(
f"/api/servers/{server_id}",
headers={"Authorization": "Bearer test-token"}
)
assert response.status_code == 200
data = response.json()
assert data["code"] == 0
assert data["message"] == "Server deleted successfully"
# Verify it's deleted
get_response = client.get(
f"/api/servers/{server_id}",
headers={"Authorization": "Bearer test-token"}
)
assert get_response.status_code == 404
def test_delete_server_not_found(self, client: TestClient):
"""Test deleting a non-existent server fails."""
response = client.delete(
"/api/servers/99999",
headers={"Authorization": "Bearer test-token"}
)
assert response.status_code == 404
def test_delete_server_no_auth(self, client: TestClient):
"""Test deleting a server without authentication fails."""
response = client.delete("/api/servers/1")
assert response.status_code == 401

View File

@@ -0,0 +1,299 @@
"""
Tests for SSH Keys API routes.
Tests the following endpoints:
- POST /api/ssh-keys
- GET /api/ssh-keys
- GET /api/ssh-keys/{id}
- DELETE /api/ssh-keys/{id}
"""
import pytest
from fastapi.testclient import TestClient
# Valid SSH private key for testing
VALID_SSH_KEY = """-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACB5WfkA8wgP/KvdGFNC1ZCbBmjZnKpM/LOXRDJS7NfRAQAAAJC9AVH1vQFR
AAAAAtzc2gtZWQyNTUxOQAAACB5WfkA8wgP/KvdGFNC1ZCbBmjZnKpM/LOXRDJS7NfRAQA
AAECB5WfkA8wgP/KvdGFNC1ZCbBmjZnKpM/LOXRDJS7NfRAQAAAHHZpXRvaXRvQVJNVjJH
AAAAFGZpbGVzeXN0ZW0uY2ZnAAAAAQAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAA/////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAEAAAAEWNvbW1lbnQ6IHRlc3Qga2V5AQIDBAUHBg=="""
# Invalid SSH key for testing
INVALID_SSH_KEY = "not-a-valid-ssh-key"
class TestCreateSshKey:
"""Tests for POST /api/ssh-keys endpoint."""
def test_create_ssh_key_success(self, client: TestClient):
"""Test creating a new SSH key successfully."""
response = client.post(
"/api/ssh-keys",
json={
"name": "test-key",
"private_key": VALID_SSH_KEY
},
headers={"Authorization": "Bearer test-token"}
)
assert response.status_code == 201
data = response.json()
assert data["code"] == 0
assert data["message"] == "SSH key created successfully"
assert data["data"]["name"] == "test-key"
assert data["data"]["id"] > 0
assert data["data"]["fingerprint"] is not None
assert data["data"]["created_at"] > 0
def test_create_ssh_key_duplicate_name(self, client: TestClient):
"""Test creating a SSH key with duplicate name fails."""
# Create first key
client.post(
"/api/ssh-keys",
json={
"name": "duplicate-key",
"private_key": VALID_SSH_KEY
},
headers={"Authorization": "Bearer test-token"}
)
# Try to create duplicate
response = client.post(
"/api/ssh-keys",
json={
"name": "duplicate-key",
"private_key": VALID_SSH_KEY
},
headers={"Authorization": "Bearer test-token"}
)
assert response.status_code == 400
data = response.json()
assert "already exists" in data["detail"]
def test_create_ssh_key_invalid_key_format(self, client: TestClient):
"""Test creating a SSH key with invalid format fails."""
response = client.post(
"/api/ssh-keys",
json={
"name": "invalid-key",
"private_key": INVALID_SSH_KEY
},
headers={"Authorization": "Bearer test-token"}
)
assert response.status_code == 400
data = response.json()
assert "Invalid SSH private key format" in data["detail"]
def test_create_ssh_key_empty_name(self, client: TestClient):
"""Test creating a SSH key with empty name fails."""
response = client.post(
"/api/ssh-keys",
json={
"name": "",
"private_key": VALID_SSH_KEY
},
headers={"Authorization": "Bearer test-token"}
)
assert response.status_code == 422 # Validation error
def test_create_ssh_key_no_auth(self, client: TestClient):
"""Test creating a SSH key without authentication fails."""
response = client.post(
"/api/ssh-keys",
json={
"name": "test-key",
"private_key": VALID_SSH_KEY
}
)
assert response.status_code == 401
def test_create_ssh_key_invalid_token(self, client: TestClient):
"""Test creating a SSH key with invalid token fails."""
response = client.post(
"/api/ssh-keys",
json={
"name": "test-key",
"private_key": VALID_SSH_KEY
},
headers={"Authorization": "Bearer invalid-token"}
)
assert response.status_code == 401
class TestListSshKeys:
"""Tests for GET /api/ssh-keys endpoint."""
def test_list_ssh_keys_empty(self, client: TestClient):
"""Test listing SSH keys when none exist."""
response = client.get(
"/api/ssh-keys",
headers={"Authorization": "Bearer test-token"}
)
assert response.status_code == 200
data = response.json()
assert data["code"] == 0
assert data["data"] == []
assert "0 SSH key" in data["message"]
def test_list_ssh_keys_with_items(self, client: TestClient):
"""Test listing SSH keys when some exist."""
# Create some keys
client.post(
"/api/ssh-keys",
json={"name": "key-1", "private_key": VALID_SSH_KEY},
headers={"Authorization": "Bearer test-token"}
)
client.post(
"/api/ssh-keys",
json={"name": "key-2", "private_key": VALID_SSH_KEY},
headers={"Authorization": "Bearer test-token"}
)
response = client.get(
"/api/ssh-keys",
headers={"Authorization": "Bearer test-token"}
)
assert response.status_code == 200
data = response.json()
assert data["code"] == 0
assert len(data["data"]) == 2
assert data["data"][0]["name"] in ["key-1", "key-2"]
assert "private_key" not in data["data"][0] # Should not expose private key
def test_list_ssh_keys_no_auth(self, client: TestClient):
"""Test listing SSH keys without authentication fails."""
response = client.get("/api/ssh-keys")
assert response.status_code == 401
class TestGetSshKey:
"""Tests for GET /api/ssh-keys/{id} endpoint."""
def test_get_ssh_key_success(self, client: TestClient):
"""Test getting a specific SSH key successfully."""
# Create a key first
create_response = client.post(
"/api/ssh-keys",
json={"name": "get-test-key", "private_key": VALID_SSH_KEY},
headers={"Authorization": "Bearer test-token"}
)
key_id = create_response.json()["data"]["id"]
# Get the key
response = client.get(
f"/api/ssh-keys/{key_id}",
headers={"Authorization": "Bearer test-token"}
)
assert response.status_code == 200
data = response.json()
assert data["code"] == 0
assert data["data"]["id"] == key_id
assert data["data"]["name"] == "get-test-key"
assert "private_key" not in data["data"]
def test_get_ssh_key_not_found(self, client: TestClient):
"""Test getting a non-existent SSH key fails."""
response = client.get(
"/api/ssh-keys/99999",
headers={"Authorization": "Bearer test-token"}
)
assert response.status_code == 404
assert "not found" in response.json()["detail"]
def test_get_ssh_key_no_auth(self, client: TestClient):
"""Test getting an SSH key without authentication fails."""
response = client.get("/api/ssh-keys/1")
assert response.status_code == 401
class TestDeleteSshKey:
"""Tests for DELETE /api/ssh-keys/{id} endpoint."""
def test_delete_ssh_key_success(self, client: TestClient):
"""Test deleting an SSH key successfully."""
# Create a key first
create_response = client.post(
"/api/ssh-keys",
json={"name": "delete-test-key", "private_key": VALID_SSH_KEY},
headers={"Authorization": "Bearer test-token"}
)
key_id = create_response.json()["data"]["id"]
# Delete the key
response = client.delete(
f"/api/ssh-keys/{key_id}",
headers={"Authorization": "Bearer test-token"}
)
assert response.status_code == 200
data = response.json()
assert data["code"] == 0
assert data["message"] == "SSH key deleted successfully"
# Verify it's deleted
get_response = client.get(
f"/api/ssh-keys/{key_id}",
headers={"Authorization": "Bearer test-token"}
)
assert get_response.status_code == 404
def test_delete_ssh_key_not_found(self, client: TestClient):
"""Test deleting a non-existent SSH key fails."""
response = client.delete(
"/api/ssh-keys/99999",
headers={"Authorization": "Bearer test-token"}
)
assert response.status_code == 404
def test_delete_ssh_key_in_use(self, client: TestClient):
"""Test deleting an SSH key that is in use by a server fails."""
# Create an SSH key
ssh_response = client.post(
"/api/ssh-keys",
json={"name": "in-use-key", "private_key": VALID_SSH_KEY},
headers={"Authorization": "Bearer test-token"}
)
ssh_key_id = ssh_response.json()["data"]["id"]
# Create a server using this key
client.post(
"/api/servers",
json={
"name": "test-server",
"url": "https://gitea.example.com",
"api_token": "test-api-token",
"ssh_key_id": ssh_key_id
},
headers={"Authorization": "Bearer test-token"}
)
# Try to delete the SSH key
response = client.delete(
f"/api/ssh-keys/{ssh_key_id}",
headers={"Authorization": "Bearer test-token"}
)
assert response.status_code == 400
assert "in use" in response.json()["detail"].lower()
def test_delete_ssh_key_no_auth(self, client: TestClient):
"""Test deleting an SSH key without authentication fails."""
response = client.delete("/api/ssh-keys/1")
assert response.status_code == 401

View File

@@ -0,0 +1,151 @@
"""
Tests for Status API routes.
Tests the following endpoints:
- GET /api/status
- GET /api/status/health
"""
import pytest
from fastapi.testclient import TestClient
VALID_SSH_KEY = """-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACB5WfkA8wgP/KvdGFNC1ZCbBmjZnKpM/LOXRDJS7NfRAQAAAJC9AVH1vQFR
AAAAAtzc2gtZWQyNTUxOQAAACB5WfkA8wgP/KvdGFNC1ZCbBmjZnKpM/LOXRDJS7NfRAQA
AAECB5WfkA8wgP/KvdGFNC1ZCbBmjZnKpM/LOXRDJS7NfRAQAAAHHZpXRvaXRvQVJNVjJH
AAAAFGZpbGVzeXN0ZW0uY2ZnAAAAAQAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAA/////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAEAAAAEWNvbW1lbnQ6IHRlc3Qga2V5AQIDBAUHBg=="""
class TestGetStatus:
"""Tests for GET /api/status endpoint."""
def test_get_status_unauthenticated(self, client: TestClient):
"""Test getting status without authentication."""
response = client.get("/api/status")
assert response.status_code == 200
data = response.json()
assert data["code"] == 0
assert data["message"] == "System status retrieved successfully"
assert data["data"]["status"] == "healthy"
assert data["data"]["version"] == "1.0.0"
assert data["data"]["authenticated"] is False
assert "database" in data["data"]
assert data["data"]["database"]["status"] == "connected"
# Storage paths should not be included for unauthenticated
assert "storage" not in data["data"]
def test_get_status_authenticated(self, client: TestClient):
"""Test getting status with authentication."""
response = client.get(
"/api/status",
headers={"Authorization": "Bearer test-token"}
)
assert response.status_code == 200
data = response.json()
assert data["code"] == 0
assert data["data"]["authenticated"] is True
assert "storage" in data["data"]
assert "data_dir" in data["data"]["storage"]
assert "repos_dir" in data["data"]["storage"]
assert "ssh_keys_dir" in data["data"]["storage"]
assert "db_path" in data["data"]["storage"]
def test_get_status_with_data(self, client: TestClient):
"""Test getting status when data exists."""
# Create an SSH key
client.post(
"/api/ssh-keys",
json={"name": "test-key", "private_key": VALID_SSH_KEY},
headers={"Authorization": "Bearer test-token"}
)
# Get status
response = client.get(
"/api/status",
headers={"Authorization": "Bearer test-token"}
)
assert response.status_code == 200
data = response.json()
assert data["data"]["database"]["ssh_keys_count"] == 1
assert data["data"]["database"]["servers_count"] == 0
assert data["data"]["database"]["repos_count"] == 0
def test_get_status_database_counts(self, client: TestClient):
"""Test database counts in status are accurate."""
# Create multiple items
client.post(
"/api/ssh-keys",
json={"name": "key-1", "private_key": VALID_SSH_KEY},
headers={"Authorization": "Bearer test-token"}
)
client.post(
"/api/ssh-keys",
json={"name": "key-2", "private_key": VALID_SSH_KEY},
headers={"Authorization": "Bearer test-token"}
)
# Create a server
ssh_response = client.post(
"/api/ssh-keys",
json={"name": "server-key", "private_key": VALID_SSH_KEY},
headers={"Authorization": "Bearer test-token"}
)
ssh_key_id = ssh_response.json()["data"]["id"]
client.post(
"/api/servers",
json={
"name": "test-server",
"url": "https://gitea.example.com",
"api_token": "test-token",
"ssh_key_id": ssh_key_id
},
headers={"Authorization": "Bearer test-token"}
)
# Get status
response = client.get(
"/api/status",
headers={"Authorization": "Bearer test-token"}
)
assert response.status_code == 200
data = response.json()
assert data["data"]["database"]["ssh_keys_count"] == 3
assert data["data"]["database"]["servers_count"] == 1
class TestHealthCheck:
"""Tests for GET /api/status/health endpoint."""
def test_health_check(self, client: TestClient):
"""Test the health check endpoint."""
response = client.get("/api/status/health")
assert response.status_code == 200
data = response.json()
assert data["code"] == 0
assert data["data"]["status"] == "ok"
assert data["message"] == "Service is healthy"
def test_health_check_no_auth_required(self, client: TestClient):
"""Test health check works without authentication."""
response = client.get("/api/status/health")
assert response.status_code == 200
def test_health_check_with_auth(self, client: TestClient):
"""Test health check works with authentication."""
response = client.get(
"/api/status/health",
headers={"Authorization": "Bearer test-token"}
)
assert response.status_code == 200