kin/tests/test_kin_fix_006_regression.py

157 lines
5.5 KiB
Python
Raw Permalink Normal View History

"""Regression tests for KIN-FIX-006: 'ssh_key' must be a valid auth_type.
Root cause: VALID_AUTH_TYPES did not include 'ssh_key', causing 422 on POST credentials.
Fix: VALID_AUTH_TYPES = {"password", "key", "ssh_key"} (web/api.py line 1028).
Acceptance criteria:
1. POST /projects/{id}/environments with auth_type='ssh_key' returns 201 (not 422)
2. auth_type='key' still returns 201
3. auth_type='password' still returns 201
4. auth_type='ftp' (invalid) returns 422
"""
import pytest
from unittest.mock import patch, MagicMock
# ---------------------------------------------------------------------------
# Fixture
# ---------------------------------------------------------------------------
@pytest.fixture
def client(tmp_path):
import web.api as api_module
api_module.DB_PATH = tmp_path / "test_fix006.db"
# Re-import app after setting DB_PATH so init_db uses the new path
from importlib import reload
import web.api
reload(web.api)
api_module.DB_PATH = tmp_path / "test_fix006.db"
from web.api import app
from fastapi.testclient import TestClient
c = TestClient(app)
c.post("/api/projects", json={"id": "testproj", "name": "Test Project", "path": "/testproj"})
return c
# ---------------------------------------------------------------------------
# Tests: VALID_AUTH_TYPES validation
# ---------------------------------------------------------------------------
def test_create_environment_ssh_key_auth_type_returns_201(client):
"""Regression KIN-FIX-006: auth_type='ssh_key' must return 201, not 422."""
r = client.post("/api/projects/testproj/environments", json={
"name": "prod-ssh",
"host": "10.0.0.1",
"username": "deploy",
"auth_type": "ssh_key",
"auth_value": "-----BEGIN RSA PRIVATE KEY-----",
})
assert r.status_code == 201, (
f"auth_type='ssh_key' must be accepted (201), got {r.status_code}: {r.text}"
)
def test_create_environment_key_auth_type_still_valid(client):
"""auth_type='key' must still return 201 after the fix."""
r = client.post("/api/projects/testproj/environments", json={
"name": "prod-key",
"host": "10.0.0.2",
"username": "deploy",
"auth_type": "key",
"auth_value": "keydata",
})
assert r.status_code == 201, (
f"auth_type='key' must still be valid (201), got {r.status_code}: {r.text}"
)
def test_create_environment_password_auth_type_still_valid(client):
"""auth_type='password' must still return 201 after the fix."""
r = client.post("/api/projects/testproj/environments", json={
"name": "prod-pass",
"host": "10.0.0.3",
"username": "root",
"auth_type": "password",
"auth_value": "s3cr3t",
})
assert r.status_code == 201, (
f"auth_type='password' must still be valid (201), got {r.status_code}: {r.text}"
)
def test_create_environment_invalid_auth_type_returns_422(client):
"""Invalid auth_type (e.g. 'ftp') must return 422 Unprocessable Entity."""
r = client.post("/api/projects/testproj/environments", json={
"name": "prod-ftp",
"host": "10.0.0.4",
"username": "ftpuser",
"auth_type": "ftp",
"auth_value": "password123",
})
assert r.status_code == 422, (
f"auth_type='ftp' must be rejected (422), got {r.status_code}: {r.text}"
)
def test_create_environment_empty_auth_type_returns_422(client):
"""Empty string auth_type must return 422."""
r = client.post("/api/projects/testproj/environments", json={
"name": "prod-empty",
"host": "10.0.0.5",
"username": "root",
"auth_type": "",
})
assert r.status_code == 422, (
f"auth_type='' must be rejected (422), got {r.status_code}: {r.text}"
)
def test_create_environment_default_auth_type_is_password(client):
"""Default auth_type (omitted) must be 'password' and return 201."""
r = client.post("/api/projects/testproj/environments", json={
"name": "prod-default",
"host": "10.0.0.6",
"username": "root",
"auth_value": "pass",
# auth_type intentionally omitted — defaults to 'password'
})
assert r.status_code == 201, (
f"Default auth_type must be accepted (201), got {r.status_code}: {r.text}"
)
# ---------------------------------------------------------------------------
# Test: VALID_AUTH_TYPES content (unit-level)
# ---------------------------------------------------------------------------
def test_valid_auth_types_contains_ssh_key():
"""Unit: VALID_AUTH_TYPES set must include 'ssh_key'."""
from web.api import VALID_AUTH_TYPES
assert "ssh_key" in VALID_AUTH_TYPES, (
f"VALID_AUTH_TYPES must contain 'ssh_key', got: {VALID_AUTH_TYPES}"
)
def test_valid_auth_types_contains_key():
"""Unit: VALID_AUTH_TYPES set must include 'key'."""
from web.api import VALID_AUTH_TYPES
assert "key" in VALID_AUTH_TYPES, (
f"VALID_AUTH_TYPES must contain 'key', got: {VALID_AUTH_TYPES}"
)
def test_valid_auth_types_contains_password():
"""Unit: VALID_AUTH_TYPES set must include 'password'."""
from web.api import VALID_AUTH_TYPES
assert "password" in VALID_AUTH_TYPES, (
f"VALID_AUTH_TYPES must contain 'password', got: {VALID_AUTH_TYPES}"
)
def test_valid_auth_types_excludes_ftp():
"""Unit: VALID_AUTH_TYPES must NOT include 'ftp'."""
from web.api import VALID_AUTH_TYPES
assert "ftp" not in VALID_AUTH_TYPES, (
f"VALID_AUTH_TYPES must not contain 'ftp', got: {VALID_AUTH_TYPES}"
)