Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
b8705fb
feat(runtime): Add generic handler factory for serverless execution
deanq Jan 3, 2026
8c0b62a
feat(cli): Add handler generator, manifest builder, and scanner for b…
deanq Jan 3, 2026
c14ed9f
test(runtime): Add comprehensive tests for generic handler
deanq Jan 3, 2026
8c84c34
test(cli): Add tests for handler generation, manifest building, and s…
deanq Jan 3, 2026
cc77fa5
docs(runtime): Document generic handler factory architecture
deanq Jan 3, 2026
72ff4a1
docs(cli): Add flash build command documentation
deanq Jan 3, 2026
e761d48
docs: Add build process and handler generation section to README
deanq Jan 3, 2026
9af1505
feat(cli): Integrate build utilities into flash build command
deanq Jan 3, 2026
b1968d6
refactor(build): Fix directory structure and add comprehensive error …
deanq Jan 3, 2026
8717dc3
feat(resources): Add LoadBalancerSlsResource for LB endpoints
deanq Jan 4, 2026
3cdb565
fix(test): Fix LoadBalancerSlsResource deployment test mocks
deanq Jan 4, 2026
daa1375
feat(resources): Phase 1 - Core infrastructure for @remote on LB endp…
deanq Jan 4, 2026
d02082b
feat(build): Phase 2.1 - Enhanced scanner for HTTP routing extraction
deanq Jan 4, 2026
e83c4f0
feat(build): Phase 2.2 - Updated manifest schema for HTTP routing
deanq Jan 4, 2026
3b41ca4
feat(cli): Add LB handler generator for FastAPI app creation
deanq Jan 4, 2026
6cc2888
feat(runtime): Implement LB handler factory for FastAPI app creation
deanq Jan 4, 2026
babfe12
feat(cli): Route build command to separate handlers for LB endpoints
deanq Jan 4, 2026
c9a160b
feat(resources): Add LiveLoadBalancer for local LB endpoint testing
deanq Jan 4, 2026
7f1961b
test(stubs): Add comprehensive unit tests for LoadBalancerSlsStub
deanq Jan 4, 2026
bc8f733
fix(test): Correct LB endpoint test decorator to match assertions
deanq Jan 4, 2026
79e8f88
docs: Add comprehensive documentation for @remote with LoadBalancer e…
deanq Jan 4, 2026
47d73f8
security: Remove /execute from deployed LoadBalancer endpoints
deanq Jan 4, 2026
2353c69
feat(build): Phase 4 - Fix LiveLoadBalancer handler generation to inc…
deanq Jan 4, 2026
d86b58c
fix(scanner): Discover LoadBalancer resources in addition to Serverle…
deanq Jan 4, 2026
db28ae0
chore: Format code for line length and remove unused imports
deanq Jan 4, 2026
7304d17
fix: Address PR #131 review feedback
deanq Jan 4, 2026
0218995
style: Format datetime chaining for line length
deanq Jan 4, 2026
483536b
fix: LiveLoadBalancer template not serialized to RunPod GraphQL
deanq Jan 4, 2026
ca8cd7e
fix: LoadBalancer endpoint URL and add CPU support
deanq Jan 4, 2026
17bf287
fix: Export CpuLiveLoadBalancer and CpuLoadBalancerSlsResource from t…
deanq Jan 4, 2026
a5368b7
fix: Add API key authentication to LoadBalancer health check
deanq Jan 4, 2026
8cd129a
fix(lb): Exclude flashboot from CpuLoadBalancerSlsResource GraphQL pa…
deanq Jan 4, 2026
cc73b94
fix(lb): Expand CpuInstanceType.ANY to all CPU flavors in CpuLoadBala…
deanq Jan 4, 2026
8bf1739
refactor(cpu): Move instanceIds validator to CpuEndpointMixin
deanq Jan 4, 2026
8f31e03
test: Update CPU instance test to reflect validator expansion
deanq Jan 4, 2026
5da2441
fix(lb): Increase health check timeout from 5s to 15s
deanq Jan 4, 2026
586286d
fix(lb): Fix CPU load balancer template deployment error
deanq Jan 4, 2026
027965c
fix(drift): Exclude runtime fields from config hash to prevent false …
deanq Jan 4, 2026
1b55718
fix(http): Standardize RunPod HTTP client authentication across codebase
deanq Jan 5, 2026
8b97197
feat(http): Extend HTTP utilities to cover both sync and async authen…
deanq Jan 5, 2026
9f4e19a
fix: Address PR feedback on HTTP utilities implementation
deanq Jan 5, 2026
462654b
Merge branch 'deanq/ae-1102-load-balancer-sls-resource' into deanq/ae…
deanq Jan 5, 2026
b57748f
refactor(drift): Extract runtime field constants and improve maintain…
deanq Jan 5, 2026
915f574
docs: Improve LoadBalancer documentation accuracy and completeness
deanq Jan 5, 2026
1c6d99d
docs: add resource config drift detection documentation
deanq Jan 5, 2026
f719c73
docs: proper name for the file
deanq Jan 5, 2026
2a2a21d
test(build): Add comprehensive test coverage for scanner and handler …
deanq Jan 6, 2026
17d338a
Merge branch 'deanq/ae-1251-handler-mapper' into deanq/ae-1102-load-b…
deanq Jan 6, 2026
5ead8e7
Merge branch 'deanq/ae-1102-load-balancer-sls-resource' into deanq/ae…
deanq Jan 6, 2026
6d3ff3b
test(scanner): Fix resource type assertions to match scanner behavior
deanq Jan 6, 2026
8fe3d67
Merge branch 'deanq/ae-1102-load-balancer-sls-resource' into deanq/ae…
deanq Jan 6, 2026
2640b98
Merge branch 'main' into deanq/ae-1196-absolute-drift-detection
deanq Jan 8, 2026
6431b62
chore: merge correction
deanq Jan 8, 2026
1c31455
fix(drift): Remove manual undeploy/deploy from update() method
deanq Jan 9, 2026
426ba16
docs(drift): Clarify _has_structural_changes detects version-triggeri…
deanq Jan 9, 2026
42382af
feat(drift): Enable environment variable drift detection
deanq Jan 9, 2026
d02d8c8
test(drift): Update tests for environment variable drift detection
deanq Jan 9, 2026
9ea43f2
Merge branch 'main' into deanq/ae-1196-absolute-drift-detection
deanq Jan 9, 2026
c8bab65
fix: Address Copilot review feedback on type hints and documentation
deanq Jan 9, 2026
8464d14
chore: Update Python version compatibility to 3.10-3.14
deanq Jan 11, 2026
27aa3e7
chore: Increase code coverage requirement to 65%
deanq Jan 11, 2026
9b27521
Merge branch 'main' into deanq/ae-1679-python-310-314-compatibility
deanq Jan 12, 2026
a1a5154
Merge branch 'main' into deanq/ae-1679-python-310-314-compatibility
deanq Jan 12, 2026
ceffe7d
Merge branch 'main' into deanq/ae-1679-python-310-314-compatibility
deanq Jan 14, 2026
06f5dc3
Merge remote-tracking branch 'origin/main' into deanq/ae-1679-python-…
deanq Jan 15, 2026
a11419c
perf(tests): make parallel test execution the default
deanq Jan 20, 2026
d88c173
Merge branch 'main' into deanq/ae-1679-python-310-314-compatibility
deanq Jan 20, 2026
d4c6a81
refactor: remove dead code and add serialization tests
deanq Jan 20, 2026
194ef6d
Merge branch 'deanq/ae-1679-python-310-314-compatibility' into deanq/…
deanq Jan 20, 2026
1e3141e
fix: regenerate uv.lock with correct dependency versions
deanq Jan 20, 2026
5755eb6
fix: mark TestLoadBalancerSlsStubRouting as serial
deanq Jan 20, 2026
5ce7e19
fix: simplify parallel test execution - remove unnecessary two-pass a…
deanq Jan 20, 2026
f73ddc3
fix: re-add serial marker for TestLoadBalancerSlsStubRouting
deanq Jan 20, 2026
4e1a64e
fix: implement proper serial test handling with two-pass execution
deanq Jan 20, 2026
05d507a
fix: implement proper serial test handling with two-pass execution
deanq Jan 20, 2026
0cd8de9
chore: consistent coverage failure point
deanq Jan 20, 2026
51354ee
chore: this is about reporting coverage (no need to fail)
deanq Jan 20, 2026
cab4017
chore: don't know why it was 64
deanq Jan 22, 2026
1675d4f
chore: make test commands parallel by default with serial variants
deanq Jan 22, 2026
26067cb
Merge branch 'main' into deanq/ae-1748-run-tests-in-parallel
deanq Jan 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 39 additions & 10 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -45,20 +45,36 @@ security-scans: # Run security scans (informational)
uv run bandit -r src/ -ll -x "**/tests/**" || echo "Security scan completed with issues (informational)"

# Test commands
test: # Run all tests
test: # Run all tests in parallel (auto-detect cores)
uv run pytest tests/ -v -n auto

test-serial: # Run all tests serially (for debugging)
uv run pytest tests/ -v

test-unit: # Run unit tests only
test-unit: # Run unit tests in parallel (auto-detect cores)
uv run pytest tests/unit/ -v -n auto -m "not integration"

test-unit-serial: # Run unit tests serially (for debugging)
uv run pytest tests/unit/ -v -m "not integration"

test-integration: # Run integration tests only
test-integration: # Run integration tests in parallel (auto-detect cores)
uv run pytest tests/integration/ -v -n auto -m integration

test-integration-serial: # Run integration tests serially (for debugging)
uv run pytest tests/integration/ -v -m integration

test-coverage: # Run tests with coverage report
test-coverage: # Run tests with coverage report (parallel by default)
uv run pytest tests/ -v -n auto -m "not serial" --cov=tetra_rp --cov-report=xml
uv run pytest tests/ -v -m "serial" --cov=tetra_rp --cov-append --cov-report=term-missing

test-coverage-serial: # Run tests with coverage report (serial execution)
uv run pytest tests/ -v --cov=tetra_rp --cov-report=term-missing

test-fast: # Run tests with fast-fail mode
uv run pytest tests/ -v -x --tb=short
test-fast: # Run tests with fast-fail mode and parallel execution
uv run pytest tests/ -v -x --tb=short -n auto

test-workers: # Run tests with specific number of workers (e.g., WORKERS=4)
uv run pytest tests/ -v -n $(WORKERS)

# Linting commands
lint: # Check code with ruff
Expand All @@ -78,17 +94,30 @@ typecheck: # Check types with mypy
uv run mypy .

# Quality gates (used in CI)
quality-check: format-check lint test-coverage # Essential quality gate for CI
quality-check-strict: format-check lint typecheck test-coverage # Strict quality gate with type checking
quality-check: format-check lint test-coverage # Essential quality gate for CI (parallel by default)
quality-check-strict: format-check lint typecheck test-coverage # Strict quality gate with type checking (parallel by default)
quality-check-serial: format-check lint test-coverage-serial # Serial quality gate for debugging

# GitHub Actions specific targets
ci-quality-github: # Quality checks with GitHub Actions formatting
ci-quality-github: # Quality checks with GitHub Actions formatting (parallel by default)
@echo "::group::Code formatting check"
uv run ruff format --check .
uv run ruff format --check .
@echo "::endgroup::"
@echo "::group::Linting"
uv run ruff check . --output-format=github
@echo "::endgroup::"
@echo "::group::Test suite with coverage"
uv run pytest tests/ --junitxml=pytest-results.xml -v -n auto -m "not serial" --cov=tetra_rp --cov-report=xml --cov-fail-under=0
uv run pytest tests/ --junitxml=pytest-results.xml -v -m "serial" --cov=tetra_rp --cov-append --cov-report=term-missing
@echo "::endgroup::"

ci-quality-github-serial: # Serial quality checks for GitHub Actions (for debugging)
@echo "::group::Code formatting check"
uv run ruff format --check .
@echo "::endgroup::"
@echo "::group::Linting"
uv run ruff check . --output-format=github
@echo "::endgroup::"
@echo "::group::Test suite with coverage (serial)"
uv run pytest tests/ --junitxml=pytest-results.xml -v --cov=tetra_rp --cov-report=term-missing
@echo "::endgroup::"
4 changes: 3 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ test = [
"pytest-mock>=3.14.0",
"pytest-asyncio>=1.0.0",
"pytest-cov>=6.2.1",
"pytest-xdist>=3.6.1",
"twine>=6.1.0",
]

Expand Down Expand Up @@ -75,7 +76,8 @@ asyncio_default_fixture_loop_scope = "function"
markers = [
"unit: Unit tests",
"integration: Integration tests",
"slow: Slow tests"
"slow: Slow tests",
"serial: Tests that must run serially (not parallelized)"
]
filterwarnings = [
"ignore::DeprecationWarning",
Expand Down
105 changes: 105 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,29 @@
from tetra_rp.core.utils.singleton import SingletonMixin


def pytest_configure(config):
"""Configure pytest-xdist to respect the serial marker.

Tests marked with @pytest.mark.serial will always run on the main worker,
while unmarked tests can be distributed to other workers.
"""
# This hook is called early in pytest initialization
# xdist will check for this during test distribution


def pytest_collection_modifyitems(config, items):
"""Mark serial tests so they don't get distributed by xdist.

This ensures that tests marked with @pytest.mark.serial run on the main
worker (worker -1 or 0) and are never distributed to other workers.
"""
for item in items:
# Check if item has the serial marker
if item.get_closest_marker("serial"):
# Add xdist marker to prevent distribution
item.add_marker(pytest.mark.xdist_group(name="serial"))


@pytest.fixture
def mock_asyncio_run_coro():
"""Create a mock asyncio.run that executes coroutines."""
Expand Down Expand Up @@ -186,6 +209,68 @@ def sample_pod_template() -> Dict[str, Any]:
}


@pytest.fixture(scope="session")
def worker_temp_dir(tmp_path_factory: pytest.TempPathFactory, worker_id: str) -> Path:
"""Provide worker-specific temporary directory for file system isolation.

Each xdist worker gets its own isolated temp directory to prevent
file system conflicts when tests write to shared paths.

Args:
tmp_path_factory: Pytest's temporary path factory.
worker_id: Worker ID from pytest-xdist ('master' for single worker).

Returns:
Path to worker-specific temporary directory.
"""
if worker_id == "master":
# Single worker (non-parallel)
return tmp_path_factory.mktemp("test_data")
else:
# Parallel execution - worker-specific directory
return tmp_path_factory.mktemp(f"test_data_{worker_id}")


@pytest.fixture(scope="session")
def worker_runpod_dir(worker_temp_dir: Path) -> Path:
"""Provide worker-specific .runpod directory for state file isolation.

Args:
worker_temp_dir: Worker-specific temporary directory.

Returns:
Path to worker-specific .runpod directory.
"""
runpod_dir = worker_temp_dir / ".runpod"
runpod_dir.mkdir(parents=True, exist_ok=True)
return runpod_dir


@pytest.fixture(autouse=True)
def isolate_resource_state_file(
monkeypatch: pytest.MonkeyPatch, worker_runpod_dir: Path
) -> Path:
"""Automatically isolate RESOURCE_STATE_FILE per worker.

Patches RESOURCE_STATE_FILE and RUNPOD_FLASH_DIR to point to
worker-specific temp directory, preventing file system conflicts.

Args:
monkeypatch: Pytest's monkeypatch fixture.
worker_runpod_dir: Worker-specific .runpod directory.

Returns:
Path to worker-specific state file.
"""
from tetra_rp.core.resources import resource_manager

worker_state_file = worker_runpod_dir / "resources.pkl"
monkeypatch.setattr(resource_manager, "RESOURCE_STATE_FILE", worker_state_file)
monkeypatch.setattr(resource_manager, "RUNPOD_FLASH_DIR", worker_runpod_dir)

return worker_state_file


@pytest.fixture(autouse=True)
def reset_singletons():
"""Reset singleton instances between tests.
Expand Down Expand Up @@ -220,6 +305,17 @@ def patched_reducer_override(self, obj):
# If patching fails, continue anyway - the test might still pass
pass

# Clear module-level caches (worker-isolated due to process boundaries)
try:
from tetra_rp.stubs.live_serverless import _SERIALIZED_FUNCTION_CACHE
from tetra_rp.execute_class import _SERIALIZED_CLASS_CACHE

_SERIALIZED_FUNCTION_CACHE.clear()
_SERIALIZED_CLASS_CACHE.clear()
except (ImportError, AttributeError):
# Caches may not exist in all configurations
pass

# Reset SingletonMixin instances to clear any accumulated state
# This prevents old singleton instances from leaking into object graphs during pickling
SingletonMixin._instances = {}
Expand All @@ -242,6 +338,15 @@ def patched_reducer_override(self, obj):
yield

# Cleanup after test
try:
from tetra_rp.stubs.live_serverless import _SERIALIZED_FUNCTION_CACHE
from tetra_rp.execute_class import _SERIALIZED_CLASS_CACHE

_SERIALIZED_FUNCTION_CACHE.clear()
_SERIALIZED_CLASS_CACHE.clear()
except (ImportError, AttributeError):
pass

SingletonMixin._instances = {}

ResourceManager._resources = {}
Expand Down
1 change: 1 addition & 0 deletions tests/integration/test_remote_concurrency.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from tetra_rp.protos.remote_execution import FunctionResponse


@pytest.mark.serial
@pytest.mark.asyncio
class TestRemoteConcurrency:
"""Test concurrency behavior of @remote decorated functions."""
Expand Down
5 changes: 5 additions & 0 deletions tests/unit/test_concurrency_issues.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ async def undeploy(self) -> Dict[str, Any]:
return result


@pytest.mark.serial
class TestSingleton:
"""Test thread safety of SingletonMixin."""

Expand Down Expand Up @@ -139,6 +140,7 @@ def create_instance():
assert exception_count == 0 # No exceptions should occur


@pytest.mark.serial
class TestResourceManagerConcurrency:
"""Test ResourceManager concurrency issues."""

Expand Down Expand Up @@ -291,6 +293,7 @@ def save_resource_2():
print(f"State loading error: {e}")


@pytest.mark.serial
class TestFunctionCacheConcurrency:
"""Test global function cache thread safety."""

Expand Down Expand Up @@ -360,6 +363,7 @@ def cache_worker(worker_id: int):
assert len(_SERIALIZED_FUNCTION_CACHE) > 0


@pytest.mark.serial
class TestClassCacheConcurrency:
"""Test class serialization cache thread safety."""

Expand Down Expand Up @@ -423,6 +427,7 @@ def cache_worker(worker_id: int):
assert len(cache_operations) > 0


@pytest.mark.serial
class TestEndToEndConcurrency:
"""End-to-end tests for concurrent remote function execution."""

Expand Down
2 changes: 2 additions & 0 deletions tests/unit/test_file_locking.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ def test_platform_detection_linux(self, mock_system):
assert info["platform"] == "Linux"


@pytest.mark.serial
class TestFileLocking:
"""Test cross-platform file locking functionality."""

Expand Down Expand Up @@ -238,6 +239,7 @@ def test_file_lock_with_write_operations(self):
assert write_file.read_bytes() == b"updated data"


@pytest.mark.serial
class TestPlatformSpecificLocking:
"""Test platform-specific locking mechanisms."""

Expand Down
1 change: 1 addition & 0 deletions tests/unit/test_load_balancer_sls_stub.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ def use_requests():
assert request["dependencies"] == deps


@pytest.mark.serial
class TestLoadBalancerSlsStubRouting:
"""Test suite for routing detection between /execute and user routes."""

Expand Down
24 changes: 24 additions & 0 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading