Skip to content

Commit 5ded4b4

Browse files
authored
chore: CI/testing improvements for parallel test execution (#317)
* chore: CI/testing improvements for parallel test execution - Add workflows-dev pytest command with parallel test runner - Add pytest-xdist for parallel test execution (-nauto) - Add timeouts to prevent test hangs (--timeout=10/60/120) - Use module-scoped async fixtures for faster tests - Remove mypy in favor of basedpyright/ty type checking - Add test-docker CI job for integration tests with testcontainers - Exclude integration tests from Python 3.9/3.14 in CI matrix
1 parent 73c1254 commit 5ded4b4

36 files changed

Lines changed: 2549 additions & 555 deletions

File tree

.github/workflows/publish_openapi.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ jobs:
3939
- name: Compute release metadata
4040
id: metadata
4141
run: >
42-
uv run --package workflows-dev workflows-dev compute-tag-metadata
42+
uv run dev compute-tag-metadata
4343
--tag "${{ env.TARGET_TAG }}"
4444
--output "$GITHUB_OUTPUT"
4545

.github/workflows/test.yml

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,29 +21,35 @@ jobs:
2121
[
2222
llama-index-workflows,
2323
llama-index-utils-workflow,
24-
workflows-dev,
2524
llama-index-integration-tests,
25+
llama-agents-dev,
2626
]
2727
exclude:
28-
- package: workflows-dev
28+
# Integration tests on 3.14 run in test-docker job with cross-package coverage
29+
- package: llama-index-integration-tests
30+
python-version: "3.9"
31+
- package: llama-index-integration-tests
32+
python-version: "3.14"
33+
# Dev CLI only needs to run on 3.14
34+
- package: llama-agents-dev
2935
python-version: "3.9"
30-
- package: workflows-dev
36+
- package: llama-agents-dev
3137
python-version: "3.10"
32-
- package: workflows-dev
38+
- package: llama-agents-dev
3339
python-version: "3.11"
34-
- package: workflows-dev
40+
- package: llama-agents-dev
3541
python-version: "3.12"
36-
- package: workflows-dev
42+
- package: llama-agents-dev
3743
python-version: "3.13"
3844
include:
3945
- package: llama-index-workflows
4046
package_dir: packages/llama-index-workflows
4147
- package: llama-index-utils-workflow
4248
package_dir: packages/llama-index-utils-workflow
43-
- package: workflows-dev
44-
package_dir: packages/workflows-dev
4549
- package: llama-index-integration-tests
4650
package_dir: packages/llama-index-integration-tests
51+
- package: llama-agents-dev
52+
package_dir: .
4753

4854
steps:
4955
- uses: actions/checkout@v4
@@ -58,7 +64,9 @@ jobs:
5864

5965
- name: Run tests
6066
if: matrix.python-version != env.COV_PYTHON_VERSION
61-
run: uv run --all-extras --directory ${{ matrix.package_dir }} -- pytest
67+
run: |
68+
uv sync --python ${{ matrix.python-version }} --all-extras --directory ${{ matrix.package_dir }}
69+
uv run --python ${{ matrix.python-version }} --all-extras --directory ${{ matrix.package_dir }} -- pytest
6270
6371
- name: Run tests with coverage
6472
if: matrix.python-version == env.COV_PYTHON_VERSION
@@ -74,8 +82,37 @@ jobs:
7482
env:
7583
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
7684

85+
test-docker:
86+
runs-on: ubuntu-latest
87+
steps:
88+
- uses: actions/checkout@v4
89+
with:
90+
fetch-depth: 0
91+
92+
- name: Install uv and set the python version
93+
uses: astral-sh/setup-uv@v6
94+
with:
95+
python-version: "3.14"
96+
enable-cache: true
97+
98+
- name: Run non-Docker tests with coverage
99+
run: uv run --all-extras --directory packages/llama-index-integration-tests -- pytest -m "not docker" --cov --cov-report=
100+
101+
- name: Run Docker tests with coverage (append)
102+
# Use longer timeout for Docker tests since container startup can take 30-60s in CI
103+
run: uv run --all-extras --directory packages/llama-index-integration-tests -- pytest --timeout=120 -m docker --cov --cov-append --cov-report=xml
104+
105+
- name: Report Coveralls
106+
uses: coverallsapp/github-action@v2
107+
with:
108+
file: packages/llama-index-integration-tests/coverage.xml
109+
flag-name: llama-index-integration-tests
110+
parallel: true
111+
env:
112+
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
113+
77114
coveralls-finish:
78-
needs: test
115+
needs: [test, test-docker]
79116
runs-on: ubuntu-latest
80117
if: github.repository == 'run-llama/workflows-py'
81118
steps:

.github/workflows/update_debugger_assets.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ jobs:
4242
4343
- name: Update index.html
4444
run: >
45-
uv run --package workflows-dev workflows-dev update-index-html
45+
uv run dev update-index-html
4646
--js-url "${{ steps.payload.outputs.js_url }}"
4747
--css-url "${{ steps.payload.outputs.css_url }}"
4848

.pre-commit-config.yaml

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -28,32 +28,8 @@ repos:
2828
- id: ty
2929
name: ty
3030
language: system
31-
entry: uv run ty check packages
31+
entry: uv run ty check packages/
3232
pass_filenames: false
33-
- repo: https://github.com/pre-commit/mirrors-mypy
34-
rev: v1.15.0
35-
hooks:
36-
- id: mypy
37-
exclude: ^packages/(llama-index-workflows-server|llama-index-workflows-client|llama-index-utils-workflow|workflows-dev|llama-index-integration-tests)/tests/|^packages/llama-index-workflows/src/llama_agents/|^packages/llama-index-workflows/tests/test_llama_agents_alias\.py
38-
additional_dependencies:
39-
[
40-
"types-Deprecated",
41-
"types-PyYAML",
42-
"types-botocore",
43-
"types-aiobotocore",
44-
"types-protobuf==4.24.0.4",
45-
"types-redis",
46-
"types-requests",
47-
"types-setuptools",
48-
"types-click",
49-
]
50-
args:
51-
[
52-
--disallow-untyped-defs,
53-
--ignore-missing-imports,
54-
--python-version=3.11,
55-
]
56-
5733
- repo: https://github.com/DetachHead/basedpyright-pre-commit-mirror
5834
rev: 1.31.1
5935
hooks:

AGENTS.md

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,24 @@ This is the LlamaIndex Workflows library - an event-driven, async-first framewor
1313
## Development Commands
1414

1515
### Testing
16-
```bash
17-
# Run all tests
18-
uv run --directory packages/llama-index-workflows pytest
1916

20-
# Run tests with coverage
21-
uv run --directory packages/llama-index-workflows pytest --cov=src/workflows --cov-report=html
17+
Use the `dev` CLI to run tests:
18+
19+
```bash
20+
# Run all package tests
21+
uv run dev
2222

23-
# Run specific test files
24-
uv run --directory packages/llama-index-workflows pytest tests/test_server.py tests/test_server_utils.py
23+
# Filter by substring match
24+
uv run dev -p workflows
25+
uv run dev -p server -p client
2526

26-
# Run tests in verbose mode
27-
uv run --directory packages/llama-index-workflows pytest -v
27+
# Pass pytest args after --
28+
uv run dev -- -k test_name
2829
```
2930

3031
### Linting & Formatting
3132
```bash
32-
# Run pre-commit hooks
33-
uv run --directory packages/llama-index-workflows pre-commit run -a
33+
uv run pre-commit run -a
3434
```
3535

3636
## Project Structure
@@ -46,7 +46,7 @@ uv run --directory packages/llama-index-workflows pre-commit run -a
4646
- **WorkflowServer** - HTTP server for serving workflows as web services
4747

4848
## Notes for Claude
49-
- Always run tests after making changes: `uv run --directory packages/llama-index-workflows pytest`
49+
- Always run tests after making changes: `uv run dev`
5050
- Never use classes for tests, only use pytest functions
5151
- Always annotate with types function arguments and return values
5252
- The project uses async/await extensively
@@ -62,17 +62,11 @@ Make sure to install uv as the package manager. Development commands rely on it.
6262
curl -fsSL https://astral.sh/uv/install.sh | sh
6363
```
6464

65-
Always run test tests and pre-commit commands before committing. They run very fast and are not verbose.
66-
67-
Tests:
65+
Always run tests and pre-commit before committing:
6866

6967
```bash
70-
uv run --directory packages/llama-index-workflows pytest -nauto --timeout=1
71-
```
72-
73-
Linting, typechecking, and formatting:
74-
```bash
75-
uv run --directory packages/llama-index-workflows pre-commit run -a
68+
uv run dev
69+
uv run pre-commit run -a
7670
```
7771

7872
## Testing Patterns
@@ -84,3 +78,10 @@ We use **pytest** with idiomatic pytest patterns. Follow these guidelines:
8478
- **Prefer Real Objects Over Mocks**: Use simple dataclasses and real objects directly when available rather than mocking them. Only mock external dependencies or things that are truly difficult to instantiate.
8579
- **DRY Test Setup**: Do not repeat patches or setup code. Create reusable abstractions—fixtures, helper functions, or module-level constants—that can be shared across tests. Tests can easily be overwhelmed with setup; start from a rich suite of testing utilities to enable many small, expressive tests.
8680
- **Simple Testing Utilities**: Testing utilities should be basic—just functions, fixtures, and global variables. Avoid over-engineering test infrastructure.
81+
82+
## Coding Style
83+
84+
- Always use `from __future__ import annotations` at the top of each test file. Never use string annotations.
85+
- Include the standard SPDX license header at the top of each test file.
86+
- Comments are useful, but avoid fluff.
87+
- Never use inline imports unless required to prevent circular dependencies.

CONTRIBUTING.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,18 @@ uv run pre-commit install
4747

4848
## Run tests
4949

50-
Tests are run with `pytest`. You can run them with:
50+
Use the `dev` CLI to run tests across packages:
5151

5252
```bash
53-
uv run pytest
53+
# Run all package tests
54+
uv run dev
55+
56+
# Filter by substring match
57+
uv run dev -p workflows
58+
uv run dev -p server -p client
59+
60+
# Pass pytest args after --
61+
uv run dev -- -k test_name
5462
```
5563

5664
Generally, all features should be covered by robust tests. If you are adding a new feature or fixing a bug, please add tests for it.

packages/llama-index-integration-tests/pyproject.toml

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,16 @@ build-backend = "uv_build"
44

55
[dependency-groups]
66
dev = [
7+
"basedpyright>=1.31.1",
8+
"psycopg[binary]>=3.2.0",
79
"pytest>=8.4.2",
810
"pytest-asyncio>=0.24.0",
911
"pytest-cov>=7.0.0",
10-
"pytest-timeout>=2.3.1"
12+
"pytest-timeout>=2.3.1",
13+
"pytest-xdist>=3.8.0",
14+
"sqlalchemy>=2.0.0",
15+
"testcontainers[postgres]>=4.0.0",
16+
"ty>=0.0.1,<0.0.9"
1117
]
1218

1319
[project]
@@ -21,10 +27,31 @@ dependencies = [
2127
"llama-index-workflows"
2228
]
2329

30+
[tool.basedpyright]
31+
typeCheckingMode = "standard"
32+
pythonVersion = "3.10"
33+
34+
[tool.coverage.run]
35+
source = [
36+
"workflows",
37+
"llama_agents"
38+
]
39+
omit = ["**/tests/*"]
40+
2441
[tool.pytest.ini_options]
2542
asyncio_mode = "auto"
26-
asyncio_default_fixture_loop_scope = "function"
43+
asyncio_default_fixture_loop_scope = "module"
44+
asyncio_default_test_loop_scope = "module"
2745
testpaths = ["tests"]
46+
# Skip docker tests by default - run with `pytest -m docker` to include them
47+
addopts = "-nauto --timeout=60 -m 'not docker'"
48+
markers = [
49+
"docker: marks tests as requiring Docker (testcontainers/PostgreSQL)"
50+
]
51+
filterwarnings = [
52+
# Ignore internal testcontainers deprecation warning (fixed in their code, just noisy)
53+
"ignore:The @wait_container_is_ready decorator is deprecated:DeprecationWarning"
54+
]
2855

2956
[tool.uv.sources]
3057
llama-index-workflows = {workspace = true}

packages/llama-index-integration-tests/tests/conftest.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from __future__ import annotations
44

5-
from typing import Any, Callable, List, Protocol, Union
5+
from typing import Any, Callable, Generator, List, Protocol, Union
66

77
import pytest
88
from llama_index.core.agent.workflow import (
@@ -17,6 +17,8 @@
1717
make_text_response,
1818
response_generator_from_list,
1919
)
20+
from sqlalchemy.engine import Engine
21+
from testcontainers.postgres import PostgresContainer
2022

2123

2224
class WorkflowFactory(Protocol):
@@ -132,3 +134,39 @@ def _create(
132134
)
133135

134136
return _create # type: ignore[return-value]
137+
138+
139+
# -- Docker/PostgreSQL Fixtures --
140+
141+
142+
@pytest.fixture(scope="module")
143+
def postgres_container() -> Generator[PostgresContainer, None, None]:
144+
"""Module-scoped PostgreSQL container for integration tests.
145+
146+
Requires Docker to be running. Used by tests marked with @pytest.mark.docker.
147+
"""
148+
with PostgresContainer("postgres:16", driver=None) as postgres:
149+
yield postgres
150+
151+
152+
@pytest.fixture(scope="module")
153+
def postgres_engine(
154+
postgres_container: PostgresContainer,
155+
) -> Generator[Engine, None, None]:
156+
"""Module-scoped PostgreSQL engine for integration tests."""
157+
from sqlalchemy import create_engine
158+
159+
# Get connection URL and convert to use psycopg (psycopg3) driver
160+
connection_url = postgres_container.get_connection_url()
161+
# Replace postgresql:// or postgresql+psycopg2:// with postgresql+psycopg://
162+
if "postgresql+psycopg2://" in connection_url:
163+
connection_url = connection_url.replace(
164+
"postgresql+psycopg2://", "postgresql+psycopg://"
165+
)
166+
elif connection_url.startswith("postgresql://"):
167+
connection_url = connection_url.replace(
168+
"postgresql://", "postgresql+psycopg://", 1
169+
)
170+
engine = create_engine(connection_url)
171+
yield engine
172+
engine.dispose()
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# SPDX-License-Identifier: MIT
2+
# Copyright (c) 2026 LlamaIndex Inc.
3+
4+
"""Basic Docker/PostgreSQL connectivity test."""
5+
6+
from __future__ import annotations
7+
8+
import pytest
9+
from sqlalchemy import text
10+
from sqlalchemy.engine import Engine
11+
12+
13+
@pytest.mark.docker
14+
def test_postgres_connection(postgres_engine: Engine) -> None:
15+
"""Test basic PostgreSQL connectivity with SELECT 1."""
16+
with postgres_engine.connect() as conn:
17+
result = conn.execute(text("SELECT 1"))
18+
assert result.scalar() == 1

packages/llama-index-utils-workflow/pyproject.toml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ dev = [
77
"pre-commit>=4.3.0",
88
"pytest>=8.4.2",
99
"pytest-asyncio>=1.0.0",
10-
"pytest-cov>=6.1.1"
10+
"pytest-cov>=6.1.1",
11+
"pytest-timeout>=2.4.0",
12+
"pytest-xdist>=3.8.0"
1113
]
1214

1315
[project]
@@ -23,5 +25,12 @@ dependencies = [
2325
"pyvis>=0.3.2"
2426
]
2527

28+
[tool.pytest.ini_options]
29+
asyncio_mode = "auto"
30+
asyncio_default_fixture_loop_scope = "module"
31+
asyncio_default_test_loop_scope = "module"
32+
testpaths = ["tests"]
33+
addopts = "-nauto --timeout=10"
34+
2635
[tool.uv.build-backend]
2736
module-name = "llama_index.utils.workflow"

0 commit comments

Comments
 (0)