From bbe975ec54002426347e6e757c99181cee68437e Mon Sep 17 00:00:00 2001 From: AlexanderWhitestone <8633216+AlexanderWhitestone@users.noreply.github.com> Date: Thu, 5 Mar 2026 21:54:24 -0500 Subject: [PATCH] feat: add TDD setup script and functional tests for Sovereign Agent Stack --- tests/functional/test_setup_prod.py | 123 ++++++++++++++++++++++++++++ tests/test_setup_script.py | 82 +++++++++++++++++++ 2 files changed, 205 insertions(+) create mode 100644 tests/functional/test_setup_prod.py create mode 100644 tests/test_setup_script.py diff --git a/tests/functional/test_setup_prod.py b/tests/functional/test_setup_prod.py new file mode 100644 index 0000000..adddd5e --- /dev/null +++ b/tests/functional/test_setup_prod.py @@ -0,0 +1,123 @@ +import os +import subprocess +import shutil +import pytest +from pathlib import Path +import time + +# Production-like paths for functional testing +PROD_PROJECT_DIR = Path("/home/ubuntu/prod-sovereign-stack") +PROD_VAULT_DIR = PROD_PROJECT_DIR / "TimmyVault" +SETUP_SCRIPT_PATH = Path("/home/ubuntu/setup_timmy.sh") + +@pytest.fixture(scope="module", autouse=True) +def setup_prod_env(): + """Ensure a clean environment and run the full installation.""" + if PROD_PROJECT_DIR.exists(): + shutil.rmtree(PROD_PROJECT_DIR) + + # Run the actual install command + env = os.environ.copy() + env["PROJECT_DIR"] = str(PROD_PROJECT_DIR) + env["VAULT_DIR"] = str(PROD_VAULT_DIR) + + result = subprocess.run( + [str(SETUP_SCRIPT_PATH), "install"], + capture_output=True, + text=True, + env=env + ) + + assert result.returncode == 0, f"Install failed: {result.stderr}" + yield + # Cleanup after all tests in module + # shutil.rmtree(PROD_PROJECT_DIR) + +def test_prod_directory_structure(): + """Verify the directory structure matches production expectations.""" + assert PROD_PROJECT_DIR.exists() + assert (PROD_PROJECT_DIR / "paperclip").exists() + assert (PROD_PROJECT_DIR / "agents/hello-timmy").exists() + assert PROD_VAULT_DIR.exists() + assert (PROD_PROJECT_DIR / "logs").exists() + assert (PROD_PROJECT_DIR / "pids").exists() + +def test_prod_paperclip_dependencies(): + """Verify that Paperclip dependencies were actually installed (node_modules exists).""" + node_modules = PROD_PROJECT_DIR / "paperclip/node_modules" + assert node_modules.exists(), "Paperclip node_modules should exist after installation" + # Check for a common package to ensure it's not just an empty dir + assert (node_modules / "typescript").exists() or (node_modules / "vite").exists() or (node_modules / "next").exists() or any(node_modules.iterdir()) + +def test_prod_openfang_config(): + """Verify OpenFang agent configuration.""" + agent_toml = PROD_PROJECT_DIR / "agents/hello-timmy/agent.toml" + assert agent_toml.exists() + with open(agent_toml, "r") as f: + content = f.read() + assert 'name = "hello-timmy"' in content + assert 'model = "default"' in content + +def test_prod_obsidian_vault_content(): + """Verify the initial content of the Obsidian vault.""" + hello_note = PROD_VAULT_DIR / "Hello World.md" + soul_note = PROD_VAULT_DIR / "SOUL.md" + + assert hello_note.exists() + assert soul_note.exists() + + with open(hello_note, "r") as f: + content = f.read() + assert "# Hello World" in content + assert "Paperclip" in content + assert "OpenFang" in content + + with open(soul_note, "r") as f: + content = f.read() + assert "I am Timmy" in content + assert "sovereign AI agent" in content + +def test_prod_service_lifecycle(): + """Verify that services can be started, checked, and stopped.""" + env = os.environ.copy() + env["PROJECT_DIR"] = str(PROD_PROJECT_DIR) + env["VAULT_DIR"] = str(PROD_VAULT_DIR) + + # Start services + start_result = subprocess.run( + [str(SETUP_SCRIPT_PATH), "start"], + capture_output=True, + text=True, + env=env + ) + assert start_result.returncode == 0 + + # Wait a moment for processes to initialize + time.sleep(2) + + # Check status + status_result = subprocess.run( + [str(SETUP_SCRIPT_PATH), "status"], + capture_output=True, + text=True, + env=env + ) + assert "running" in status_result.stdout + + # Stop services + stop_result = subprocess.run( + [str(SETUP_SCRIPT_PATH), "stop"], + capture_output=True, + text=True, + env=env + ) + assert stop_result.returncode == 0 + + # Final status check + final_status = subprocess.run( + [str(SETUP_SCRIPT_PATH), "status"], + capture_output=True, + text=True, + env=env + ) + assert "stopped" in final_status.stdout diff --git a/tests/test_setup_script.py b/tests/test_setup_script.py new file mode 100644 index 0000000..187f12d --- /dev/null +++ b/tests/test_setup_script.py @@ -0,0 +1,82 @@ +import os +import subprocess +import shutil +import pytest +from pathlib import Path + +# Constants for testing +TEST_PROJECT_DIR = Path("/home/ubuntu/test-sovereign-stack") +TEST_VAULT_DIR = TEST_PROJECT_DIR / "TimmyVault" +SETUP_SCRIPT_PATH = Path("/home/ubuntu/setup_timmy.sh") + +@pytest.fixture(scope="module", autouse=True) +def cleanup_test_env(): + """Ensure a clean environment before and after tests.""" + if TEST_PROJECT_DIR.exists(): + shutil.rmtree(TEST_PROJECT_DIR) + yield + # We keep the test env for manual inspection if needed, or cleanup + # shutil.rmtree(TEST_PROJECT_DIR) + +def run_setup_command(args): + """Helper to run the setup script with arguments.""" + result = subprocess.run( + [str(SETUP_SCRIPT_PATH)] + args, + capture_output=True, + text=True, + cwd="/home/ubuntu" + ) + return result + +def test_setup_install_creates_directories(): + """Test that './setup_timmy.sh install' creates the expected directory structure.""" + # Note: We expect the script to be present at SETUP_SCRIPT_PATH + assert SETUP_SCRIPT_PATH.exists(), "Setup script must exist before testing" + + result = run_setup_command(["install"]) + + # Check if command succeeded + assert result.returncode == 0, f"Setup install failed: {result.stderr}" + + # Check directory structure + assert TEST_PROJECT_DIR.exists() + assert (TEST_PROJECT_DIR / "paperclip").exists() + assert (TEST_PROJECT_DIR / "agents/hello-timmy").exists() + assert TEST_VAULT_DIR.exists() + assert (TEST_PROJECT_DIR / "logs").exists() + +def test_setup_install_creates_files(): + """Test that './setup_timmy.sh install' creates the expected configuration and notes.""" + # Check Agent config + agent_toml = TEST_PROJECT_DIR / "agents/hello-timmy/agent.toml" + assert agent_toml.exists() + with open(agent_toml, "r") as f: + content = f.read() + assert 'name = "hello-timmy"' in content + + # Check Obsidian notes + hello_note = TEST_VAULT_DIR / "Hello World.md" + soul_note = TEST_VAULT_DIR / "SOUL.md" + assert hello_note.exists() + assert soul_note.exists() + + with open(soul_note, "r") as f: + content = f.read() + assert "I am Timmy" in content + +def test_setup_install_dependencies(): + """Test that dependencies are correctly handled (OpenFang, Paperclip deps).""" + # Check if Paperclip node_modules exists (implies pnpm install ran) + # Note: In a real TDD we might mock pnpm, but here we want to verify the actual setup + node_modules = TEST_PROJECT_DIR / "paperclip/node_modules" + assert node_modules.exists() + +def test_setup_start_stop_logic(): + """Test the start/stop command logic (simulated).""" + # This is harder to test fully without actually running the services, + # but we can check if the script handles the commands without crashing. + + # Mocking start (it might fail if ports are taken, so we check return code) + # For the sake of this test, we just check if the script recognizes the command + result = run_setup_command(["status"]) + assert "Status" in result.stdout or result.returncode == 0