Files
agentic-govbot/tests/test_basic.py
Nathan Schneider fbc37ecb8f Initial commit: Platform-agnostic governance bot
Govbot is an AI-powered governance bot that interprets natural language
constitutions and facilitates collective decision-making across social
platforms.

Core features:
- Agentic architecture with constitutional reasoning (RAG)
- Platform-agnostic design (Mastodon, Discord, Telegram, etc.)
- Action primitives for flexible governance processes
- Temporal awareness for multi-day proposals and voting
- Audit trail with constitutional citations
- Reversible actions with supermajority veto
- Works with local (Ollama) and cloud AI models

Platform support:
- Mastodon: Full implementation with streaming, moderation, and admin skills
- Discord/Telegram: Platform abstraction ready for implementation

Documentation:
- README.md: Architecture and overview
- QUICKSTART.md: Getting started guide
- PLATFORMS.md: Platform implementation guide for developers
- MASTODON_SETUP.md: Complete Mastodon deployment guide
- constitution.md: Example governance constitution

Technical stack:
- Python 3.11+
- SQLAlchemy for state management
- llm CLI for model abstraction
- Mastodon.py for Mastodon integration
- Pydantic for configuration validation

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-06 17:09:26 -07:00

173 lines
4.6 KiB
Python

"""
Basic tests for Govbot functionality.
These tests demonstrate the core features without requiring Mastodon.
"""
import pytest
from pathlib import Path
import sys
import tempfile
import shutil
# Add src to path
sys.path.insert(0, str(Path(__file__).parent.parent))
from src.govbot.db.models import init_db, get_session
from src.govbot.agent import GovernanceAgent
from src.govbot.governance.constitution import ConstitutionalReasoner
from src.govbot.governance.primitives import GovernancePrimitives
@pytest.fixture
def test_db():
"""Create a temporary test database"""
temp_dir = tempfile.mkdtemp()
db_path = Path(temp_dir) / "test.db"
engine = init_db(str(db_path))
session = get_session(engine)
yield session
session.close()
shutil.rmtree(temp_dir)
@pytest.fixture
def constitution_path():
"""Get path to test constitution"""
return str(Path(__file__).parent.parent / "constitution.md")
def test_constitution_loading(constitution_path):
"""Test that constitution loads and parses correctly"""
reasoner = ConstitutionalReasoner(constitution_path)
assert reasoner.constitution_text
assert len(reasoner.sections) > 0
# Test section listing
sections = reasoner.list_sections()
assert any("Proposal" in s for s in sections)
def test_constitutional_query(constitution_path):
"""Test querying the constitution"""
reasoner = ConstitutionalReasoner(constitution_path)
result = reasoner.query("What are the rules for standard proposals?")
assert "answer" in result
assert "citations" in result
assert len(result["citations"]) > 0
def test_primitives_store_and_query(test_db):
"""Test basic primitive operations"""
primitives = GovernancePrimitives(test_db)
# Store a record
record_id = primitives.store_record(
record_type="test_record",
data={"foo": "bar", "count": 42},
actor="test_user",
reasoning="Testing storage",
)
assert record_id > 0
# Query it back
records = primitives.query_records(record_type="store_test_record")
assert len(records) > 0
def test_create_process(test_db):
"""Test creating a governance process"""
primitives = GovernancePrimitives(test_db)
process_id = primitives.create_process(
process_type="test_proposal",
creator="test_user",
deadline_days=6,
constitutional_basis="Article 3, Section 1",
initial_state={"proposal_text": "Test proposal"},
)
assert process_id > 0
# Verify it was created
from src.govbot.db import queries
process = queries.get_process(test_db, process_id)
assert process is not None
assert process.process_type == "test_proposal"
assert process.creator == "test_user"
def test_vote_counting(test_db):
"""Test vote counting and threshold checking"""
primitives = GovernancePrimitives(test_db)
# Create a process
process_id = primitives.create_process(
process_type="test_proposal",
creator="alice",
deadline_days=6,
constitutional_basis="Article 3",
initial_state={"votes": {}},
)
# Cast some votes
primitives.update_process_state(
process_id,
{
"votes.alice": {"vote": "agree"},
"votes.bob": {"vote": "agree"},
"votes.charlie": {"vote": "disagree"},
},
)
# Count votes
counts = primitives.count_votes(process_id)
assert counts["agree"] == 2
assert counts["disagree"] == 1
# Check threshold
passed = primitives.check_threshold(counts, "simple_majority")
assert passed is True
def test_agent_proposal_creation(test_db, constitution_path):
"""Test agent creating a proposal from natural language"""
agent = GovernanceAgent(test_db, constitution_path, model=None)
result = agent.process_request(
request="I propose we have weekly meetings",
actor="test_user",
)
assert result["success"] is True
assert result["process_id"] is not None
assert "proposal" in result["response"].lower()
def test_proposal_interpretation(constitution_path):
"""Test interpreting different proposal types"""
reasoner = ConstitutionalReasoner(constitution_path)
# Standard proposal
info = reasoner.interpret_proposal("We should update the guidelines")
assert "proposal_type" in info
assert info["discussion_period_days"] >= 2
# Urgent proposal
info = reasoner.interpret_proposal("URGENT: We need to address this spam attack")
# Should recognize urgency (though AI interpretation may vary)
assert "proposal_type" in info
if __name__ == "__main__":
# Run tests
pytest.main([__file__, "-v"])