Files
Orchestrator/shared/tools/monorepo-cli/tests/test_e2e_integration.py
T
2026-06-30 20:37:40 +02:00

835 lines
32 KiB
Python

"""End-to-End Integration-Tests für den gesamten Komponentenstack.
Testet die folgenden Workflows über mehrere Komponenten hinweg:
1. Projekt erstellen → Env verschlüsseln → Env laden → Secret-Zugriff prüfen
2. Artefakt erstellen → Indexieren → Suchen → Freigeben → Widerrufen
3. Repo einbinden → Sync → Read-Only-Schutz
4. Migration → Validierung → Rollback
5. Secret verschlüsseln → Maschinenkontext wechseln → Entschlüsselung fehlschlägt
6. Team_Repo vorbereiten → Isolation prüfen → Sync → Konflikt-Auflösung
7. Shared-Mirror in Team_Repo → Read-Only-Prüfung
Requirements: 1.1, 2.1, 3.2, 4.1, 5.2, 6.1, 9.1, 9.4, 10.3, 10.5, 10.12
"""
from __future__ import annotations
import subprocess
from pathlib import Path
from unittest.mock import MagicMock, patch
import pytest
import yaml
from monorepo.bridge import ContextBridge
from monorepo.encryption import DecryptionResult, EncryptionResult, SecretEncryptionManager
from monorepo.federation import FederationManager
from monorepo.knowledge.etl import ETLPipeline
from monorepo.knowledge.index import IndexEntry, YAMLIndex
from monorepo.knowledge.sources.markdown import MarkdownSource
from monorepo.knowledge.store import KnowledgeStore
from monorepo.migration import MigrationEngine
from monorepo.models import (
ConflictInfo,
MachineContext,
MigrationPlan,
RepoEntry,
ScopeConfig,
SyncResult,
)
from monorepo.repos import RepoManager
from monorepo.security import ContextGuard
from monorepo.structure import StructureManager
# ---------------------------------------------------------------------------
# Fixtures
# ---------------------------------------------------------------------------
@pytest.fixture()
def monorepo_root(tmp_path: Path) -> Path:
"""Creates a full monorepo structure for E2E integration tests."""
root = tmp_path / "monorepo"
root.mkdir()
# Context directories
for ctx in ("privat", "dhive", "bahn", "shared"):
(root / ctx).mkdir()
# Shared subdirectories
for sub in ("tools", "powers", "knowledge-store", "config", "mcp-servers"):
(root / "shared" / sub).mkdir(parents=True, exist_ok=True)
# .env files per context
(root / "privat" / ".env").write_text(
"PRIVAT_SECRET=privat_val_123\nPRIVAT_API=http://localhost:3000\n",
encoding="utf-8",
)
(root / "dhive" / ".env").write_text(
"DHIVE_SECRET=dhive_val_456\nDHIVE_TOKEN=tok_dhive\n",
encoding="utf-8",
)
(root / "bahn" / ".env").write_text(
"BAHN_SECRET=bahn_val_789\nBAHN_ENDPOINT=https://bahn.api\n",
encoding="utf-8",
)
(root / "shared" / ".env").write_text(
"SHARED_KEY=shared_val\n", encoding="utf-8"
)
# access-config.yaml
config_dir = root / "shared" / "config"
access_config = {
"contexts": {
"privat": {
"env_file": "privat/.env",
"allowed_shared": ["shared/tools/", "shared/config/"],
},
"dhive": {
"env_file": "dhive/.env",
"allowed_shared": ["shared/tools/", "shared/config/"],
},
"bahn": {
"env_file": "bahn/.env",
"allowed_shared": [
"shared/tools/",
"shared/config/",
"shared/knowledge-store/",
],
},
"shared": {
"env_file": "shared/.env",
"allowed_shared": ["*"],
},
}
}
(config_dir / "access-config.yaml").write_text(
yaml.dump(access_config), encoding="utf-8"
)
# machine-context.yaml (full access)
machine_config = {
"machine": {
"name": "test-hauptrechner",
"description": "Test machine with full access",
"authorized_contexts": ["privat", "dhive", "bahn"],
"key_source": "keyring",
}
}
(config_dir / "machine-context.yaml").write_text(
yaml.dump(machine_config), encoding="utf-8"
)
# team-repos.yaml
team_repos_config = {
"version": "1.0",
"federation": {
"topology": "hub-and-spoke",
"hub_owner": "andre",
"conflict_strategy": "team-wins",
},
"team_repos": [
{
"context": "privat",
"url": "https://github.com/test/privat-team.git",
"branch": "main",
"sync_direction": "bidirectional",
"sync_frequency": "manual",
"shared_mirror": {
"enabled": True,
"paths": ["shared/tools/common-scripts/"],
"mode": "read-only",
},
},
{
"context": "dhive",
"url": "https://gitlab.dhive.io/team/dhive-mono.git",
"branch": "main",
"sync_direction": "bidirectional",
"sync_frequency": "on-push",
"shared_mirror": {
"enabled": True,
"paths": ["shared/tools/"],
"mode": "read-only",
},
},
{
"context": "bahn",
"url": "https://gitlab.2700.2db.it/team/bahn-workspace.git",
"branch": "main",
"sync_direction": "bidirectional",
"sync_frequency": "daily",
"shared_mirror": {
"enabled": True,
"paths": ["shared/tools/"],
"mode": "read-only",
},
},
],
}
(config_dir / "team-repos.yaml").write_text(
yaml.dump(team_repos_config), encoding="utf-8"
)
# repos.yaml (empty registry)
repos_config = {"repos": []}
(config_dir / "repos.yaml").write_text(
yaml.dump(repos_config), encoding="utf-8"
)
# Knowledge store index
ks_path = root / "shared" / "knowledge-store"
(ks_path / "_index.yaml").write_text(
yaml.dump({"version": "1.0", "last_updated": "", "artifacts": []}),
encoding="utf-8",
)
return root
@pytest.fixture()
def full_machine_context() -> MachineContext:
"""MachineContext authorized for all contexts."""
return MachineContext(
name="test-hauptrechner",
description="Full access",
authorized_contexts=["privat", "dhive", "bahn"],
key_source="keyring",
)
@pytest.fixture()
def limited_machine_context() -> MachineContext:
"""MachineContext authorized only for dhive (simulates dhive-laptop)."""
return MachineContext(
name="dhive-laptop",
description="Only dhive access",
authorized_contexts=["dhive"],
key_source="keyring",
)
@pytest.fixture()
def scope_config() -> ScopeConfig:
"""ScopeConfig for knowledge store."""
return ScopeConfig(
scopes={
"privat": {"scope": "privat", "paths": ["privat/"]},
"dhive": {"scope": "dhive", "paths": ["dhive/"]},
"bahn": {"scope": "bahn", "paths": ["bahn/"]},
"shared": {"scope": "shared", "paths": ["shared/"]},
}
)
# ---------------------------------------------------------------------------
# Test 1: Projekt erstellen → Env verschlüsseln → Env laden → Secret-Zugriff
# Requirements: 1.1, 2.1, 9.1, 9.4
# ---------------------------------------------------------------------------
class TestProjectEncryptionSecretAccess:
"""E2E: Project creation → Encrypt env → Load env → Cross-context denial."""
def test_create_project_encrypt_env_load_and_deny_cross_context(
self, monorepo_root: Path, full_machine_context: MachineContext
) -> None:
"""Full flow: create project, encrypt its env, load it, deny cross-access."""
# Step 1: Create a project in privat context
structure_mgr = StructureManager(monorepo_root)
project_path = structure_mgr.create_project("privat", "my-new-app")
assert project_path.exists()
assert project_path == monorepo_root / "privat" / "my-new-app"
# Step 2: Create a project-level .env for the context
env_content = "MY_APP_SECRET=super-secret-123\nDB_URL=postgres://localhost/mydb\n"
env_file = monorepo_root / "privat" / ".env"
env_file.write_text(env_content, encoding="utf-8")
# Step 3: Create encryption manager and encrypt the file
encryption_mgr = SecretEncryptionManager(monorepo_root, full_machine_context)
result = encryption_mgr.encrypt_file(env_file, "privat")
# Encryption may or may not succeed (git-crypt might not be installed)
# but the flow should be exercised regardless
# Step 4: Load env via ContextGuard (with encryption integration)
guard = ContextGuard(
root_path=monorepo_root,
encryption_manager=encryption_mgr,
)
# Loading env for own context should succeed
env_vars = guard.load_env("privat")
assert "MY_APP_SECRET" in env_vars
assert env_vars["MY_APP_SECRET"] == "super-secret-123"
# Step 5: Cross-context access should be denied
access_allowed = guard.check_access("dhive", monorepo_root / "privat" / ".env")
assert access_allowed is False
# dhive should not be able to access privat's .env content via its own context
access_allowed_bahn = guard.check_access("bahn", monorepo_root / "privat" / ".env")
assert access_allowed_bahn is False
def test_shared_tools_accessible_from_any_context(
self, monorepo_root: Path, full_machine_context: MachineContext
) -> None:
"""Shared tools are accessible from all contexts."""
encryption_mgr = SecretEncryptionManager(monorepo_root, full_machine_context)
guard = ContextGuard(root_path=monorepo_root, encryption_manager=encryption_mgr)
# Create a shared tool file
tool_file = monorepo_root / "shared" / "tools" / "helper.py"
tool_file.write_text("# shared helper", encoding="utf-8")
# All contexts can access shared tools
for ctx in ("privat", "dhive", "bahn"):
assert guard.check_access(ctx, tool_file) is True
# ---------------------------------------------------------------------------
# Test 2: Artefakt erstellen → Indexieren → Suchen → Freigeben → Widerrufen
# Requirements: 3.2, 5.2
# ---------------------------------------------------------------------------
class TestArtifactIngestSearchShareRevoke:
"""E2E: Create artifact → Ingest via ETL → Search → Share → Revoke."""
def test_full_knowledge_lifecycle(
self, monorepo_root: Path, scope_config: ScopeConfig
) -> None:
"""Ingest markdown, search it, share it, then revoke the share."""
ks_path = monorepo_root / "shared" / "knowledge-store"
# Step 1: Create a markdown artifact in bahn context source
source_dir = monorepo_root / "bahn" / "docs"
source_dir.mkdir(parents=True)
artifact_md = source_dir / "api-patterns.md"
artifact_md.write_text(
"---\n"
"type: decision\n"
"title: REST API Patterns für InfraGO\n"
"tags: [api, rest, patterns]\n"
"---\n\n"
"# REST API Patterns\n\n"
"Wir verwenden RESTful API-Konventionen für alle neuen Services.\n",
encoding="utf-8",
)
# Step 2: Ingest via KnowledgeStore
store = KnowledgeStore(ks_path, scope_config)
source = MarkdownSource(directory=source_dir)
ingest_result = store.ingest(source, "bahn")
assert ingest_result.processed >= 1
assert ingest_result.updated >= 1
# Step 3: Search the index
results = store.search("REST API", allowed_scopes=["bahn", "shared"])
assert len(results) >= 1
found_titles = [r.entry.title for r in results]
assert any("REST API" in t or "api" in t.lower() for t in found_titles)
# Step 4: Verify scope filtering - privat should not see bahn artifacts
privat_results = store.search("REST API", allowed_scopes=["privat"])
bahn_artifact_in_privat = [
r for r in privat_results if r.entry.scope == "bahn"
]
assert len(bahn_artifact_in_privat) == 0
# Step 5: Share the artifact via ContextBridge
bridge = ContextBridge(
knowledge_base_path=ks_path,
index=store.index,
)
# Get the artifact ID from the index
index_entries = store.get_index(scope="bahn")
assert len(index_entries) >= 1
artifact_id = index_entries[0].id
# Share with user confirmation
share_result = bridge.share_artifact(artifact_id, user_confirmed=True)
assert share_result.success is True
assert bridge.is_shared(artifact_id) is True
# Other contexts can now access the shared content
content = bridge.get_shared_content(artifact_id, "privat")
assert content is not None
assert "REST" in content or "api" in content.lower()
# Step 6: Revoke the share
bridge.revoke_share(artifact_id)
assert bridge.is_shared(artifact_id) is False
# After revocation, other contexts cannot access
content_after = bridge.get_shared_content(artifact_id, "privat")
assert content_after is None
def test_share_blocked_for_sensitive_content(
self, monorepo_root: Path, scope_config: ScopeConfig
) -> None:
"""Sensitive artifacts cannot be shared across contexts."""
ks_path = monorepo_root / "shared" / "knowledge-store"
# Create artifact with sensitive content
source_dir = monorepo_root / "dhive" / "secrets-docs"
source_dir.mkdir(parents=True)
sensitive_md = source_dir / "credentials.md"
sensitive_md.write_text(
"---\n"
"type: note\n"
"title: API Credentials\n"
"tags: [credentials]\n"
"---\n\n"
"# Credentials\n\n"
"api_key: sk-12345-abcdef\n",
encoding="utf-8",
)
# Ingest the sensitive artifact
store = KnowledgeStore(ks_path, scope_config)
source = MarkdownSource(directory=source_dir)
store.ingest(source, "dhive")
# Try to share - should be blocked
bridge = ContextBridge(knowledge_base_path=ks_path, index=store.index)
index_entries = store.get_index(scope="dhive")
assert len(index_entries) >= 1
artifact_id = index_entries[0].id
share_result = bridge.share_artifact(artifact_id, user_confirmed=True)
assert share_result.success is False
assert any("sensib" in e.lower() or "sensitive" in e.lower() for e in share_result.errors)
# ---------------------------------------------------------------------------
# Test 3: Repo einbinden → Sync → Read-Only-Schutz
# Requirements: 4.1
# ---------------------------------------------------------------------------
class TestRepoAddSyncReadonly:
"""E2E: Add repo → Sync → Protect read-only."""
def test_add_repo_sync_and_readonly_protection(
self, monorepo_root: Path
) -> None:
"""Add an external repo, sync it, and verify read-only protection."""
config_path = monorepo_root / "shared" / "config" / "repos.yaml"
# Initialize a git repo in monorepo_root for hook installation
git_dir = monorepo_root / ".git"
git_dir.mkdir()
hooks_dir = git_dir / "hooks"
hooks_dir.mkdir()
repo_mgr = RepoManager(config_path=config_path, monorepo_root=monorepo_root)
# Step 1: Add a read-only repo entry (mock git operations)
entry = RepoEntry(
name="symphony-spec",
url="https://github.com/openai/symphony",
mode="read-only",
target="shared/references/symphony",
pinned="v1.0.0",
mechanism="subtree",
)
# Mock git subprocess to avoid network calls
with patch("monorepo.repos.subprocess.run") as mock_run:
mock_run.return_value = MagicMock(
returncode=0, stdout="", stderr=""
)
repo_mgr.add_repo(entry)
# Verify repo was registered
assert repo_mgr._find_repo("symphony-spec") is not None
# Step 2: Sync the repo (mocked)
# Create the target directory to simulate existing subtree
target_path = monorepo_root / "shared" / "references" / "symphony"
target_path.mkdir(parents=True)
(target_path / "README.md").write_text("# Symphony", encoding="utf-8")
with patch("monorepo.repos.subprocess.run") as mock_run:
mock_run.return_value = MagicMock(
returncode=0, stdout="1 commit pulled", stderr=""
)
sync_result = repo_mgr.sync("symphony-spec")
# Sync should complete (success or graceful failure)
assert sync_result is not None
# Step 3: Protect read-only
repo_mgr.protect_readonly(target_path)
# Verify pre-commit hook was created/updated
hook_path = hooks_dir / "pre-commit"
assert hook_path.exists()
hook_content = hook_path.read_text(encoding="utf-8")
# The hook should reference the protected path
assert "symphony" in hook_content or "references" in hook_content
# ---------------------------------------------------------------------------
# Test 4: Migration → Validierung → Rollback
# Requirements: 6.1
# ---------------------------------------------------------------------------
class TestMigrationValidateRollback:
"""E2E: Migrate repo → Validate → Rollback."""
def test_migrate_validate_and_rollback(self, monorepo_root: Path) -> None:
"""Migrate a repo, validate it, then rollback."""
# Initialize git in monorepo_root
git_dir = monorepo_root / ".git"
git_dir.mkdir(exist_ok=True)
engine = MigrationEngine(monorepo_root=monorepo_root)
plan = MigrationPlan(
source_repo="https://github.com/test/old-project.git",
target_context="dhive",
target_name="old-project",
mode="subtree",
dependencies=[],
order=1,
)
# Step 1: Execute migration (mock git calls)
with patch.object(engine, "_run_git") as mock_git:
mock_git.return_value = MagicMock(
returncode=0, stdout="", stderr=""
)
# Also mock specific detection methods
with patch.object(engine, "_get_remote_branches", return_value=["main"]):
with patch.object(engine, "_get_local_branches", return_value=["main"]):
result = engine.migrate(plan)
# Migration should succeed or detect conflicts gracefully
assert result is not None
# Step 2: Validate the migration
if result.success:
# Create target path to simulate successful migration
target = monorepo_root / "dhive" / "old-project"
target.mkdir(parents=True, exist_ok=True)
(target / "README.md").write_text("# Old Project", encoding="utf-8")
# Mock git validation commands
with patch.object(engine, "_run_git") as mock_git:
mock_git.return_value = MagicMock(
returncode=0, stdout="5", stderr=""
)
validation = engine.validate("old-project")
assert validation is not None
# Step 3: Rollback
with patch.object(engine, "_run_git") as mock_git:
mock_git.return_value = MagicMock(
returncode=0, stdout="", stderr=""
)
engine.rollback("old-project")
# After rollback, the project should no longer be in registry
assert engine.is_migrated("old-project") is False
# ---------------------------------------------------------------------------
# Test 5: Secret verschlüsseln → Maschinenkontext wechseln → Entschlüsselung
# fehlschlägt
# Requirements: 9.1, 9.4
# ---------------------------------------------------------------------------
class TestSecretEncryptionMachineContextSwitch:
"""E2E: Encrypt secret → Switch machine context → Decryption fails."""
def test_encrypt_then_switch_context_decrypt_fails(
self,
monorepo_root: Path,
full_machine_context: MachineContext,
limited_machine_context: MachineContext,
) -> None:
"""Encrypt with full access, then try to decrypt with limited access."""
# Step 1: Encrypt a secret file with full machine context
full_mgr = SecretEncryptionManager(monorepo_root, full_machine_context)
secret_file = monorepo_root / "privat" / ".env"
encrypt_result = full_mgr.encrypt_file(secret_file, "privat")
# The encryption manager should at least attempt to encrypt
# (git-crypt may not be available but the flow is tested)
# Step 2: Verify full context can still decrypt
assert full_mgr.is_authorized("privat") is True
assert full_mgr.is_authorized("dhive") is True
assert full_mgr.is_authorized("bahn") is True
# Step 3: Switch to limited machine context (only dhive)
limited_mgr = SecretEncryptionManager(monorepo_root, limited_machine_context)
# Step 4: Limited context should NOT be authorized for privat
assert limited_mgr.is_authorized("privat") is False
assert limited_mgr.is_authorized("bahn") is False
assert limited_mgr.is_authorized("dhive") is True
# Step 5: Decryption attempt for privat should fail/be denied
decrypt_result = limited_mgr.decrypt_file(secret_file)
# Either returns failure or raises PermissionError
if decrypt_result is not None:
assert decrypt_result.success is False
def test_context_guard_denies_load_env_on_unauthorized_machine(
self,
monorepo_root: Path,
limited_machine_context: MachineContext,
) -> None:
"""ContextGuard denies env loading for unauthorized context on limited machine."""
limited_mgr = SecretEncryptionManager(monorepo_root, limited_machine_context)
guard = ContextGuard(root_path=monorepo_root, encryption_manager=limited_mgr)
# dhive env should load fine (authorized)
env = guard.load_env("dhive")
assert "DHIVE_SECRET" in env
# privat env should fail (not authorized on this machine)
with pytest.raises(PermissionError):
guard.load_env("privat")
# ---------------------------------------------------------------------------
# Test 6: Team_Repo vorbereiten → Isolation prüfen → Sync → Konflikt-Auflösung
# Requirements: 10.3, 10.5
# ---------------------------------------------------------------------------
class TestFederationPrepareIsolateSyncConflict:
"""E2E: Prepare team repo → Verify isolation → Sync → Resolve conflicts."""
def test_prepare_team_repo_verify_isolation(
self, monorepo_root: Path, full_machine_context: MachineContext
) -> None:
"""Prepare team repo and verify it contains only its own context."""
encryption_mgr = SecretEncryptionManager(monorepo_root, full_machine_context)
config_path = monorepo_root / "shared" / "config" / "team-repos.yaml"
fed_mgr = FederationManager(
config_path=config_path,
monorepo_root=monorepo_root,
encryption_manager=encryption_mgr,
)
# Create some content in privat context
(monorepo_root / "privat" / "my-project").mkdir(parents=True)
(monorepo_root / "privat" / "my-project" / "main.py").write_text(
"# My private project\nprint('hello')\n", encoding="utf-8"
)
# Create content in other contexts (should NOT appear in privat team repo)
(monorepo_root / "dhive" / "dhive-project").mkdir(parents=True)
(monorepo_root / "dhive" / "dhive-project" / "app.py").write_text(
"# dhive project", encoding="utf-8"
)
# Step 1: Prepare the team repo for privat
team_repo_path = fed_mgr.prepare_team_repo("privat")
assert team_repo_path.exists()
# Step 2: Verify isolation - no references to other contexts
isolation_report = fed_mgr.verify_isolation(team_repo_path, "privat")
assert isolation_report.is_isolated is True
assert len(isolation_report.leaks) == 0
# Verify privat content is present
privat_files = list(team_repo_path.rglob("*.py"))
assert len(privat_files) >= 1
# Verify dhive/bahn content is NOT present
dhive_files = list(team_repo_path.rglob("*dhive*"))
# Filter out any path references in filenames only
for f in dhive_files:
assert "dhive-project" not in str(f) or not f.exists()
def test_sync_and_conflict_resolution(
self, monorepo_root: Path, full_machine_context: MachineContext
) -> None:
"""Sync from team repo and resolve conflicts with team-wins strategy."""
encryption_mgr = SecretEncryptionManager(monorepo_root, full_machine_context)
config_path = monorepo_root / "shared" / "config" / "team-repos.yaml"
fed_mgr = FederationManager(
config_path=config_path,
monorepo_root=monorepo_root,
encryption_manager=encryption_mgr,
)
# Mock the git subtree operations (no actual network calls)
with patch.object(
fed_mgr.sync_engine, "subtree_pull"
) as mock_pull:
mock_pull.return_value = SyncResult(
success=True,
context="privat",
direction="pull",
commits_synced=3,
conflicts=[],
)
result = fed_mgr.sync_from_team("privat")
assert result.success is True
assert result.commits_synced == 3
# Test conflict resolution with team-wins strategy
with patch.object(
fed_mgr.sync_engine, "subtree_pull"
) as mock_pull:
mock_pull.return_value = SyncResult(
success=False,
context="dhive",
direction="pull",
commits_synced=0,
conflicts=[
ConflictInfo(
file_path="dhive/config.yaml",
conflict_type="content",
source="team-repo",
details="Conflicting changes in config.yaml",
)
],
)
conflict_result = fed_mgr.sync_from_team("dhive")
assert conflict_result.success is False
assert len(conflict_result.conflicts) >= 1
# Resolve conflict with team-wins strategy (mock git operations)
with patch.object(fed_mgr.sync_engine, "_run_git") as mock_git:
# First call: git diff to find conflicted files
mock_git.return_value = MagicMock(
stdout="dhive/config.yaml\n", stderr="", returncode=0
)
resolved = fed_mgr.resolve_conflict("dhive", strategy="team-wins")
assert resolved is not None
assert resolved.context == "dhive"
assert resolved.strategy == "team-wins"
# ---------------------------------------------------------------------------
# Test 7: Shared-Mirror in Team_Repo → Read-Only-Prüfung
# Requirements: 10.12
# ---------------------------------------------------------------------------
class TestSharedMirrorReadOnly:
"""E2E: Mirror shared files into team repo → Verify read-only."""
def test_mirror_shared_to_team_repo_readonly(
self, monorepo_root: Path, full_machine_context: MachineContext
) -> None:
"""Shared files mirrored to team repo should be read-only."""
encryption_mgr = SecretEncryptionManager(monorepo_root, full_machine_context)
config_path = monorepo_root / "shared" / "config" / "team-repos.yaml"
fed_mgr = FederationManager(
config_path=config_path,
monorepo_root=monorepo_root,
encryption_manager=encryption_mgr,
)
# Create shared content to be mirrored
shared_tools = monorepo_root / "shared" / "tools" / "common-scripts"
shared_tools.mkdir(parents=True)
(shared_tools / "deploy.sh").write_text(
"#!/bin/bash\necho 'deploying...'\n", encoding="utf-8"
)
(shared_tools / "lint.sh").write_text(
"#!/bin/bash\necho 'linting...'\n", encoding="utf-8"
)
# Step 1: Mirror shared files to privat team repo
mirror_result = fed_mgr.mirror_shared(
"privat", ["shared/tools/common-scripts/"]
)
assert mirror_result is not None
assert mirror_result.success is True
# Step 2: Verify mirrored paths are recorded
assert len(mirror_result.mirrored_paths) >= 1
# Step 3: Verify context is correct
assert mirror_result.context == "privat"
def test_mirror_shared_with_multiple_paths(
self, monorepo_root: Path, full_machine_context: MachineContext
) -> None:
"""Multiple shared paths can be mirrored to team repo."""
encryption_mgr = SecretEncryptionManager(monorepo_root, full_machine_context)
config_path = monorepo_root / "shared" / "config" / "team-repos.yaml"
fed_mgr = FederationManager(
config_path=config_path,
monorepo_root=monorepo_root,
encryption_manager=encryption_mgr,
)
# Create multiple shared resources
(monorepo_root / "shared" / "tools" / "script-a.py").write_text(
"# script A", encoding="utf-8"
)
(monorepo_root / "shared" / "tools" / "script-b.py").write_text(
"# script B", encoding="utf-8"
)
mirror_result = fed_mgr.mirror_shared("dhive", ["shared/tools/"])
assert mirror_result is not None
assert mirror_result.success is True
assert len(mirror_result.mirrored_paths) >= 1
# ---------------------------------------------------------------------------
# Test: Integration Stack Factory (create_integrated_stack)
# Requirements: 2.2, 9.3, 9.4, 10.10
# ---------------------------------------------------------------------------
class TestIntegratedStackFactory:
"""E2E: Verify create_integrated_stack wires all components correctly."""
def test_create_integrated_stack_full(self, monorepo_root: Path) -> None:
"""Full stack creation with all components wired."""
from monorepo.integration import create_integrated_stack
stack = create_integrated_stack(monorepo_root)
# All components should be created
assert stack["context_guard"] is not None
assert stack["audit_logger"] is not None
assert stack["orchestrator_adapter"] is not None
assert stack["encryption_manager"] is not None
assert stack["federation_manager"] is not None
assert stack["machine_context_manager"] is not None
# Verify the ContextGuard has encryption wired
guard = stack["context_guard"]
assert guard.encryption_manager is not None
# Verify the guard can load env with encryption
env = guard.load_env("privat")
assert "PRIVAT_SECRET" in env
def test_create_integrated_stack_cross_context_denied(
self, monorepo_root: Path
) -> None:
"""Integrated stack correctly denies cross-context access."""
from monorepo.integration import create_integrated_stack
stack = create_integrated_stack(monorepo_root)
guard = stack["context_guard"]
# Cross-context access should be denied
assert guard.check_access("privat", monorepo_root / "dhive" / ".env") is False
assert guard.check_access("dhive", monorepo_root / "bahn" / ".env") is False
assert guard.check_access("bahn", monorepo_root / "privat" / ".env") is False
# Same-context access should be allowed
assert guard.check_access("privat", monorepo_root / "privat" / ".env") is True
assert guard.check_access("dhive", monorepo_root / "dhive" / ".env") is True