diff --git a/WORKLOG.md b/WORKLOG.md
index b4cde30..b505902 100644
--- a/WORKLOG.md
+++ b/WORKLOG.md
@@ -7,3 +7,4 @@
| Date | Task ID | Layer | Action | Files | Notes | device |
|---|---|---|---|---|---|---|
| 2026-06-20 | T3B-2026-06-20 | governance | init | (all) | Initial SSOT bundle generation (T3B) | macbook |
+| 2026-07-23 | AUDIT-LANE-FLAT-LAYOUT-001 | packaging | fix | pyproject.toml, tests/test_install_path.py, tests/__init__.py | Add `[tool.setuptools] py-modules = []` + `[tool.setuptools.packages.find]` no-op to suppress flat-layout auto-discovery; new regression test runs `uv pip install` against the workspace path and asserts success + no flat-layout error. DAG tick: +1. Second lane after PR #42 (fix/auth-kit-gitlink). | macbook |
diff --git a/packages/phenokit-config-kit/ORIGIN.md b/packages/phenokit-config-kit/ORIGIN.md
new file mode 100644
index 0000000..3087918
--- /dev/null
+++ b/packages/phenokit-config-kit/ORIGIN.md
@@ -0,0 +1,5 @@
+# Origin - phenokit-config-kit
+
+Folded from `KooshaPari/PhenoKits/libs/python/phenokit-config-kit` into `KooshaPari/phenotype-python-sdk/packages/phenokit-config-kit` on 2026-06-20.
+
+This package is intentionally kept distinct from `packages/phenotype-config` because the source contains config-kit linting/pre-commit/pytest scaffolds that are not file-equivalent to the existing SDK config package.
diff --git a/packages/phenokit-config-kit/README.md b/packages/phenokit-config-kit/README.md
new file mode 100644
index 0000000..b55e09f
--- /dev/null
+++ b/packages/phenokit-config-kit/README.md
@@ -0,0 +1,226 @@
+# PhenoKit Config Kit
+
+A comprehensive configuration management package for Python projects providing pytest plugins, fixtures, linting configurations, and pre-commit hooks.
+
+## Features
+
+- **Pre-commit Configuration**: Multiple pre-commit hook configurations (basic, comprehensive, security)
+- **Pytest Configuration**: Standardized testing configurations for different scenarios
+- **Pytest Plugins**: Architecture, performance, and security testing plugins
+- **Pytest Fixtures**: Common, performance, and security fixtures for testing
+- **Linting Configuration**: Code quality and style enforcement configs
+- **Test Data Factory**: Generate consistent test data across projects
+
+## Installation
+
+```bash
+# Basic installation
+pip install phenokit-config-kit
+
+# With all dependencies
+pip install phenokit-config-kit[all]
+
+# With specific extras
+pip install phenokit-config-kit[testing,linting]
+```
+
+## Quick Start
+
+### Using Pre-commit Configurations
+
+Copy the desired pre-commit configuration to your project:
+
+```bash
+# Basic configuration
+cp pre-commit/basic.yaml .pre-commit-config.yaml
+
+# Comprehensive configuration
+cp pre-commit/comprehensive.yaml .pre-commit-config.yaml
+
+# Security-focused configuration
+cp pre-commit/security.yaml .pre-commit-config.yaml
+```
+
+Install pre-commit hooks:
+```bash
+pre-commit install
+```
+
+### Using Pytest Configurations
+
+```bash
+# Use basic configuration
+pytest --ini=config-kit/pytest/basic.ini
+
+# Use comprehensive configuration
+pytest --ini=config-kit/pytest/comprehensive.ini
+
+# Use CI configuration
+pytest --ini=config-kit/pytest/ci.ini
+```
+
+### Using Pytest Plugins
+
+The pytest plugins are automatically registered when you install the package:
+
+```python
+# Architecture testing (auto-added to test collection)
+pytest --max-file-size=500 --max-complexity=10
+
+# Performance testing
+pytest -m performance --benchmark-threshold=1.0
+
+# Security testing
+pytest -m security
+```
+
+### Using Fixtures
+
+```python
+import pytest
+from config_kit.pytest.fixtures.common import mock_client, test_data
+from config_kit.pytest.fixtures.performance import performance_monitor
+from config_kit.pytest.fixtures.security import security_context
+
+def test_with_fixtures(mock_client, test_data):
+ result = mock_client.call_tool("test_tool", test_data["user"])
+ assert result["success"] is True
+
+@pytest.mark.performance
+def test_performance(performance_monitor):
+ with performance_monitor["measure"]("test_operation"):
+ result = expensive_operation()
+ assert result is not None
+```
+
+### Using Test Data Factory
+
+```python
+from config_kit.pytest.data.factory import TestDataFactory
+
+factory = TestDataFactory(seed=42)
+
+# Generate user data
+user_data = factory.user_data()
+
+# Generate organization data
+org_data = factory.organization_data()
+
+# Generate project data
+project_data = factory.project_data(organization_id="org-123")
+```
+
+## Configuration Files
+
+### Pre-commit Configurations
+- `pre-commit/basic.yaml` - Basic pre-commit hooks
+- `pre-commit/comprehensive.yaml` - Full linting and security suite
+- `pre-commit/security.yaml` - Security-focused configuration
+
+### Pytest Configurations
+- `pytest/basic.ini` - Basic pytest configuration
+- `pytest/comprehensive.ini` - Enterprise-grade configuration
+- `pytest/ci.ini` - CI/CD optimized
+- `pytest/parallel.ini` - Parallel execution optimized
+- `pytest/performance.ini` - Performance testing
+- `pytest/security.ini` - Security testing
+
+### Linting Configurations
+- `linting/pyproject.toml` - Python linter configs (Ruff, Black, isort, mypy)
+- `linting/.editorconfig` - Editor configuration
+- `linting/.markdownlint.json` - Markdown linting rules
+- `linting/linters.toml` - Centralized linter configuration
+- `linting/ci-cd/gatekeeper.toml` - Quality gate definitions
+
+## Pytest Plugins
+
+### Architecture Plugin
+
+Provides architecture fitness tests:
+- File size validation
+- Import boundary enforcement
+- Dependency direction validation
+- Cyclomatic complexity analysis
+
+### Performance Plugin
+
+Provides performance testing capabilities:
+- Benchmark testing
+- Memory profiling
+- Performance regression detection
+
+### Security Plugin
+
+Provides security testing capabilities:
+- Vulnerability scanning
+- Forbidden imports check
+- Hardcoded secrets detection
+
+## Git Hooks
+
+The package includes useful git hooks:
+
+### Pre-commit Hook
+Validates staged files with linters (Ruff, Black, isort, mypy, bandit, etc.)
+
+### Commit-msg Hook
+Validates commit messages for:
+- Conventional commits format
+- Proper capitalization
+- Proper punctuation
+- Minimum length requirements
+
+To use the git hooks:
+```bash
+cp linting/git-hooks/pre-commit .git/hooks/pre-commit
+chmod +x .git/hooks/pre-commit
+
+cp linting/git-hooks/commit-msg .git/hooks/commit-msg
+chmod +x .git/hooks/commit-msg
+```
+
+## Quality Gate Script
+
+Run comprehensive quality checks:
+
+```bash
+bash linting/ci-cd/scripts/quality-gate.sh
+```
+
+This runs all configured linters and generates a `quality_gate_results.json` report.
+
+## Development
+
+### Project Structure
+
+```
+config-kit/
+├── pyproject.toml # Package configuration
+├── README.md # This file
+├── pre-commit/ # Pre-commit configurations
+├── pytest/ # Pytest configurations, plugins, fixtures
+│ ├── plugins/ # Pytest plugins
+│ ├── fixtures/ # Pytest fixtures
+│ └── data/ # Test data utilities
+└── linting/ # Linting configurations and scripts
+ ├── ci-cd/ # CI/CD scripts
+ └── git-hooks/ # Git hooks
+```
+
+### Running Tests
+
+```bash
+pytest --ini=pytest/comprehensive.ini
+```
+
+## License
+
+MIT License - See LICENSE file for details.
+
+## Contributing
+
+1. Fork the repository
+2. Create a feature branch
+3. Make your changes
+4. Run the quality gate script
+5. Submit a pull request
diff --git a/packages/phenokit-config-kit/config_kit/__init__.py b/packages/phenokit-config-kit/config_kit/__init__.py
new file mode 100644
index 0000000..c3e4a18
--- /dev/null
+++ b/packages/phenokit-config-kit/config_kit/__init__.py
@@ -0,0 +1,16 @@
+"""
+Config Kit - A comprehensive configuration management package for Python projects.
+
+This package provides:
+- Pre-commit hook configurations
+- Pytest configurations, plugins, and fixtures
+- Linting configurations
+- Test data factory
+"""
+
+__version__ = "0.1.0"
+__all__ = [
+ "pytest",
+ "linting",
+ "pre_commit",
+]
diff --git a/packages/phenokit-config-kit/config_kit/pytest/__init__.py b/packages/phenokit-config-kit/config_kit/pytest/__init__.py
new file mode 100644
index 0000000..b90e163
--- /dev/null
+++ b/packages/phenokit-config-kit/config_kit/pytest/__init__.py
@@ -0,0 +1,7 @@
+"""
+Pytest module for config-kit.
+
+This module provides pytest plugins, fixtures, and configurations.
+"""
+
+__version__ = "0.1.0"
diff --git a/packages/phenokit-config-kit/config_kit/pytest/data/__init__.py b/packages/phenokit-config-kit/config_kit/pytest/data/__init__.py
new file mode 100644
index 0000000..4269819
--- /dev/null
+++ b/packages/phenokit-config-kit/config_kit/pytest/data/__init__.py
@@ -0,0 +1,13 @@
+"""
+Test data management for config-kit package.
+
+This module provides utilities for managing test data across different projects.
+"""
+
+__version__ = "0.1.0"
+__all__ = [
+ "TestDataFactory",
+ "TestDataGenerator",
+ "TestDataManager",
+ "TestDataValidator",
+]
diff --git a/packages/phenokit-config-kit/config_kit/pytest/data/factory.py b/packages/phenokit-config-kit/config_kit/pytest/data/factory.py
new file mode 100644
index 0000000..10944f7
--- /dev/null
+++ b/packages/phenokit-config-kit/config_kit/pytest/data/factory.py
@@ -0,0 +1,225 @@
+"""
+Test data factory for generating test data.
+
+This module provides a factory for generating test data across different projects.
+"""
+
+import random
+import string
+import uuid
+from dataclasses import dataclass, field
+from datetime import datetime, timedelta
+from typing import Any
+
+
+@dataclass
+class TestDataFactory:
+ """Factory for generating test data."""
+
+ seed: int | None = None
+ _random: random.Random = field(default_factory=random.Random)
+
+ def __post_init__(self):
+ if self.seed is not None:
+ self._random.seed(self.seed)
+
+ def random_string(self, length: int = 10, chars: str = None) -> str:
+ """Generate a random string."""
+ if chars is None:
+ chars = string.ascii_letters + string.digits
+ return "".join(self._random.choices(chars, k=length))
+
+ def random_email(self, domain: str = "example.com") -> str:
+ """Generate a random email address."""
+ username = self.random_string(8, string.ascii_lowercase + string.digits)
+ return f"{username}@{domain}"
+
+ def random_uuid(self) -> str:
+ """Generate a random UUID."""
+ return str(uuid.uuid4())
+
+ def random_int(self, min_val: int = 0, max_val: int = 100) -> int:
+ """Generate a random integer."""
+ return self._random.randint(min_val, max_val)
+
+ def random_float(self, min_val: float = 0.0, max_val: float = 1.0) -> float:
+ """Generate a random float."""
+ return self._random.uniform(min_val, max_val)
+
+ def random_choice(self, choices: list[Any]) -> Any:
+ """Choose a random item from a list."""
+ return self._random.choice(choices)
+
+ def random_choices(self, choices: list[Any], k: int = 1) -> list[Any]:
+ """Choose multiple random items from a list."""
+ return self._random.choices(choices, k=k)
+
+ def random_bool(self) -> bool:
+ """Generate a random boolean."""
+ return self._random.choice([True, False])
+
+ def random_datetime(self, start: datetime = None, end: datetime = None) -> datetime:
+ """Generate a random datetime."""
+ if start is None:
+ start = datetime.now() - timedelta(days=365)
+ if end is None:
+ end = datetime.now()
+
+ delta = end - start
+ random_seconds = self._random.randint(0, int(delta.total_seconds()))
+ return start + timedelta(seconds=random_seconds)
+
+ def random_date(self, start: datetime = None, end: datetime = None) -> datetime:
+ """Generate a random date (time set to 00:00:00)."""
+ dt = self.random_datetime(start, end)
+ return dt.replace(hour=0, minute=0, second=0, microsecond=0)
+
+ def user_data(self, **overrides) -> dict[str, Any]:
+ """Generate user test data."""
+ data = {
+ "id": self.random_uuid(),
+ "username": self.random_string(8, string.ascii_lowercase + string.digits),
+ "email": self.random_email(),
+ "full_name": f"{self.random_string(6)} {self.random_string(8)}",
+ "roles": self.random_choices(["user", "admin", "moderator"], k=self.random_int(1, 3)),
+ "created_at": self.random_datetime(),
+ "updated_at": self.random_datetime(),
+ "is_active": self.random_bool(),
+ }
+ data.update(overrides)
+ return data
+
+ def organization_data(self, **overrides) -> dict[str, Any]:
+ """Generate organization test data."""
+ data = {
+ "id": self.random_uuid(),
+ "name": f"Test Organization {self.random_string(6)}",
+ "description": f"Test organization description {self.random_string(20)}",
+ "created_at": self.random_datetime(),
+ "updated_at": self.random_datetime(),
+ "is_active": self.random_bool(),
+ }
+ data.update(overrides)
+ return data
+
+ def project_data(self, organization_id: str = None, **overrides) -> dict[str, Any]:
+ """Generate project test data."""
+ data = {
+ "id": self.random_uuid(),
+ "name": f"Test Project {self.random_string(6)}",
+ "description": f"Test project description {self.random_string(20)}",
+ "status": self.random_choice(["active", "inactive", "archived"]),
+ "organization_id": organization_id or self.random_uuid(),
+ "created_at": self.random_datetime(),
+ "updated_at": self.random_datetime(),
+ }
+ data.update(overrides)
+ return data
+
+ def document_data(self, project_id: str = None, **overrides) -> dict[str, Any]:
+ """Generate document test data."""
+ data = {
+ "id": self.random_uuid(),
+ "title": f"Test Document {self.random_string(6)}",
+ "content": f"Test document content {self.random_string(50)}",
+ "project_id": project_id or self.random_uuid(),
+ "created_at": self.random_datetime(),
+ "updated_at": self.random_datetime(),
+ }
+ data.update(overrides)
+ return data
+
+ def requirement_data(self, document_id: str = None, **overrides) -> dict[str, Any]:
+ """Generate requirement test data."""
+ data = {
+ "id": self.random_uuid(),
+ "title": f"Test Requirement {self.random_string(6)}",
+ "description": f"Test requirement description {self.random_string(30)}",
+ "priority": self.random_choice(["low", "medium", "high", "critical"]),
+ "status": self.random_choice(["draft", "review", "approved", "rejected"]),
+ "document_id": document_id or self.random_uuid(),
+ "created_at": self.random_datetime(),
+ "updated_at": self.random_datetime(),
+ }
+ data.update(overrides)
+ return data
+
+ def test_data(self, project_id: str = None, **overrides) -> dict[str, Any]:
+ """Generate test entity data."""
+ data = {
+ "id": self.random_uuid(),
+ "name": f"Test {self.random_string(6)}",
+ "description": f"Test description {self.random_string(20)}",
+ "status": self.random_choice(["pending", "running", "passed", "failed", "skipped"]),
+ "project_id": project_id or self.random_uuid(),
+ "created_at": self.random_datetime(),
+ "updated_at": self.random_datetime(),
+ }
+ data.update(overrides)
+ return data
+
+ def relationship_data(self, source_id: str = None, target_id: str = None, **overrides) -> dict[str, Any]:
+ """Generate relationship test data."""
+ data = {
+ "id": self.random_uuid(),
+ "source_id": source_id or self.random_uuid(),
+ "target_id": target_id or self.random_uuid(),
+ "relationship_type": self.random_choice(["parent", "child", "sibling", "related"]),
+ "created_at": self.random_datetime(),
+ "updated_at": self.random_datetime(),
+ }
+ data.update(overrides)
+ return data
+
+ def workflow_data(self, project_id: str = None, **overrides) -> dict[str, Any]:
+ """Generate workflow test data."""
+ data = {
+ "id": self.random_uuid(),
+ "name": f"Test Workflow {self.random_string(6)}",
+ "description": f"Test workflow description {self.random_string(20)}",
+ "status": self.random_choice(["draft", "active", "paused", "completed"]),
+ "project_id": project_id or self.random_uuid(),
+ "created_at": self.random_datetime(),
+ "updated_at": self.random_datetime(),
+ }
+ data.update(overrides)
+ return data
+
+ def query_data(self, project_id: str = None, **overrides) -> dict[str, Any]:
+ """Generate query test data."""
+ data = {
+ "id": self.random_uuid(),
+ "name": f"Test Query {self.random_string(6)}",
+ "query": f"SELECT * FROM test_table WHERE id = '{self.random_uuid()}'",
+ "project_id": project_id or self.random_uuid(),
+ "created_at": self.random_datetime(),
+ "updated_at": self.random_datetime(),
+ }
+ data.update(overrides)
+ return data
+
+ def create_related_data(self, entity_type: str, count: int = 5, **overrides) -> list[dict[str, Any]]:
+ """Create related test data."""
+ data = []
+ for _ in range(count):
+ if entity_type == "user":
+ data.append(self.user_data(**overrides))
+ elif entity_type == "organization":
+ data.append(self.organization_data(**overrides))
+ elif entity_type == "project":
+ data.append(self.project_data(**overrides))
+ elif entity_type == "document":
+ data.append(self.document_data(**overrides))
+ elif entity_type == "requirement":
+ data.append(self.requirement_data(**overrides))
+ elif entity_type == "test":
+ data.append(self.test_data(**overrides))
+ elif entity_type == "relationship":
+ data.append(self.relationship_data(**overrides))
+ elif entity_type == "workflow":
+ data.append(self.workflow_data(**overrides))
+ elif entity_type == "query":
+ data.append(self.query_data(**overrides))
+ else:
+ raise ValueError(f"Unknown entity type: {entity_type}")
+ return data
diff --git a/packages/phenokit-config-kit/config_kit/pytest/fixtures/__init__.py b/packages/phenokit-config-kit/config_kit/pytest/fixtures/__init__.py
new file mode 100644
index 0000000..fe01c35
--- /dev/null
+++ b/packages/phenokit-config-kit/config_kit/pytest/fixtures/__init__.py
@@ -0,0 +1,18 @@
+"""
+Pytest fixtures for config-kit package.
+
+This module provides a collection of pytest fixtures that can be used
+across different Python projects.
+"""
+
+__version__ = "0.1.0"
+__all__ = [
+ "cache_fixture",
+ "database_fixture",
+ "message_broker_fixture",
+ "mock_client",
+ "performance_monitor",
+ "security_context",
+ "temp_dir",
+ "test_data",
+]
diff --git a/packages/phenokit-config-kit/config_kit/pytest/fixtures/common.py b/packages/phenokit-config-kit/config_kit/pytest/fixtures/common.py
new file mode 100644
index 0000000..598cde9
--- /dev/null
+++ b/packages/phenokit-config-kit/config_kit/pytest/fixtures/common.py
@@ -0,0 +1,226 @@
+"""
+Common pytest fixtures for testing.
+
+This module provides commonly used fixtures across different projects.
+"""
+
+import os
+import shutil
+import tempfile
+from collections.abc import Generator
+from pathlib import Path
+from typing import Any
+from unittest.mock import Mock
+
+import pytest
+
+
+@pytest.fixture
+def temp_dir() -> Generator[Path, None, None]:
+ """Create a temporary directory for testing."""
+ temp_path = Path(tempfile.mkdtemp())
+ try:
+ yield temp_path
+ finally:
+ shutil.rmtree(temp_path, ignore_errors=True)
+
+
+@pytest.fixture
+def mock_client() -> Mock:
+ """Create a mock client for testing."""
+ client = Mock()
+ client.call_tool = Mock(return_value={"success": True, "data": {}})
+ client.list_tools = Mock(return_value={"tools": []})
+ client.list_resources = Mock(return_value={"resources": []})
+ return client
+
+
+@pytest.fixture
+def test_data() -> dict[str, Any]:
+ """Provide test data for testing."""
+ return {
+ "user": {
+ "id": "test-user-123",
+ "name": "Test User",
+ "email": "test@example.com",
+ "roles": ["user"],
+ },
+ "organization": {
+ "id": "test-org-123",
+ "name": "Test Organization",
+ "description": "Test organization for testing",
+ },
+ "project": {
+ "id": "test-project-123",
+ "name": "Test Project",
+ "status": "active",
+ "organization_id": "test-org-123",
+ },
+ "document": {
+ "id": "test-doc-123",
+ "title": "Test Document",
+ "content": "Test document content",
+ "project_id": "test-project-123",
+ },
+ }
+
+
+@pytest.fixture
+def performance_monitor() -> Generator[dict[str, Any], None, None]:
+ """Monitor performance during test execution."""
+ import time
+
+ import psutil
+
+ start_time = time.time()
+ start_memory = psutil.Process().memory_info().rss / 1024 / 1024
+
+ monitor_data = {
+ "start_time": start_time,
+ "start_memory": start_memory,
+ "peak_memory": start_memory,
+ "measurements": [],
+ }
+
+ def measure(name: str):
+ current_time = time.time()
+ current_memory = psutil.Process().memory_info().rss / 1024 / 1024
+
+ measurement = {
+ "name": name,
+ "timestamp": current_time,
+ "memory": current_memory,
+ "duration": current_time - start_time,
+ }
+
+ monitor_data["measurements"].append(measurement)
+
+ monitor_data["peak_memory"] = max(monitor_data["peak_memory"], current_memory)
+
+ return measurement
+
+ monitor_data["measure"] = measure
+
+ try:
+ yield monitor_data
+ finally:
+ end_time = time.time()
+ end_memory = psutil.Process().memory_info().rss / 1024 / 1024
+
+ monitor_data["end_time"] = end_time
+ monitor_data["end_memory"] = end_memory
+ monitor_data["total_duration"] = end_time - start_time
+ monitor_data["memory_delta"] = end_memory - start_memory
+
+
+@pytest.fixture
+def security_context() -> dict[str, Any]:
+ """Provide security context for testing."""
+ return {
+ "user_id": "test-user-123",
+ "organization_id": "test-org-123",
+ "roles": ["user"],
+ "permissions": ["read", "write"],
+ "auth_token": "test-token-123",
+ "session_id": "test-session-123",
+ }
+
+
+@pytest.fixture
+def mock_http_client() -> Mock:
+ """Create a mock HTTP client for testing."""
+ client = Mock()
+ client.get = Mock(return_value=Mock(status_code=200, json=dict))
+ client.post = Mock(return_value=Mock(status_code=201, json=dict))
+ client.put = Mock(return_value=Mock(status_code=200, json=dict))
+ client.delete = Mock(return_value=Mock(status_code=204, json=dict))
+ return client
+
+
+@pytest.fixture
+def mock_database() -> Mock:
+ """Create a mock database for testing."""
+ db = Mock()
+ db.execute = Mock(return_value=Mock(rowcount=1))
+ db.fetchone = Mock(return_value={})
+ db.fetchall = Mock(return_value=[])
+ db.commit = Mock()
+ db.rollback = Mock()
+ return db
+
+
+@pytest.fixture
+def mock_cache() -> Mock:
+ """Create a mock cache for testing."""
+ cache = Mock()
+ cache.get = Mock(return_value=None)
+ cache.set = Mock(return_value=True)
+ cache.delete = Mock(return_value=True)
+ cache.clear = Mock(return_value=True)
+ return cache
+
+
+@pytest.fixture
+def mock_message_broker() -> Mock:
+ """Create a mock message broker for testing."""
+ broker = Mock()
+ broker.publish = Mock(return_value=True)
+ broker.subscribe = Mock(return_value=Mock())
+ broker.unsubscribe = Mock(return_value=True)
+ return broker
+
+
+@pytest.fixture
+def mock_file_system() -> Mock:
+ """Create a mock file system for testing."""
+ fs = Mock()
+ fs.exists = Mock(return_value=True)
+ fs.read_text = Mock(return_value="test content")
+ fs.write_text = Mock(return_value=10)
+ fs.mkdir = Mock(return_value=True)
+ fs.rmdir = Mock(return_value=True)
+ fs.unlink = Mock(return_value=True)
+ return fs
+
+
+@pytest.fixture
+def mock_logger() -> Mock:
+ """Create a mock logger for testing."""
+ logger = Mock()
+ logger.debug = Mock()
+ logger.info = Mock()
+ logger.warning = Mock()
+ logger.error = Mock()
+ logger.critical = Mock()
+ return logger
+
+
+@pytest.fixture
+def mock_config() -> Mock:
+ """Create a mock configuration for testing."""
+ config = Mock()
+ config.get = Mock(return_value="default_value")
+ config.set = Mock(return_value=True)
+ config.has = Mock(return_value=True)
+ return config
+
+
+@pytest.fixture
+def mock_environment() -> Generator[dict[str, str], None, None]:
+ """Create a mock environment for testing."""
+ original_env = os.environ.copy()
+
+ test_env = {
+ "TEST_MODE": "true",
+ "TEST_DATABASE_URL": "sqlite:///:memory:",
+ "TEST_CACHE_URL": "memory://",
+ "TEST_MESSAGE_BROKER_URL": "memory://",
+ }
+
+ os.environ.update(test_env)
+
+ try:
+ yield test_env
+ finally:
+ os.environ.clear()
+ os.environ.update(original_env)
diff --git a/packages/phenokit-config-kit/config_kit/pytest/fixtures/performance.py b/packages/phenokit-config-kit/config_kit/pytest/fixtures/performance.py
new file mode 100644
index 0000000..5c74ef9
--- /dev/null
+++ b/packages/phenokit-config-kit/config_kit/pytest/fixtures/performance.py
@@ -0,0 +1,183 @@
+"""
+Performance testing fixtures for pytest.
+
+This module provides fixtures specifically for performance testing.
+"""
+
+import threading
+import time
+from collections.abc import Generator
+from contextlib import contextmanager
+from typing import Any
+from unittest.mock import Mock
+
+import psutil
+import pytest
+
+
+@pytest.fixture
+def performance_monitor() -> Generator[dict[str, Any], None, None]:
+ """Monitor performance during test execution."""
+ start_time = time.time()
+ start_memory = psutil.Process().memory_info().rss / 1024 / 1024
+
+ monitor_data = {
+ "start_time": start_time,
+ "start_memory": start_memory,
+ "peak_memory": start_memory,
+ "measurements": [],
+ "threads": [],
+ }
+
+ def measure(name: str, **kwargs):
+ current_time = time.time()
+ current_memory = psutil.Process().memory_info().rss / 1024 / 1024
+
+ measurement = {
+ "name": name,
+ "timestamp": current_time,
+ "memory": current_memory,
+ "duration": current_time - start_time,
+ **kwargs,
+ }
+
+ monitor_data["measurements"].append(measurement)
+
+ monitor_data["peak_memory"] = max(monitor_data["peak_memory"], current_memory)
+
+ return measurement
+
+ def start_thread_monitoring():
+ """Start background thread monitoring."""
+ def monitor():
+ while True:
+ current_memory = psutil.Process().memory_info().rss / 1024 / 1024
+ monitor_data["peak_memory"] = max(monitor_data["peak_memory"], current_memory)
+ time.sleep(0.1)
+
+ thread = threading.Thread(target=monitor, daemon=True)
+ thread.start()
+ monitor_data["threads"].append(thread)
+
+ monitor_data["measure"] = measure
+ monitor_data["start_thread_monitoring"] = start_thread_monitoring
+
+ try:
+ yield monitor_data
+ finally:
+ end_time = time.time()
+ end_memory = psutil.Process().memory_info().rss / 1024 / 1024
+
+ monitor_data["end_time"] = end_time
+ monitor_data["end_memory"] = end_memory
+ monitor_data["total_duration"] = end_time - start_time
+ monitor_data["memory_delta"] = end_memory - start_memory
+
+
+@pytest.fixture
+def benchmark_data() -> Generator[dict[str, Any], None, None]:
+ """Provide benchmark data for testing."""
+ return {
+ "iterations": 1000,
+ "warmup_iterations": 100,
+ "timeout": 60.0,
+ "threshold": 1.0,
+ "results": [],
+ }
+
+
+@pytest.fixture
+def load_test_data() -> Generator[dict[str, Any], None, None]:
+ """Provide load test data for testing."""
+ return {
+ "users": 10,
+ "duration": 60,
+ "ramp_up": 10,
+ "ramp_down": 10,
+ "requests_per_second": 100,
+ "concurrent_requests": 50,
+ }
+
+
+@pytest.fixture
+def memory_profiler() -> Generator[dict[str, Any], None, None]:
+ """Provide memory profiling capabilities."""
+ profiler_data = {
+ "snapshots": [],
+ "baseline": None,
+ "current": None,
+ }
+
+ def take_snapshot(name: str):
+ snapshot = {
+ "name": name,
+ "timestamp": time.time(),
+ "memory": psutil.Process().memory_info().rss / 1024 / 1024,
+ "memory_percent": psutil.Process().memory_percent(),
+ "cpu_percent": psutil.Process().cpu_percent(),
+ }
+ profiler_data["snapshots"].append(snapshot)
+ profiler_data["current"] = snapshot
+ return snapshot
+
+ def set_baseline():
+ profiler_data["baseline"] = take_snapshot("baseline")
+
+ def get_memory_delta():
+ if profiler_data["baseline"] and profiler_data["current"]:
+ return profiler_data["current"]["memory"] - profiler_data["baseline"]["memory"]
+ return 0
+
+ profiler_data["take_snapshot"] = take_snapshot
+ profiler_data["set_baseline"] = set_baseline
+ profiler_data["get_memory_delta"] = get_memory_delta
+
+ yield profiler_data
+
+
+@pytest.fixture
+def performance_thresholds() -> dict[str, float]:
+ """Provide performance thresholds for testing."""
+ return {
+ "max_duration": 1.0, # seconds
+ "max_memory": 100.0, # MB
+ "max_cpu": 80.0, # percent
+ "max_memory_delta": 50.0, # MB
+ }
+
+
+@pytest.fixture
+def mock_performance_client() -> Mock:
+ """Create a mock performance client for testing."""
+ client = Mock()
+ client.benchmark = Mock(return_value={"duration": 0.5, "memory": 50.0})
+ client.profile = Mock(return_value={"cpu": 10.0, "memory": 50.0})
+ client.load_test = Mock(return_value={"throughput": 100.0, "latency": 0.1})
+ return client
+
+
+@contextmanager
+def performance_context(name: str, threshold: float = 1.0):
+ """Context manager for performance monitoring."""
+ start_time = time.time()
+ start_memory = psutil.Process().memory_info().rss / 1024 / 1024
+
+ try:
+ yield
+ finally:
+ end_time = time.time()
+ end_memory = psutil.Process().memory_info().rss / 1024 / 1024
+
+ duration = end_time - start_time
+ memory_delta = end_memory - start_memory
+
+ if duration > threshold:
+ pytest.fail(f"Performance threshold exceeded for {name}: {duration:.2f}s > {threshold}s")
+
+ print(f"Performance: {name} - Duration: {duration:.2f}s, Memory: {memory_delta:.2f}MB")
+
+
+@pytest.fixture
+def performance_context_factory():
+ """Factory for creating performance contexts."""
+ return performance_context
diff --git a/packages/phenokit-config-kit/config_kit/pytest/fixtures/security.py b/packages/phenokit-config-kit/config_kit/pytest/fixtures/security.py
new file mode 100644
index 0000000..e2f5d5e
--- /dev/null
+++ b/packages/phenokit-config-kit/config_kit/pytest/fixtures/security.py
@@ -0,0 +1,196 @@
+"""
+Security testing fixtures for pytest.
+
+This module provides fixtures specifically for security testing.
+"""
+
+import hashlib
+import hmac
+from collections.abc import Generator
+from typing import Any
+from unittest.mock import Mock
+
+import pytest
+
+
+@pytest.fixture
+def security_context() -> Generator[dict[str, Any], None, None]:
+ """Provide security context for testing."""
+ return {
+ "user_id": "test-user-123",
+ "organization_id": "test-org-123",
+ "roles": ["user"],
+ "permissions": ["read", "write"],
+ "auth_token": "test-token-123",
+ "session_id": "test-session-123",
+ "ip_address": "127.0.0.1",
+ "user_agent": "test-agent",
+ "request_id": "test-request-123",
+ }
+
+
+@pytest.fixture
+def mock_auth_client() -> Mock:
+ """Create a mock authentication client for testing."""
+ client = Mock()
+ client.authenticate = Mock(return_value={"success": True, "user_id": "test-user-123"})
+ client.authorize = Mock(return_value={"success": True, "permissions": ["read", "write"]})
+ client.validate_token = Mock(return_value={"valid": True, "user_id": "test-user-123"})
+ client.refresh_token = Mock(return_value={"success": True, "token": "new-token-123"})
+ client.logout = Mock(return_value={"success": True})
+ return client
+
+
+@pytest.fixture
+def mock_security_scanner() -> Mock:
+ """Create a mock security scanner for testing."""
+ scanner = Mock()
+ scanner.scan_vulnerabilities = Mock(return_value={"vulnerabilities": []})
+ scanner.scan_dependencies = Mock(return_value={"vulnerabilities": []})
+ scanner.scan_secrets = Mock(return_value={"secrets": []})
+ scanner.scan_code = Mock(return_value={"issues": []})
+ return scanner
+
+
+@pytest.fixture
+def test_credentials() -> dict[str, str]:
+ """Provide test credentials for testing."""
+ return {
+ "username": "testuser",
+ "password": "testpassword123",
+ "email": "test@example.com",
+ "api_key": "test-api-key-123",
+ "secret": "test-secret-123",
+ }
+
+
+@pytest.fixture
+def test_tokens() -> dict[str, str]:
+ """Provide test tokens for testing."""
+ return {
+ "access_token": "test-access-token-123",
+ "refresh_token": "test-refresh-token-123",
+ "id_token": "test-id-token-123",
+ "csrf_token": "test-csrf-token-123",
+ }
+
+
+@pytest.fixture
+def test_hashes() -> dict[str, str]:
+ """Provide test hashes for testing."""
+ test_string = "test-string-123"
+ return {
+ "md5": hashlib.md5(test_string.encode()).hexdigest(),
+ "sha1": hashlib.sha1(test_string.encode()).hexdigest(),
+ "sha256": hashlib.sha256(test_string.encode()).hexdigest(),
+ "sha512": hashlib.sha512(test_string.encode()).hexdigest(),
+ "hmac": hmac.new(b"secret-key", test_string.encode(), hashlib.sha256).hexdigest(),
+ }
+
+
+@pytest.fixture
+def test_encryption_data() -> dict[str, Any]:
+ """Provide test encryption data for testing."""
+ return {
+ "plaintext": "test-plaintext-123",
+ "ciphertext": "encrypted-test-data-123",
+ "key": "test-encryption-key-123",
+ "iv": "test-initialization-vector-123",
+ "algorithm": "AES-256-GCM",
+ }
+
+
+@pytest.fixture
+def test_sql_injection_payloads() -> list[str]:
+ """Provide test SQL injection payloads for testing."""
+ return [
+ "'; DROP TABLE users; --",
+ "1' OR '1'='1",
+ "1' UNION SELECT * FROM users --",
+ "1'; INSERT INTO users (username, password) VALUES ('hacker', 'password'); --",
+ "1' AND (SELECT COUNT(*) FROM users) > 0 --",
+ ]
+
+
+@pytest.fixture
+def test_xss_payloads() -> list[str]:
+ """Provide test XSS payloads for testing."""
+ return [
+ "",
+ "
",
+ "