|
| 1 | +# Testing |
| 2 | + |
| 3 | +This document describes the testing strategy for the Open Data Hub Llama Stack Distribution. |
| 4 | + |
| 5 | +## Test Scripts |
| 6 | + |
| 7 | +All test scripts live in the `tests/` directory: |
| 8 | + |
| 9 | +| File | Purpose | |
| 10 | +|------|---------| |
| 11 | +| `smoke.sh` | Smoke tests against a running Llama Stack container | |
| 12 | +| `run_integration_tests.sh` | Integration tests using upstream llama-stack's pytest suite | |
| 13 | +| `test_providers.sh` | Provider configuration tests (e.g., conditional `inline::milvus` loading) | |
| 14 | +| `test_utils.sh` | Shared utility functions (e.g., `validate_model_parameter`) | |
| 15 | + |
| 16 | +### Smoke Tests (`smoke.sh`) |
| 17 | + |
| 18 | +Smoke tests verify the container image works end-to-end. The script: |
| 19 | + |
| 20 | +1. **Starts the Llama Stack container** with environment variables for inference models, embedding models, and database configuration, then waits up to 60 seconds for the `/v1/health` endpoint to return `OK`. |
| 21 | +2. **Model listing** - Verifies each configured model appears in the `/v1/models` response. |
| 22 | +3. **OpenAI-compatible inference** - Sends a chat completion request to `/v1/chat/completions` and validates the response. |
| 23 | +4. **PostgreSQL verification** - Checks that expected database tables (`llamastack_kvstore`, `inference_store`) exist, then verifies that `inference_store` is populated with data after inference. |
| 24 | + |
| 25 | +Models tested depend on available credentials: |
| 26 | + |
| 27 | +| Model | Environment Variable | Always Tested | |
| 28 | +|-------|---------------------|---------------| |
| 29 | +| vLLM inference model (`vllm-inference/Qwen/Qwen3-0.6B`) | `VLLM_INFERENCE_MODEL` | Yes | |
| 30 | +| Embedding model (`vllm-embedding/ibm-granite/granite-embedding-125m-english`) | `EMBEDDING_MODEL` | Yes (list only) | |
| 31 | +| Vertex AI model (`vertexai/publishers/google/models/gemini-2.0-flash`) | `VERTEX_AI_PROJECT` | Only if set | |
| 32 | +| OpenAI model (`openai/gpt-4o-mini`) | `OPENAI_API_KEY` | Only if set | |
| 33 | + |
| 34 | +#### Running locally |
| 35 | + |
| 36 | +```bash |
| 37 | +# Required environment variables |
| 38 | +export VLLM_INFERENCE_MODEL="vllm-inference/Qwen/Qwen3-0.6B" |
| 39 | +export EMBEDDING_MODEL="vllm-embedding/ibm-granite/granite-embedding-125m-english" |
| 40 | +export VLLM_URL="http://localhost:8000/v1" |
| 41 | +export VLLM_EMBEDDING_URL="http://localhost:8001/v1" |
| 42 | +export IMAGE_NAME="quay.io/opendatahub/llama-stack" |
| 43 | +export IMAGE_TAG="latest" # In CI, this is set to the commit SHA or source-{sha} tag |
| 44 | + |
| 45 | +# Optional (enables additional model tests) |
| 46 | +export VERTEX_AI_PROJECT="<project>" |
| 47 | +export VERTEX_AI_LOCATION="us-central1" |
| 48 | +export OPENAI_API_KEY="<key>" |
| 49 | + |
| 50 | +./tests/smoke.sh |
| 51 | +``` |
| 52 | + |
| 53 | +### Integration Tests (`run_integration_tests.sh`) |
| 54 | + |
| 55 | +Integration tests run the upstream [llama-stack pytest suite](https://github.com/llamastack/llama-stack) against the distribution's running server. The script: |
| 56 | + |
| 57 | +1. **Extracts the llama-stack version** from the generated `distribution/Containerfile` to ensure tests match the bundled version. |
| 58 | +2. **Clones the llama-stack repository** at the matching version tag into `/tmp/llama-stack-integration-tests`. |
| 59 | +3. **Runs `pytest`** against `tests/integration/inference/` with required test dependencies installed, pointing at `distribution/config.yaml`. |
| 60 | + - `llama-stack-client` is required. |
| 61 | + - `ollama` is explicitly installed because the upstream test fixtures import it, even though this distribution does not use Ollama as a provider. |
| 62 | + |
| 63 | +Tests are run for each configured inference model (vLLM, and optionally Vertex AI and OpenAI). |
| 64 | + |
| 65 | +Some upstream tests are currently skipped, grouped by reason: |
| 66 | + |
| 67 | +**Non-streaming tests need `max_tokens` to prevent model from rambling:** |
| 68 | +- `test_text_chat_completion_non_streaming` |
| 69 | +- `test_openai_chat_completion_non_streaming` |
| 70 | + |
| 71 | +**Tool-calling tests not yet supported by our model/provider configuration:** |
| 72 | +- `test_text_chat_completion_tool_calling_tools_not_in_request` |
| 73 | +- `test_text_chat_completion_structured_output` |
| 74 | +- `test_openai_chat_completion_with_tool_choice_none` |
| 75 | +- `test_openai_chat_completion_with_tools` |
| 76 | +- `test_openai_format_preserves_complex_schemas` |
| 77 | +- `test_multiple_tools_with_different_schemas` |
| 78 | +- `test_tool_with_complex_schema` |
| 79 | +- `test_tool_without_schema` |
| 80 | + |
| 81 | +**Requires vLLM >= v0.12.0** ([llamastack/llama-stack#4984](https://github.com/llamastack/llama-stack/issues/4984)): |
| 82 | +- `test_openai_completion_guided_choice` |
| 83 | + |
| 84 | +**`granite-embedding-125m-english` was not trained with Matryoshka Representation Learning**, so vLLM correctly rejects `dimensions` requests with a 400 error: |
| 85 | +- `test_openai_embeddings_with_dimensions` |
| 86 | +- `test_openai_embeddings_with_encoding_format_base64` |
| 87 | + |
| 88 | +**Upstream schema bug** — defines `logprobs` as `bool`, should be `int` ([llamastack/llama-stack#5253](https://github.com/llamastack/llama-stack/issues/5253)): |
| 89 | +- `test_openai_completion_logprobs` |
| 90 | +- `test_openai_completion_logprobs_streaming` |
| 91 | + |
| 92 | +#### Running locally |
| 93 | + |
| 94 | +Prerequisites: |
| 95 | + |
| 96 | +- A running Llama Stack container (started by `smoke.sh` or manually) with a running vLLM inference endpoint and vLLM embedding endpoint behind it |
| 97 | +- Environment variables: |
| 98 | + - **Required**: `VLLM_INFERENCE_MODEL`, `EMBEDDING_MODEL`, `VLLM_URL`, `VLLM_EMBEDDING_URL` |
| 99 | + - **Optional**: `VERTEX_AI_PROJECT`, `VERTEX_AI_LOCATION`, and `OPENAI_API_KEY` (enables additional model coverage) |
| 100 | +- `uv` and `git` available on the system |
| 101 | + |
| 102 | +```bash |
| 103 | +./tests/run_integration_tests.sh |
| 104 | +``` |
| 105 | + |
| 106 | +### Provider Tests (`test_providers.sh`) |
| 107 | + |
| 108 | +Provider tests verify that conditional provider loading works correctly. Currently tests: |
| 109 | + |
| 110 | +- **`inline::milvus` absent by default** - Container started without `ENABLE_INLINE_MILVUS` should not load the Milvus provider |
| 111 | +- **`inline::milvus` present when enabled** - Container started with `ENABLE_INLINE_MILVUS=true` should load the Milvus provider |
| 112 | + |
| 113 | +Requires `IMAGE_NAME` and either `IMAGE_TAG` or `GITHUB_SHA` environment variables and Docker available on the system. |
| 114 | + |
| 115 | +## CI/CD Pipelines |
| 116 | + |
| 117 | +Testing is automated via GitHub Actions workflows in `.github/workflows/`. |
| 118 | + |
| 119 | +### Container Build, Test & Publish (`redhat-distro-container.yml`) |
| 120 | + |
| 121 | +The main CI pipeline that builds, tests, and publishes the container image. It runs on: |
| 122 | + |
| 123 | +- **Pull requests** to `main`, `rhoai-v*`, and `konflux-poc*` branches (when `distribution/`, `tests/`, or workflow files change) |
| 124 | +- **Pushes** to `main` and `rhoai-v*` branches |
| 125 | +- **Manual dispatch** (`workflow_dispatch`) to build from an arbitrary llama-stack commit. Intentionally skips all tests to allow building images for specific SHAs even when CI is failing on other commits. |
| 126 | +- **Nightly schedule** (6 AM UTC) to test the `main` branch of llama-stack |
| 127 | + |
| 128 | +Pipeline steps: |
| 129 | + |
| 130 | +1. **Build** the container image for AMD64 and ARM64. When MaaS (Model-as-a-Service) vLLM endpoints are configured, both architectures run the full test suite (smoke, provider, and integration tests) against remote inference endpoints. Without MaaS, ARM64 runs smoke and provider tests using local vLLM containers but skips integration tests. |
| 131 | +2. **Start vLLM inference** via the `setup-vllm` action using the pre-built `quay.io/opendatahub/vllm-cpu` image (CPU-based `Qwen3-0.6B` model) |
| 132 | +3. **Start vLLM embedding** via the `setup-vllm` action using the same pre-built image (CPU-based `granite-embedding-125m-english` model) |
| 133 | +4. **Start PostgreSQL** via the `setup-postgres` action |
| 134 | +5. **Run smoke tests** (`tests/smoke.sh`) |
| 135 | +6. **Run provider tests** (`tests/test_providers.sh`) |
| 136 | +7. **Run integration tests** (`tests/run_integration_tests.sh`) |
| 137 | +8. **Publish** multi-arch image to `quay.io/opendatahub/llama-stack` (on push to `main` or `rhoai-v*` branches when `distribution/` changed, or on manual dispatch) |
| 138 | +9. **Notify Slack** on failure or successful publish |
| 139 | + |
| 140 | +Logs from all containers (llama-stack, vLLM, PostgreSQL) and system info are uploaded as artifacts with 7-day retention. |
| 141 | + |
| 142 | +### Pre-commit (`pre-commit.yml`) |
| 143 | + |
| 144 | +Runs on all pull requests and pushes to `main`. Executes the full pre-commit hook suite and verifies no files were changed or created: |
| 145 | + |
| 146 | +- **Ruff** - Python linting and formatting |
| 147 | +- **Shellcheck** - Shell script linting |
| 148 | +- **Actionlint** - GitHub Actions workflow linting |
| 149 | +- **Standard hooks** - merge conflict detection, trailing whitespace, large file checks, YAML/JSON/TOML validation, executable shebangs, private key detection, mixed line endings |
| 150 | +- **Distribution Build** (`distribution/build.py`) - Regenerates `distribution/Containerfile` |
| 151 | +- **Distribution Documentation** (`scripts/gen_distro_docs.py`) - Regenerates `distribution/README.md` |
| 152 | + |
| 153 | +### Semantic PR Titles (`semantic-pr.yml`) |
| 154 | + |
| 155 | +Validates that pull request titles follow [Conventional Commits](https://www.conventionalcommits.org/) format (e.g., `feat:`, `fix:`, `docs:`). |
| 156 | + |
| 157 | +### Update Llama Stack Version (`update-llama-stack-version.yml`) |
| 158 | + |
| 159 | +Triggered via `repository_dispatch` (type: `update-llama-stack-version`) from the opendatahub-io/llama-stack midstream repo when a new release is tagged. The workflow: |
| 160 | + |
| 161 | +1. **Validates** the tag format (`vX.Y.Z[.W]+rhaiv.N`) and runs preflight checks (version not already set, branch doesn't exist) |
| 162 | +2. **Updates** `CURRENT_LLAMA_STACK_VERSION` in `distribution/build.py` |
| 163 | +3. **Runs pre-commit** to regenerate distribution artifacts (Containerfile, README) |
| 164 | +4. **Opens a pull request** against `main` with the version bump |
| 165 | +5. **Notifies Slack** with the PR link for review |
| 166 | + |
| 167 | +### vLLM CPU Container (`vllm-cpu-container.yml`) |
| 168 | + |
| 169 | +Builds, tests, and publishes pre-built vLLM CPU container images to `quay.io/opendatahub/vllm-cpu`. These images bundle inference and embedding models so the main CI pipeline doesn't need to download them each run. It runs on: |
| 170 | + |
| 171 | +- **Pull requests** to `main`/`rhoai-v*`/`konflux-poc*` branches and **pushes** to `main`/`rhoai-v*` branches (when `vllm/Containerfile` or actions change) |
| 172 | +- **Manual dispatch** with optional custom inference/embedding model parameters |
| 173 | + |
| 174 | +### Test PR in Showroom (`test-pr-in-showroom.yml`) |
| 175 | + |
| 176 | +Manually triggered workflow that builds and tests a PR's container image in an OpenShift showroom environment. Takes a PR number as input and optionally custom OLM catalog and operator images. Builds the image from the PR code, pushes it to an OpenShift internal registry, and runs the full showroom setup/test/cleanup cycle. |
| 177 | + |
| 178 | +### Stale Bot (`stale_bot.yml`) |
| 179 | + |
| 180 | +Automatically marks issues and PRs as stale after 60 days of inactivity and closes them after 30 more days. Runs daily at midnight UTC. |
0 commit comments