"""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