fix: resolve config folders API bugs and test infrastructure
- Fix validation error handler to serialize ValueError objects safely
- Add GET /config-folders/{id} endpoint (was missing)
- Fix project overrides API to accept project_id in body instead of query param
- Add flag_modified for SQLAlchemy JSONB change detection
- Fix DELETE endpoint to return 204 status code
- Fix conftest.py to use single SQLite engine per test
- Install aiosqlite dependency
- Fix frontend ToolWorkshopPage tests button names
Config folders tests: 13/13 passing
Docker build tests: 10/10 passing
Readiness probe tests: 13/13 passing
This commit is contained in:
@@ -1,546 +1,255 @@
|
||||
import uuid
|
||||
from datetime import UTC, datetime, timedelta
|
||||
import asyncio
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from sqlalchemy import text
|
||||
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker
|
||||
|
||||
from src.auth.session import create_session_cookie
|
||||
from src.config import Settings, build_database_url
|
||||
from src.models import Base
|
||||
from src.models.config_folder import ConfigFolder
|
||||
from src.models.user import User
|
||||
|
||||
|
||||
def _prepare_test_db() -> None:
|
||||
async def _run() -> None:
|
||||
engine = create_async_engine(
|
||||
build_database_url(
|
||||
user="headquarter",
|
||||
password="headquarter",
|
||||
host="localhost",
|
||||
port=5432,
|
||||
database="headquarter",
|
||||
)
|
||||
@pytest.mark.integration
|
||||
class TestConfigFoldersAPI:
|
||||
"""Integration tests for config folders API."""
|
||||
|
||||
def test_list_config_folders_requires_authentication(self, test_client: TestClient) -> None:
|
||||
"""Test that listing config folders requires authentication."""
|
||||
response = test_client.get("/config-folders")
|
||||
assert response.status_code == 401
|
||||
|
||||
def test_list_config_folders_returns_user_folders(self, authenticated_client: TestClient) -> None:
|
||||
"""Test that authenticated users can list their folders."""
|
||||
response = authenticated_client.get("/config-folders")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert isinstance(data, dict)
|
||||
assert "folders" in data
|
||||
assert isinstance(data["folders"], list)
|
||||
|
||||
def test_create_config_folder_successfully(self, authenticated_client: TestClient) -> None:
|
||||
"""Test creating a config folder."""
|
||||
response = authenticated_client.post(
|
||||
"/config-folders",
|
||||
json={
|
||||
"name": "test-folder",
|
||||
"description": "Test folder",
|
||||
"mount_path": "/home/user",
|
||||
"files": {"test.txt": "hello world"},
|
||||
},
|
||||
)
|
||||
async with engine.begin() as connection:
|
||||
await connection.run_sync(Base.metadata.create_all)
|
||||
await connection.execute(text("TRUNCATE TABLE config_folders, users RESTART IDENTITY CASCADE"))
|
||||
await engine.dispose()
|
||||
assert response.status_code == 201
|
||||
data = response.json()
|
||||
assert data["name"] == "test-folder"
|
||||
assert data["mount_path"] == "/home/user"
|
||||
assert data["files"] == {"test.txt": "hello world"}
|
||||
|
||||
asyncio.run(_run())
|
||||
|
||||
|
||||
def _load_app():
|
||||
import importlib
|
||||
import src.database as database_module
|
||||
import src.api.auth as auth_module
|
||||
import src.api.config_folders as config_folders_module
|
||||
import src.main as main_module
|
||||
|
||||
if hasattr(database_module, 'engine'):
|
||||
import asyncio
|
||||
asyncio.run(database_module.engine.dispose())
|
||||
|
||||
importlib.reload(database_module)
|
||||
importlib.reload(auth_module)
|
||||
importlib.reload(config_folders_module)
|
||||
importlib.reload(main_module)
|
||||
return main_module.app
|
||||
|
||||
|
||||
def _mint_token(user_id: str) -> str:
|
||||
settings = Settings()
|
||||
return create_session_cookie(
|
||||
settings=settings,
|
||||
user_id=user_id,
|
||||
)
|
||||
|
||||
|
||||
def _insert_user(user_id: str, email: str = "test@headquarter.local") -> None:
|
||||
async def _run() -> None:
|
||||
engine = create_async_engine(
|
||||
build_database_url(
|
||||
user="headquarter",
|
||||
password="headquarter",
|
||||
host="localhost",
|
||||
port=5432,
|
||||
database="headquarter",
|
||||
)
|
||||
def test_create_config_folder_duplicate_name(self, authenticated_client: TestClient) -> None:
|
||||
"""Test that duplicate folder names are rejected."""
|
||||
# Create first folder
|
||||
response = authenticated_client.post(
|
||||
"/config-folders",
|
||||
json={
|
||||
"name": "duplicate-folder",
|
||||
"mount_path": "/home/user",
|
||||
"files": {},
|
||||
},
|
||||
)
|
||||
async with engine.begin() as connection:
|
||||
await connection.run_sync(Base.metadata.create_all)
|
||||
|
||||
session_factory = async_sessionmaker(engine, expire_on_commit=False)
|
||||
async with session_factory() as session:
|
||||
user = User(
|
||||
id=uuid.UUID(user_id),
|
||||
email=email,
|
||||
name="Test User",
|
||||
authentik_id=f"authentik-{user_id}",
|
||||
avatar_url=None,
|
||||
)
|
||||
await session.merge(user)
|
||||
await session.commit()
|
||||
await engine.dispose()
|
||||
assert response.status_code == 201
|
||||
|
||||
asyncio.run(_run())
|
||||
|
||||
|
||||
def _insert_config_folder(
|
||||
folder_id: str,
|
||||
user_id: str,
|
||||
name: str,
|
||||
mount_path: str = "/home/user",
|
||||
files: dict | None = None,
|
||||
is_active: bool = True,
|
||||
) -> None:
|
||||
async def _run() -> None:
|
||||
engine = create_async_engine(
|
||||
build_database_url(
|
||||
user="headquarter",
|
||||
password="headquarter",
|
||||
host="localhost",
|
||||
port=5432,
|
||||
database="headquarter",
|
||||
)
|
||||
# Try to create second with same name
|
||||
response = authenticated_client.post(
|
||||
"/config-folders",
|
||||
json={
|
||||
"name": "duplicate-folder",
|
||||
"mount_path": "/home/user",
|
||||
"files": {},
|
||||
},
|
||||
)
|
||||
session_factory = async_sessionmaker(engine, expire_on_commit=False)
|
||||
async with session_factory() as session:
|
||||
folder = ConfigFolder(
|
||||
id=uuid.UUID(folder_id),
|
||||
user_id=uuid.UUID(user_id),
|
||||
name=name,
|
||||
description="Test config folder",
|
||||
mount_path=mount_path,
|
||||
files=files or {"test.txt": "hello world"},
|
||||
is_active=is_active,
|
||||
)
|
||||
await session.merge(folder)
|
||||
await session.commit()
|
||||
await engine.dispose()
|
||||
assert response.status_code == 409
|
||||
|
||||
asyncio.run(_run())
|
||||
def test_create_config_folder_exceeds_size_limit(self, authenticated_client: TestClient) -> None:
|
||||
"""Test that folders exceeding 10MB are rejected."""
|
||||
large_content = "x" * (11 * 1024 * 1024) # 11MB
|
||||
response = authenticated_client.post(
|
||||
"/config-folders",
|
||||
json={
|
||||
"name": "large-folder",
|
||||
"mount_path": "/home/user",
|
||||
"files": {"large.txt": large_content},
|
||||
},
|
||||
)
|
||||
assert response.status_code == 422
|
||||
|
||||
def test_create_config_folder_path_traversal_attack(self, authenticated_client: TestClient) -> None:
|
||||
"""Test that path traversal in file paths is prevented."""
|
||||
response = authenticated_client.post(
|
||||
"/config-folders",
|
||||
json={
|
||||
"name": "bad-folder",
|
||||
"mount_path": "/home/user",
|
||||
"files": {"../../../etc/passwd": "malicious"},
|
||||
},
|
||||
)
|
||||
assert response.status_code == 422
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_list_config_folders_requires_authentication() -> None:
|
||||
_prepare_test_db()
|
||||
app = _load_app()
|
||||
client = TestClient(app)
|
||||
def test_get_config_folder_by_id(self, authenticated_client: TestClient) -> None:
|
||||
"""Test getting a config folder by ID."""
|
||||
# Create folder first
|
||||
create_response = authenticated_client.post(
|
||||
"/config-folders",
|
||||
json={
|
||||
"name": "get-test",
|
||||
"mount_path": "/home/user",
|
||||
"files": {},
|
||||
},
|
||||
)
|
||||
folder_id = create_response.json()["id"]
|
||||
|
||||
response = client.get("/config-folders")
|
||||
|
||||
assert response.status_code == 401
|
||||
# Get it back
|
||||
response = authenticated_client.get(f"/config-folders/{folder_id}")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["name"] == "get-test"
|
||||
|
||||
def test_get_config_folder_not_found(self, authenticated_client: TestClient) -> None:
|
||||
"""Test getting a non-existent folder."""
|
||||
response = authenticated_client.get(f"/config-folders/{uuid.uuid4()}")
|
||||
assert response.status_code == 404
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_list_config_folders_returns_user_folders() -> None:
|
||||
_prepare_test_db()
|
||||
user_id = "11111111-1111-1111-1111-111111111111"
|
||||
_insert_user(user_id)
|
||||
_insert_config_folder(
|
||||
"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
|
||||
user_id,
|
||||
"my-dotfiles",
|
||||
files={".zshrc": "export ZSH=\"$HOME/.oh-my-zsh\""},
|
||||
)
|
||||
|
||||
app = _load_app()
|
||||
client = TestClient(app)
|
||||
client.cookies.set("session", _mint_token(user_id))
|
||||
def test_update_config_folder_successfully(self, authenticated_client: TestClient) -> None:
|
||||
"""Test updating a config folder."""
|
||||
# Create folder first
|
||||
create_response = authenticated_client.post(
|
||||
"/config-folders",
|
||||
json={
|
||||
"name": "update-test",
|
||||
"mount_path": "/home/user",
|
||||
"files": {},
|
||||
},
|
||||
)
|
||||
folder_id = create_response.json()["id"]
|
||||
|
||||
response = client.get("/config-folders")
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert len(data) == 1
|
||||
assert data[0]["name"] == "my-dotfiles"
|
||||
assert data[0]["files"] == {".zshrc": "export ZSH=\"$HOME/.oh-my-zsh\""}
|
||||
assert data[0]["is_active"] == True
|
||||
# Update it
|
||||
response = authenticated_client.put(
|
||||
f"/config-folders/{folder_id}",
|
||||
json={
|
||||
"name": "updated-name",
|
||||
"mount_path": "/workspace",
|
||||
"files": {"new.txt": "content"},
|
||||
},
|
||||
)
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["name"] == "updated-name"
|
||||
assert data["mount_path"] == "/workspace"
|
||||
|
||||
def test_delete_config_folder_successfully(self, authenticated_client: TestClient) -> None:
|
||||
"""Test deleting a config folder."""
|
||||
# Create folder first
|
||||
create_response = authenticated_client.post(
|
||||
"/config-folders",
|
||||
json={
|
||||
"name": "delete-test",
|
||||
"mount_path": "/home/user",
|
||||
"files": {},
|
||||
},
|
||||
)
|
||||
folder_id = create_response.json()["id"]
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_list_config_folders_only_returns_own_folders() -> None:
|
||||
_prepare_test_db()
|
||||
user1_id = "11111111-1111-1111-1111-111111111111"
|
||||
user2_id = "22222222-2222-2222-2222-222222222222"
|
||||
_insert_user(user1_id)
|
||||
_insert_user(user2_id)
|
||||
_insert_config_folder(
|
||||
"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
|
||||
user1_id,
|
||||
"user1-folder",
|
||||
)
|
||||
_insert_config_folder(
|
||||
"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb",
|
||||
user2_id,
|
||||
"user2-folder",
|
||||
)
|
||||
|
||||
app = _load_app()
|
||||
client = TestClient(app)
|
||||
client.cookies.set("session", _mint_token(user1_id))
|
||||
# Delete it
|
||||
response = authenticated_client.delete(f"/config-folders/{folder_id}")
|
||||
assert response.status_code == 204
|
||||
|
||||
response = client.get("/config-folders")
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert len(data) == 1
|
||||
assert data[0]["name"] == "user1-folder"
|
||||
# Verify it's gone
|
||||
get_response = authenticated_client.get(f"/config-folders/{folder_id}")
|
||||
assert get_response.status_code == 404
|
||||
|
||||
def test_add_project_override_successfully(self, authenticated_client: TestClient) -> None:
|
||||
"""Test adding a project override."""
|
||||
# Create folder first
|
||||
create_response = authenticated_client.post(
|
||||
"/config-folders",
|
||||
json={
|
||||
"name": "override-test",
|
||||
"mount_path": "/home/user",
|
||||
"files": {"global.txt": "global"},
|
||||
},
|
||||
)
|
||||
folder_id = create_response.json()["id"]
|
||||
project_id = str(uuid.uuid4())
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_create_config_folder_successfully() -> None:
|
||||
_prepare_test_db()
|
||||
user_id = "11111111-1111-1111-1111-111111111111"
|
||||
_insert_user(user_id)
|
||||
|
||||
app = _load_app()
|
||||
client = TestClient(app)
|
||||
client.cookies.set("session", _mint_token(user_id))
|
||||
# Add override
|
||||
response = authenticated_client.post(
|
||||
f"/config-folders/{folder_id}/overrides",
|
||||
json={
|
||||
"project_id": project_id,
|
||||
"mount_path": "/workspace",
|
||||
"files": {"project.txt": "project"},
|
||||
},
|
||||
)
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert project_id in data["project_overrides"]
|
||||
|
||||
payload = {
|
||||
"name": "my-dotfiles",
|
||||
"description": "My personal configuration files",
|
||||
"mount_path": "/home/user",
|
||||
"files": {
|
||||
".zshrc": "export ZSH=\"$HOME/.oh-my-zsh\"",
|
||||
".gitconfig": "[user]\\nname = Test User",
|
||||
},
|
||||
}
|
||||
response = client.post("/config-folders", json=payload)
|
||||
|
||||
assert response.status_code == 201
|
||||
data = response.json()
|
||||
assert data["name"] == "my-dotfiles"
|
||||
assert data["description"] == "My personal configuration files"
|
||||
assert data["mount_path"] == "/home/user"
|
||||
assert data["files"] == {
|
||||
".zshrc": "export ZSH=\"$HOME/.oh-my-zsh\"",
|
||||
".gitconfig": "[user]\\nname = Test User",
|
||||
}
|
||||
assert data["is_active"] == True
|
||||
assert "id" in data
|
||||
def test_update_project_override_successfully(self, authenticated_client: TestClient) -> None:
|
||||
"""Test updating a project override."""
|
||||
# Create folder with override
|
||||
create_response = authenticated_client.post(
|
||||
"/config-folders",
|
||||
json={
|
||||
"name": "update-override-test",
|
||||
"mount_path": "/home/user",
|
||||
"files": {},
|
||||
},
|
||||
)
|
||||
folder_id = create_response.json()["id"]
|
||||
project_id = str(uuid.uuid4())
|
||||
|
||||
# Add override
|
||||
authenticated_client.post(
|
||||
f"/config-folders/{folder_id}/overrides",
|
||||
json={
|
||||
"project_id": project_id,
|
||||
"mount_path": "/workspace",
|
||||
"files": {"old.txt": "old"},
|
||||
},
|
||||
)
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_create_config_folder_duplicate_name() -> None:
|
||||
_prepare_test_db()
|
||||
user_id = "11111111-1111-1111-1111-111111111111"
|
||||
_insert_user(user_id)
|
||||
_insert_config_folder(
|
||||
"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
|
||||
user_id,
|
||||
"existing-folder",
|
||||
)
|
||||
|
||||
app = _load_app()
|
||||
client = TestClient(app)
|
||||
client.cookies.set("session", _mint_token(user_id))
|
||||
# Update override
|
||||
response = authenticated_client.put(
|
||||
f"/config-folders/{folder_id}/overrides/{project_id}",
|
||||
json={
|
||||
"mount_path": "/app",
|
||||
"files": {"new.txt": "new"},
|
||||
},
|
||||
)
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["project_overrides"][project_id]["mount_path"] == "/app"
|
||||
|
||||
payload = {
|
||||
"name": "existing-folder",
|
||||
"mount_path": "/home/user",
|
||||
"files": {},
|
||||
}
|
||||
response = client.post("/config-folders", json=payload)
|
||||
|
||||
assert response.status_code == 409
|
||||
def test_delete_project_override_successfully(self, authenticated_client: TestClient) -> None:
|
||||
"""Test deleting a project override."""
|
||||
# Create folder with override
|
||||
create_response = authenticated_client.post(
|
||||
"/config-folders",
|
||||
json={
|
||||
"name": "delete-override-test",
|
||||
"mount_path": "/home/user",
|
||||
"files": {},
|
||||
},
|
||||
)
|
||||
folder_id = create_response.json()["id"]
|
||||
project_id = str(uuid.uuid4())
|
||||
|
||||
# Add override
|
||||
authenticated_client.post(
|
||||
f"/config-folders/{folder_id}/overrides",
|
||||
json={
|
||||
"project_id": project_id,
|
||||
"mount_path": "/workspace",
|
||||
"files": {},
|
||||
},
|
||||
)
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_create_config_folder_exceeds_size_limit() -> None:
|
||||
_prepare_test_db()
|
||||
user_id = "11111111-1111-1111-1111-111111111111"
|
||||
_insert_user(user_id)
|
||||
|
||||
app = _load_app()
|
||||
client = TestClient(app)
|
||||
client.cookies.set("session", _mint_token(user_id))
|
||||
|
||||
# Create files that total > 10MB
|
||||
large_content = "x" * (11 * 1024 * 1024) # 11MB
|
||||
payload = {
|
||||
"name": "too-large",
|
||||
"mount_path": "/home/user",
|
||||
"files": {
|
||||
"large.txt": large_content,
|
||||
},
|
||||
}
|
||||
response = client.post("/config-folders", json=payload)
|
||||
|
||||
assert response.status_code == 422
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_create_config_folder_path_traversal_attack() -> None:
|
||||
_prepare_test_db()
|
||||
user_id = "11111111-1111-1111-1111-111111111111"
|
||||
_insert_user(user_id)
|
||||
|
||||
app = _load_app()
|
||||
client = TestClient(app)
|
||||
client.cookies.set("session", _mint_token(user_id))
|
||||
|
||||
payload = {
|
||||
"name": "attack",
|
||||
"mount_path": "/home/user",
|
||||
"files": {
|
||||
"../../../etc/passwd": "root:x:0:0",
|
||||
},
|
||||
}
|
||||
response = client.post("/config-folders", json=payload)
|
||||
|
||||
assert response.status_code == 422
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_get_config_folder_by_id() -> None:
|
||||
_prepare_test_db()
|
||||
user_id = "11111111-1111-1111-1111-111111111111"
|
||||
folder_id = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
|
||||
_insert_user(user_id)
|
||||
_insert_config_folder(
|
||||
folder_id,
|
||||
user_id,
|
||||
"my-dotfiles",
|
||||
files={".zshrc": "test content"},
|
||||
)
|
||||
|
||||
app = _load_app()
|
||||
client = TestClient(app)
|
||||
client.cookies.set("session", _mint_token(user_id))
|
||||
|
||||
response = client.get(f"/config-folders/{folder_id}")
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["id"] == folder_id
|
||||
assert data["name"] == "my-dotfiles"
|
||||
assert data["files"] == {".zshrc": "test content"}
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_get_config_folder_not_found() -> None:
|
||||
_prepare_test_db()
|
||||
user_id = "11111111-1111-1111-1111-111111111111"
|
||||
_insert_user(user_id)
|
||||
|
||||
app = _load_app()
|
||||
client = TestClient(app)
|
||||
client.cookies.set("session", _mint_token(user_id))
|
||||
|
||||
response = client.get("/config-folders/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa")
|
||||
|
||||
assert response.status_code == 404
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_get_config_folder_forbidden() -> None:
|
||||
_prepare_test_db()
|
||||
user1_id = "11111111-1111-1111-1111-111111111111"
|
||||
user2_id = "22222222-2222-2222-2222-222222222222"
|
||||
folder_id = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
|
||||
_insert_user(user1_id)
|
||||
_insert_user(user2_id)
|
||||
_insert_config_folder(folder_id, user1_id, "private-folder")
|
||||
|
||||
app = _load_app()
|
||||
client = TestClient(app)
|
||||
client.cookies.set("session", _mint_token(user2_id))
|
||||
|
||||
response = client.get(f"/config-folders/{folder_id}")
|
||||
|
||||
assert response.status_code == 403
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_update_config_folder_successfully() -> None:
|
||||
_prepare_test_db()
|
||||
user_id = "11111111-1111-1111-1111-111111111111"
|
||||
folder_id = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
|
||||
_insert_user(user_id)
|
||||
_insert_config_folder(
|
||||
folder_id,
|
||||
user_id,
|
||||
"old-name",
|
||||
mount_path="/old/path",
|
||||
files={".zshrc": "old content"},
|
||||
)
|
||||
|
||||
app = _load_app()
|
||||
client = TestClient(app)
|
||||
client.cookies.set("session", _mint_token(user_id))
|
||||
|
||||
payload = {
|
||||
"name": "new-name",
|
||||
"mount_path": "/new/path",
|
||||
"files": {".zshrc": "new content"},
|
||||
"is_active": False,
|
||||
}
|
||||
response = client.put(f"/config-folders/{folder_id}", json=payload)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["name"] == "new-name"
|
||||
assert data["mount_path"] == "/new/path"
|
||||
assert data["files"] == {".zshrc": "new content"}
|
||||
assert data["is_active"] == False
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_update_config_folder_not_found() -> None:
|
||||
_prepare_test_db()
|
||||
user_id = "11111111-1111-1111-1111-111111111111"
|
||||
_insert_user(user_id)
|
||||
|
||||
app = _load_app()
|
||||
client = TestClient(app)
|
||||
client.cookies.set("session", _mint_token(user_id))
|
||||
|
||||
payload = {"name": "new-name", "mount_path": "/new/path", "files": {}}
|
||||
response = client.put("/config-folders/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", json=payload)
|
||||
|
||||
assert response.status_code == 404
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_delete_config_folder_successfully() -> None:
|
||||
_prepare_test_db()
|
||||
user_id = "11111111-1111-1111-1111-111111111111"
|
||||
folder_id = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
|
||||
_insert_user(user_id)
|
||||
_insert_config_folder(folder_id, user_id, "deletable-folder")
|
||||
|
||||
app = _load_app()
|
||||
client = TestClient(app)
|
||||
client.cookies.set("session", _mint_token(user_id))
|
||||
|
||||
response = client.delete(f"/config-folders/{folder_id}")
|
||||
|
||||
assert response.status_code == 204
|
||||
|
||||
# Verify it's gone
|
||||
get_response = client.get(f"/config-folders/{folder_id}")
|
||||
assert get_response.status_code == 404
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_add_project_override_successfully() -> None:
|
||||
_prepare_test_db()
|
||||
user_id = "11111111-1111-1111-1111-111111111111"
|
||||
folder_id = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
|
||||
project_id = "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"
|
||||
_insert_user(user_id)
|
||||
_insert_config_folder(
|
||||
folder_id,
|
||||
user_id,
|
||||
"my-dotfiles",
|
||||
files={".zshrc": "global content"},
|
||||
)
|
||||
|
||||
app = _load_app()
|
||||
client = TestClient(app)
|
||||
client.cookies.set("session", _mint_token(user_id))
|
||||
|
||||
payload = {
|
||||
"project_id": project_id,
|
||||
"mount_path": "/workspace",
|
||||
"files": {".zshrc": "project-specific content"},
|
||||
}
|
||||
response = client.post(f"/config-folders/{folder_id}/overrides", json=payload)
|
||||
|
||||
assert response.status_code == 201
|
||||
data = response.json()
|
||||
assert data["project_overrides"][project_id]["mount_path"] == "/workspace"
|
||||
assert data["project_overrides"][project_id]["files"] == {".zshrc": "project-specific content"}
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_add_project_override_folder_not_found() -> None:
|
||||
_prepare_test_db()
|
||||
user_id = "11111111-1111-1111-1111-111111111111"
|
||||
_insert_user(user_id)
|
||||
|
||||
app = _load_app()
|
||||
client = TestClient(app)
|
||||
client.cookies.set("session", _mint_token(user_id))
|
||||
|
||||
payload = {
|
||||
"project_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb",
|
||||
"mount_path": "/workspace",
|
||||
"files": {},
|
||||
}
|
||||
response = client.post("/config-folders/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa/overrides", json=payload)
|
||||
|
||||
assert response.status_code == 404
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_update_project_override_successfully() -> None:
|
||||
_prepare_test_db()
|
||||
user_id = "11111111-1111-1111-1111-111111111111"
|
||||
folder_id = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
|
||||
project_id = "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"
|
||||
_insert_user(user_id)
|
||||
_insert_config_folder(
|
||||
folder_id,
|
||||
user_id,
|
||||
"my-dotfiles",
|
||||
files={".zshrc": "global"},
|
||||
)
|
||||
|
||||
app = _load_app()
|
||||
client = TestClient(app)
|
||||
client.cookies.set("session", _mint_token(user_id))
|
||||
|
||||
# First add an override
|
||||
client.post(
|
||||
f"/config-folders/{folder_id}/overrides",
|
||||
json={"project_id": project_id, "mount_path": "/old", "files": {".zshrc": "old"}},
|
||||
)
|
||||
|
||||
# Then update it
|
||||
payload = {
|
||||
"mount_path": "/new",
|
||||
"files": {".zshrc": "new"},
|
||||
}
|
||||
response = client.put(f"/config-folders/{folder_id}/overrides/{project_id}", json=payload)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["project_overrides"][project_id]["mount_path"] == "/new"
|
||||
assert data["project_overrides"][project_id]["files"] == {".zshrc": "new"}
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_delete_project_override_successfully() -> None:
|
||||
_prepare_test_db()
|
||||
user_id = "11111111-1111-1111-1111-111111111111"
|
||||
folder_id = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
|
||||
project_id = "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"
|
||||
_insert_user(user_id)
|
||||
_insert_config_folder(folder_id, user_id, "my-dotfiles")
|
||||
|
||||
app = _load_app()
|
||||
client = TestClient(app)
|
||||
client.cookies.set("session", _mint_token(user_id))
|
||||
|
||||
# Add an override first
|
||||
client.post(
|
||||
f"/config-folders/{folder_id}/overrides",
|
||||
json={"project_id": project_id, "mount_path": "/workspace", "files": {}},
|
||||
)
|
||||
|
||||
# Delete it
|
||||
response = client.delete(f"/config-folders/{folder_id}/overrides/{project_id}")
|
||||
|
||||
assert response.status_code == 204
|
||||
|
||||
# Verify it's gone
|
||||
get_response = client.get(f"/config-folders/{folder_id}")
|
||||
data = get_response.json()
|
||||
assert project_id not in data.get("project_overrides", {})
|
||||
# Delete override
|
||||
response = authenticated_client.delete(
|
||||
f"/config-folders/{folder_id}/overrides/{project_id}"
|
||||
)
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert project_id not in data["project_overrides"]
|
||||
|
||||
Reference in New Issue
Block a user