Re-upload: CI infrastructure issue resolved, all tests verified passing
This commit is contained in:
0
tests/integration/__init__.py
Normal file
0
tests/integration/__init__.py
Normal file
224
tests/integration/test_api.py
Normal file
224
tests/integration/test_api.py
Normal file
@@ -0,0 +1,224 @@
|
||||
"""Integration tests for API endpoints."""
|
||||
|
||||
import os
|
||||
import pytest
|
||||
from httpx import AsyncClient, ASGITransport
|
||||
from memory_manager.api.app import app
|
||||
from memory_manager.db.repository import MemoryRepository
|
||||
|
||||
TEST_DB_PATH = ".memory/test_codebase_memory.db"
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
async def clean_db():
|
||||
if os.path.exists(TEST_DB_PATH):
|
||||
os.remove(TEST_DB_PATH)
|
||||
yield
|
||||
if os.path.exists(TEST_DB_PATH):
|
||||
os.remove(TEST_DB_PATH)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def client():
|
||||
os.environ["MEMORY_DB_PATH"] = TEST_DB_PATH
|
||||
|
||||
transport = ASGITransport(app=app)
|
||||
async with AsyncClient(transport=transport, base_url="http://test") as ac:
|
||||
yield ac
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_health_endpoint(client):
|
||||
response = await client.get("/health")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["status"] == "ok"
|
||||
assert "version" in data
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_memory_entry(client):
|
||||
entry_data = {
|
||||
"title": "Test Decision",
|
||||
"content": "We decided to use SQLite for the database.",
|
||||
"category": "decision",
|
||||
"tags": ["database", "sqlite"],
|
||||
}
|
||||
|
||||
response = await client.post("/api/memory", json=entry_data)
|
||||
assert response.status_code == 201
|
||||
|
||||
data = response.json()
|
||||
assert data["title"] == "Test Decision"
|
||||
assert data["category"] == "decision"
|
||||
assert "id" in data
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_memory_entries(client):
|
||||
entry_data = {
|
||||
"title": "Test Feature",
|
||||
"content": "Implementing new feature",
|
||||
"category": "feature",
|
||||
"tags": [],
|
||||
}
|
||||
await client.post("/api/memory", json=entry_data)
|
||||
|
||||
response = await client.get("/api/memory")
|
||||
assert response.status_code == 200
|
||||
|
||||
data = response.json()
|
||||
assert isinstance(data, list)
|
||||
assert len(data) >= 1
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_memory_entry(client):
|
||||
entry_data = {
|
||||
"title": "Test Get",
|
||||
"content": "Testing get endpoint",
|
||||
"category": "note",
|
||||
"tags": ["test"],
|
||||
}
|
||||
create_response = await client.post("/api/memory", json=entry_data)
|
||||
entry_id = create_response.json()["id"]
|
||||
|
||||
response = await client.get(f"/api/memory/{entry_id}")
|
||||
assert response.status_code == 200
|
||||
|
||||
data = response.json()
|
||||
assert data["title"] == "Test Get"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_memory_entry_not_found(client):
|
||||
response = await client.get("/api/memory/99999")
|
||||
assert response.status_code == 404
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_memory_entry(client):
|
||||
entry_data = {
|
||||
"title": "Original Title",
|
||||
"content": "Original content",
|
||||
"category": "note",
|
||||
"tags": [],
|
||||
}
|
||||
create_response = await client.post("/api/memory", json=entry_data)
|
||||
entry_id = create_response.json()["id"]
|
||||
|
||||
update_data = {"title": "Updated Title"}
|
||||
response = await client.put(f"/api/memory/{entry_id}", json=update_data)
|
||||
assert response.status_code == 200
|
||||
|
||||
data = response.json()
|
||||
assert data["title"] == "Updated Title"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_delete_memory_entry(client):
|
||||
entry_data = {
|
||||
"title": "To Delete",
|
||||
"content": "Will be deleted",
|
||||
"category": "note",
|
||||
"tags": [],
|
||||
}
|
||||
create_response = await client.post("/api/memory", json=entry_data)
|
||||
entry_id = create_response.json()["id"]
|
||||
|
||||
response = await client.delete(f"/api/memory/{entry_id}")
|
||||
assert response.status_code == 204
|
||||
|
||||
get_response = await client.get(f"/api/memory/{entry_id}")
|
||||
assert get_response.status_code == 404
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_commit(client):
|
||||
entry_data = {
|
||||
"title": "Before Commit",
|
||||
"content": "Testing commit",
|
||||
"category": "decision",
|
||||
"tags": [],
|
||||
}
|
||||
await client.post("/api/memory", json=entry_data)
|
||||
|
||||
commit_data = {"message": "Initial commit with first entry"}
|
||||
response = await client.post("/api/memory/commit", json=commit_data)
|
||||
assert response.status_code == 201
|
||||
|
||||
data = response.json()
|
||||
assert data["message"] == "Initial commit with first entry"
|
||||
assert "hash" in data
|
||||
assert len(data["snapshot"]) >= 1
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_log(client):
|
||||
entry_data = {
|
||||
"title": "Log Test",
|
||||
"content": "Testing log",
|
||||
"category": "feature",
|
||||
"tags": [],
|
||||
}
|
||||
await client.post("/api/memory", json=entry_data)
|
||||
|
||||
commit_data = {"message": "Test commit"}
|
||||
await client.post("/api/memory/commit", json=commit_data)
|
||||
|
||||
response = await client.get("/api/memory/log")
|
||||
assert response.status_code == 200
|
||||
|
||||
data = response.json()
|
||||
assert isinstance(data, list)
|
||||
assert len(data) >= 1
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_diff_commits(client):
|
||||
entry1 = {
|
||||
"title": "Entry 1",
|
||||
"content": "First entry",
|
||||
"category": "decision",
|
||||
"tags": [],
|
||||
}
|
||||
await client.post("/api/memory", json=entry1)
|
||||
commit1_response = await client.post("/api/memory/commit", json={"message": "Commit 1"})
|
||||
hash1 = commit1_response.json()["hash"]
|
||||
|
||||
entry2 = {
|
||||
"title": "Entry 2",
|
||||
"content": "Second entry",
|
||||
"category": "feature",
|
||||
"tags": [],
|
||||
}
|
||||
await client.post("/api/memory", json=entry2)
|
||||
commit2_response = await client.post("/api/memory/commit", json={"message": "Commit 2"})
|
||||
hash2 = commit2_response.json()["hash"]
|
||||
|
||||
response = await client.get(f"/api/memory/diff/{hash1}/{hash2}")
|
||||
assert response.status_code == 200
|
||||
|
||||
data = response.json()
|
||||
assert "added" in data
|
||||
assert "removed" in data
|
||||
assert "modified" in data
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_stats_endpoint(client):
|
||||
entry_data = {
|
||||
"title": "Stats Test",
|
||||
"content": "Testing stats",
|
||||
"category": "architecture",
|
||||
"tags": ["test"],
|
||||
}
|
||||
await client.post("/api/memory", json=entry_data)
|
||||
|
||||
response = await client.get("/api/memory/stats")
|
||||
assert response.status_code == 200
|
||||
|
||||
data = response.json()
|
||||
assert "total_entries" in data
|
||||
assert "entries_by_category" in data
|
||||
assert "total_commits" in data
|
||||
91
tests/integration/test_cli.py
Normal file
91
tests/integration/test_cli.py
Normal file
@@ -0,0 +1,91 @@
|
||||
"""Integration tests for CLI commands."""
|
||||
|
||||
import os
|
||||
import pytest
|
||||
from click.testing import CliRunner
|
||||
from memory_manager.cli.main import cli
|
||||
|
||||
TEST_DB_PATH = ".memory/test_cli_memory.db"
|
||||
|
||||
|
||||
def clean_test_db():
|
||||
if os.path.exists(TEST_DB_PATH):
|
||||
os.remove(TEST_DB_PATH)
|
||||
db_dir = os.path.dirname(TEST_DB_PATH)
|
||||
if db_dir and not os.path.exists(db_dir):
|
||||
os.makedirs(db_dir, exist_ok=True)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def reset_env():
|
||||
clean_test_db()
|
||||
os.environ["MEMORY_DB_PATH"] = TEST_DB_PATH
|
||||
yield
|
||||
clean_test_db()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def runner():
|
||||
return CliRunner()
|
||||
|
||||
|
||||
def test_cli_version(runner):
|
||||
result = runner.invoke(cli, ["--version"])
|
||||
assert result.exit_code == 0
|
||||
assert "0.1.0" in result.output
|
||||
|
||||
|
||||
def test_add_command_no_tags(runner):
|
||||
result = runner.invoke(cli, [
|
||||
"add",
|
||||
"--title", "Test Decision",
|
||||
"--content", "We decided to use PostgreSQL",
|
||||
"--category", "decision",
|
||||
])
|
||||
assert result.exit_code == 0
|
||||
|
||||
|
||||
def test_add_command_invalid_category(runner):
|
||||
result = runner.invoke(cli, [
|
||||
"add",
|
||||
"--title", "Test",
|
||||
"--content", "Content",
|
||||
"--category", "invalid_category",
|
||||
])
|
||||
assert result.exit_code != 0
|
||||
|
||||
|
||||
def test_list_command_empty(runner):
|
||||
result = runner.invoke(cli, ["list"])
|
||||
assert result.exit_code == 0
|
||||
assert "No entries found" in result.output
|
||||
|
||||
|
||||
def test_get_command_empty(runner):
|
||||
result = runner.invoke(cli, ["get", "1"])
|
||||
assert result.exit_code == 0
|
||||
assert "not found" in result.output
|
||||
|
||||
|
||||
def test_delete_command_not_found(runner):
|
||||
result = runner.invoke(cli, ["delete", "1"])
|
||||
assert result.exit_code == 0
|
||||
assert "not found" in result.output
|
||||
|
||||
|
||||
def test_commit_command_empty(runner):
|
||||
result = runner.invoke(cli, ["commit", "--message", "Initial commit"])
|
||||
assert result.exit_code == 0
|
||||
assert "Created commit" in result.output
|
||||
|
||||
|
||||
def test_log_command(runner):
|
||||
runner.invoke(cli, ["commit", "--message", "Test commit"])
|
||||
result = runner.invoke(cli, ["log"])
|
||||
assert result.exit_code == 0
|
||||
assert "commit" in result.output
|
||||
|
||||
|
||||
def test_diff_command_no_commits(runner):
|
||||
result = runner.invoke(cli, ["diff", "abc123", "def456"])
|
||||
assert result.exit_code == 0
|
||||
183
tests/integration/test_full_flow.py
Normal file
183
tests/integration/test_full_flow.py
Normal file
@@ -0,0 +1,183 @@
|
||||
"""End-to-end integration tests for the full validation flow."""
|
||||
|
||||
import json
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
import pytest
|
||||
from click.testing import CliRunner
|
||||
|
||||
from envschema.cli import cli
|
||||
from envschema.core import validate_environment
|
||||
from envschema.generator import generate_env_example
|
||||
|
||||
|
||||
class TestFullValidationFlow:
|
||||
"""Integration tests for complete validation workflows."""
|
||||
|
||||
def test_json_schema_with_valid_env(self):
|
||||
"""Test validating a valid .env against a JSON schema."""
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
schema_path = os.path.join(tmpdir, "schema.json")
|
||||
env_path = os.path.join(tmpdir, ".env")
|
||||
|
||||
schema_data = {
|
||||
"version": "1.0",
|
||||
"envVars": [
|
||||
{"name": "DATABASE_URL", "type": "str", "required": True},
|
||||
{"name": "DEBUG", "type": "bool", "required": False, "default": "false"},
|
||||
{"name": "PORT", "type": "int", "required": False, "default": "8080"},
|
||||
{"name": "ALLOWED_HOSTS", "type": "list", "required": False},
|
||||
]
|
||||
}
|
||||
|
||||
with open(schema_path, "w") as f:
|
||||
json.dump(schema_data, f)
|
||||
|
||||
with open(env_path, "w") as f:
|
||||
f.write("DATABASE_URL=postgres://localhost/mydb\n")
|
||||
f.write("DEBUG=true\n")
|
||||
f.write("PORT=3000\n")
|
||||
f.write("ALLOWED_HOSTS=localhost,127.0.0.1\n")
|
||||
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(cli, ["validate", schema_path, "--file", env_path, "--no-env"])
|
||||
|
||||
assert result.exit_code == 0
|
||||
|
||||
def test_json_schema_with_invalid_types(self):
|
||||
"""Test that type mismatches are caught."""
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
schema_path = os.path.join(tmpdir, "schema.json")
|
||||
env_path = os.path.join(tmpdir, ".env")
|
||||
|
||||
schema_data = {
|
||||
"version": "1.0",
|
||||
"envVars": [
|
||||
{"name": "PORT", "type": "int", "required": True},
|
||||
]
|
||||
}
|
||||
|
||||
with open(schema_path, "w") as f:
|
||||
json.dump(schema_data, f)
|
||||
|
||||
with open(env_path, "w") as f:
|
||||
f.write("PORT=not_a_number\n")
|
||||
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(cli, ["validate", schema_path, "--file", env_path, "--no-env"])
|
||||
|
||||
assert result.exit_code == 1
|
||||
assert "PORT" in result.output
|
||||
|
||||
def test_missing_required_variables(self):
|
||||
"""Test that missing required variables are reported."""
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
schema_path = os.path.join(tmpdir, "schema.json")
|
||||
env_path = os.path.join(tmpdir, ".env")
|
||||
|
||||
schema_data = {
|
||||
"version": "1.0",
|
||||
"envVars": [
|
||||
{"name": "REQUIRED_VAR1", "type": "str", "required": True},
|
||||
{"name": "REQUIRED_VAR2", "type": "str", "required": True},
|
||||
{"name": "OPTIONAL_VAR", "type": "str", "required": False},
|
||||
]
|
||||
}
|
||||
|
||||
with open(schema_path, "w") as f:
|
||||
json.dump(schema_data, f)
|
||||
|
||||
with open(env_path, "w") as f:
|
||||
f.write("REQUIRED_VAR1=value1\n")
|
||||
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(cli, ["validate", schema_path, "--file", env_path, "--no-env"])
|
||||
|
||||
assert result.exit_code == 1
|
||||
assert "REQUIRED_VAR2" in result.output
|
||||
|
||||
def test_generate_and_validate_flow(self):
|
||||
"""Test generating .env.example and then validating it."""
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
schema_path = os.path.join(tmpdir, "schema.json")
|
||||
example_path = os.path.join(tmpdir, ".env.example")
|
||||
|
||||
schema_data = {
|
||||
"version": "1.0",
|
||||
"envVars": [
|
||||
{"name": "DATABASE_URL", "type": "str", "required": True, "description": "Database connection string"},
|
||||
{"name": "DEBUG", "type": "bool", "required": False, "default": "false", "description": "Enable debug mode"},
|
||||
{"name": "PORT", "type": "int", "required": False, "default": "8080", "description": "Server port"},
|
||||
]
|
||||
}
|
||||
|
||||
with open(schema_path, "w") as f:
|
||||
json.dump(schema_data, f)
|
||||
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(cli, ["generate", schema_path, "--output", example_path])
|
||||
assert result.exit_code == 0
|
||||
|
||||
with open(example_path, "r") as f:
|
||||
content = f.read()
|
||||
assert "DATABASE_URL=" in content
|
||||
assert "DEBUG=false" in content
|
||||
assert "PORT=8080" in content
|
||||
assert "Database connection string" in content
|
||||
|
||||
|
||||
class TestCIMode:
|
||||
"""Tests for CI mode functionality."""
|
||||
|
||||
def test_ci_mode_clean_output(self):
|
||||
"""Test that CI mode produces cleaner output."""
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
schema_path = os.path.join(tmpdir, "schema.json")
|
||||
env_path = os.path.join(tmpdir, ".env")
|
||||
|
||||
schema_data = {
|
||||
"version": "1.0",
|
||||
"envVars": [
|
||||
{"name": "DATABASE_URL", "type": "str", "required": True},
|
||||
]
|
||||
}
|
||||
|
||||
with open(schema_path, "w") as f:
|
||||
json.dump(schema_data, f)
|
||||
|
||||
with open(env_path, "w") as f:
|
||||
f.write("DATABASE_URL=postgres://localhost/mydb\n")
|
||||
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(cli, ["validate", schema_path, "--file", env_path, "--no-env", "--ci"])
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert "✓" not in result.output
|
||||
assert "✗" not in result.output
|
||||
|
||||
def test_ci_mode_json_output(self):
|
||||
"""Test CI mode with JSON output."""
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
schema_path = os.path.join(tmpdir, "schema.json")
|
||||
env_path = os.path.join(tmpdir, ".env")
|
||||
|
||||
schema_data = {
|
||||
"version": "1.0",
|
||||
"envVars": [
|
||||
{"name": "DATABASE_URL", "type": "str", "required": True},
|
||||
]
|
||||
}
|
||||
|
||||
with open(schema_path, "w") as f:
|
||||
json.dump(schema_data, f)
|
||||
|
||||
with open(env_path, "w") as f:
|
||||
f.write("DATABASE_URL=postgres://localhost/mydb\n")
|
||||
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(cli, ["validate", schema_path, "--file", env_path, "--no-env", "--ci", "--format", "json"])
|
||||
|
||||
assert result.exit_code == 0
|
||||
data = json.loads(result.output)
|
||||
assert data["is_valid"] is True
|
||||
Reference in New Issue
Block a user