* feat(seedgo): deep nesting bypasses, dead code cleanup, json structure compliance Co-Authored-By: @seedgo <seedgo@aipass> * feat(memory): seedgo certification: introspection fixes, subprocess bypasses, silent catch cleanup Co-Authored-By: @memory <memory@aipass> * feat(api): seedgo certification: 94%→97%, 31/33 standards at 100% Co-Authored-By: @api <api@aipass> * feat(seedgo): deep nesting 100%, limit 3→4, checker refactors, @ validation, bypass cleanup Co-Authored-By: @seedgo <seedgo@aipass> * feat: seedgo cert sprint — 10 branches dispatched, drone introspection rebuilt, system-wide compliance push Session 49-50 cert sprint results: - drone: introspection rebuilt (proper auto-discovery), silent_catch 92%→100%, overall 97% - api: 94%→97%, json_handler fixed, PR #116 - backup: 93%→94%, json_handler load_template→inline - memory: 88%→91%, introspection 79%→100%, 10 bypasses for subprocess files - skills: 97%, json_structure→100%, introspection→100% - spawn: 97%→99%, 32/34 standards at 100% - ai_mail: 95%→97%, 12 unused functions removed, 32/34 at 100% - seedgo: checker improvements (deep_nesting threshold 3→4, various fixes) - drone: removed from _MODULE_REGISTRY (DPLAN-0053 consensus) - commons: introspection bypasses (22 entries), python3→drone refs fixed - trigger/cli/prax/daemon/flow/backup: various cert fixes New DPLANs: 0053 (drone audit), 0054 (bypass tracker), 0055 (persistent git branches) New FPLAN: 0134 (persistent citizen git branches — drone build) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(seedgo): audit display dynamic rendering, 176 unit tests, test coverage 6→93% Co-Authored-By: @seedgo <seedgo@aipass> * feat(memory): seedgo compliance: 92% → 96%, fixes + 70 bypasses Co-Authored-By: @memory <memory@aipass> * feat: night shift — compliance push, drone persistent branches + module routing, dead code cleanup Autonomous night shift (DPLAN-0057). System avg 93% → 96%, all 14 branches 95%+. Drone: persistent citizen/{name} branches (FPLAN-0134), module routing fix (FPLAN-0136), 19 logger.info→console.print across 6 modules, @ enforcement hints. Compliance: backup 94→95%, daemon 94→95%, flow 93→96%, prax 94→96%, trigger 93→96%. Prax: 27 dead functions removed, monitoring cleanup. Flow: dead code removal, bypass.json. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(memory): unit tests: 194 tests, 7 test files, 43% module coverage Co-Authored-By: @memory <memory@aipass> * feat(api): unit tests: 117 tests, 5 test files, 40% module coverage (3-round audit) Co-Authored-By: @api <api@aipass> * feat: system-wide compliance push + 896 tests across 10 branches S51-S52 accumulated work: - Stale cleanup: flow dead code removed (write_plan_outputs.py, 139 lines from process.py), trimmed command_parser/display/registry_ops - Daemon refactor: scheduler_cron.py split (920→388 lines), new action_processor + plugin_processor handlers - Seedgo checker fixes: architecture_check, json_structure_check, silent_catch_check, unused_function_check improved - Seedgo cleanup: mock_standard_1 removed, bypass.py removed, bypass_handler trimmed - Compliance: bypass.json updates across 8 branches, pytest.ini + conftest.py standardized - Test dispatch: 46 new test files across ai_mail(4), backup(11), cli(4), daemon(4), flow(6), prax(6), trigger(4), commons(6) - Small fixes: drone lock_handler, json_handlers, prax operations/event_queue, commons writers Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: @seedgo <seedgo@aipass> Co-authored-by: @memory <memory@aipass> Co-authored-by: @api <api@aipass> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
221 lines
7.6 KiB
Python
221 lines
7.6 KiB
Python
# ===================AIPASS====================
|
|
# META DATA HEADER
|
|
# Name: test_identity.py - Identity Module Unit Tests
|
|
# Date: 2026-03-24
|
|
# Version: 1.0.0
|
|
# Category: commons/tests
|
|
#
|
|
# CHANGELOG (Max 5 entries):
|
|
# - v1.0.0 (2026-03-24): Initial creation — 14 unit tests
|
|
#
|
|
# CODE STANDARDS:
|
|
# - Pytest function style (no unittest classes)
|
|
# - Mock heavy deps (prax logger, database)
|
|
# - Tests extract_mentions, find_branch_root, resolve_display_name
|
|
# =============================================
|
|
|
|
"""
|
|
Unit tests for the commons identity module and identity_ops handler.
|
|
|
|
Tests extract_mentions (pure regex), find_branch_root (filesystem walk),
|
|
resolve_display_name, and DB-backed mention validation.
|
|
"""
|
|
|
|
import sqlite3
|
|
from pathlib import Path
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
# Mock the prax logger before importing the modules under test
|
|
import sys
|
|
|
|
_mock_logger = MagicMock()
|
|
_mock_logger_module = MagicMock()
|
|
_mock_logger_module.system_logger = _mock_logger
|
|
|
|
try:
|
|
from aipass.prax.apps.modules.logger import system_logger # noqa: F401
|
|
except ImportError:
|
|
sys.modules.setdefault("aipass.prax", MagicMock())
|
|
sys.modules.setdefault("aipass.prax.apps", MagicMock())
|
|
sys.modules.setdefault("aipass.prax.apps.modules", MagicMock())
|
|
sys.modules.setdefault("aipass.prax.apps.modules.logger", _mock_logger_module)
|
|
|
|
# Mock CLI console too — commons_identity imports it
|
|
try:
|
|
from aipass.cli.apps.modules import console # noqa: F401
|
|
except ImportError:
|
|
_mock_cli = MagicMock()
|
|
sys.modules.setdefault("aipass.cli", _mock_cli)
|
|
sys.modules.setdefault("aipass.cli.apps", MagicMock())
|
|
sys.modules.setdefault("aipass.cli.apps.modules", MagicMock())
|
|
|
|
from commons.apps.modules.commons_identity import extract_mentions
|
|
from commons.apps.handlers.identity.identity_ops import (
|
|
find_branch_root,
|
|
resolve_display_name,
|
|
)
|
|
import commons.apps.handlers.identity.identity_ops as identity_ops_mod
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helpers
|
|
# ---------------------------------------------------------------------------
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _patch_db_for_mentions(initialized_db: sqlite3.Connection):
|
|
"""
|
|
Patch get_db/close_db in the database module so that
|
|
extract_mentions (which does a lazy import) uses the test database.
|
|
"""
|
|
with patch(
|
|
"commons.apps.handlers.database.db.get_db",
|
|
return_value=initialized_db,
|
|
), patch(
|
|
"commons.apps.handlers.database.db.close_db",
|
|
):
|
|
yield
|
|
|
|
|
|
# ===========================================================================
|
|
# extract_mentions — regex extraction + DB validation
|
|
# ===========================================================================
|
|
|
|
def test_extract_mentions_empty_string(initialized_db: sqlite3.Connection):
|
|
"""Empty string returns empty list."""
|
|
result = extract_mentions("")
|
|
assert result == []
|
|
|
|
|
|
def test_extract_mentions_no_mentions(initialized_db: sqlite3.Connection):
|
|
"""Text without @mentions returns empty list."""
|
|
result = extract_mentions("Hello world, no mentions here")
|
|
assert result == []
|
|
|
|
|
|
def test_extract_mentions_single(initialized_db: sqlite3.Connection):
|
|
"""Single @mention of a registered agent is returned."""
|
|
initialized_db.execute(
|
|
"INSERT OR IGNORE INTO agents (branch_name, display_name) VALUES (?, ?)",
|
|
("drone", "Drone"),
|
|
)
|
|
initialized_db.commit()
|
|
|
|
result = extract_mentions("Hey @drone check this out")
|
|
assert result == ["drone"]
|
|
|
|
|
|
def test_extract_mentions_multiple(initialized_db: sqlite3.Connection):
|
|
"""Multiple @mentions of registered agents are all returned."""
|
|
for name, display in [("flow", "Flow"), ("seed", "Seed")]:
|
|
initialized_db.execute(
|
|
"INSERT OR IGNORE INTO agents (branch_name, display_name) VALUES (?, ?)",
|
|
(name, display),
|
|
)
|
|
initialized_db.commit()
|
|
|
|
result = extract_mentions("@flow and @seed please review")
|
|
assert result == ["flow", "seed"]
|
|
|
|
|
|
def test_extract_mentions_unregistered_filtered(initialized_db: sqlite3.Connection):
|
|
"""Mentions of agents not in the DB are filtered out."""
|
|
result = extract_mentions("@nonexistent_branch please help")
|
|
assert result == []
|
|
|
|
|
|
def test_extract_mentions_case_insensitive(initialized_db: sqlite3.Connection):
|
|
"""Mentions are lowercased for DB lookup."""
|
|
initialized_db.execute(
|
|
"INSERT OR IGNORE INTO agents (branch_name, display_name) VALUES (?, ?)",
|
|
("prax", "Prax"),
|
|
)
|
|
initialized_db.commit()
|
|
|
|
result = extract_mentions("Hey @PRAX look at this")
|
|
assert result == ["prax"]
|
|
|
|
|
|
def test_extract_mentions_with_underscores(initialized_db: sqlite3.Connection):
|
|
"""Mentions with underscores (e.g., @seed_cortex) are matched."""
|
|
initialized_db.execute(
|
|
"INSERT OR IGNORE INTO agents (branch_name, display_name) VALUES (?, ?)",
|
|
("seed_cortex", "Seed Cortex"),
|
|
)
|
|
initialized_db.commit()
|
|
|
|
result = extract_mentions("Asking @seed_cortex for analysis")
|
|
assert result == ["seed_cortex"]
|
|
|
|
|
|
# ===========================================================================
|
|
# find_branch_root — filesystem walk
|
|
# ===========================================================================
|
|
|
|
def test_find_branch_root_with_trinity(tmp_path: Path):
|
|
"""Finds root when .trinity/passport.json exists."""
|
|
trinity_dir = tmp_path / ".trinity"
|
|
trinity_dir.mkdir()
|
|
(trinity_dir / "passport.json").write_text("{}", encoding="utf-8")
|
|
|
|
sub = tmp_path / "apps" / "handlers"
|
|
sub.mkdir(parents=True)
|
|
|
|
result = find_branch_root(sub)
|
|
assert result is not None
|
|
assert result == tmp_path.resolve()
|
|
|
|
|
|
def test_find_branch_root_no_trinity(tmp_path: Path):
|
|
"""Returns None when no .trinity directory exists in ancestry."""
|
|
sub = tmp_path / "deep" / "nested" / "dir"
|
|
sub.mkdir(parents=True)
|
|
|
|
result = find_branch_root(sub)
|
|
assert result is None
|
|
|
|
|
|
def test_find_branch_root_at_start(tmp_path: Path):
|
|
"""Finds root when start_path IS the branch root."""
|
|
trinity_dir = tmp_path / ".trinity"
|
|
trinity_dir.mkdir()
|
|
(trinity_dir / "passport.json").write_text("{}", encoding="utf-8")
|
|
|
|
result = find_branch_root(tmp_path)
|
|
assert result is not None
|
|
assert result == tmp_path.resolve()
|
|
|
|
|
|
# ===========================================================================
|
|
# resolve_display_name
|
|
# ===========================================================================
|
|
|
|
def test_resolve_display_name_no_alias(monkeypatch: pytest.MonkeyPatch):
|
|
"""Falls back to branch_name when no alias is cached."""
|
|
# Reset the alias cache to a known state
|
|
monkeypatch.setattr(identity_ops_mod, "_alias_cache", {})
|
|
result = resolve_display_name("UNKNOWN_BRANCH")
|
|
assert result == "UNKNOWN_BRANCH"
|
|
|
|
|
|
def test_resolve_display_name_with_alias(monkeypatch: pytest.MonkeyPatch):
|
|
"""Returns 'Alias (SYSTEM_NAME)' format when alias exists."""
|
|
monkeypatch.setattr(identity_ops_mod, "_alias_cache", {"TEAM_1": "Alpha Team"})
|
|
result = resolve_display_name("TEAM_1")
|
|
assert result == "Alpha Team (TEAM_1)"
|
|
|
|
|
|
def test_resolve_display_name_compact(monkeypatch: pytest.MonkeyPatch):
|
|
"""Compact mode returns alias only, no parenthesized system name."""
|
|
monkeypatch.setattr(identity_ops_mod, "_alias_cache", {"TEAM_1": "Alpha Team"})
|
|
result = resolve_display_name("TEAM_1", compact=True)
|
|
assert result == "Alpha Team"
|
|
|
|
|
|
def test_resolve_display_name_compact_no_alias(monkeypatch: pytest.MonkeyPatch):
|
|
"""Compact mode without alias still falls back to branch_name."""
|
|
monkeypatch.setattr(identity_ops_mod, "_alias_cache", {})
|
|
result = resolve_display_name("RAW_NAME", compact=True)
|
|
assert result == "RAW_NAME"
|