Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
58 changes: 58 additions & 0 deletions .github/workflows/integration-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Integration Tests for Exosphere
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Set minimal permissions for the workflow (defense-in-depth)

Not required but recommended.

 name: Integration Tests for Exosphere
+permissions:
+  contents: read
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
name: Integration Tests for Exosphere
name: Integration Tests for Exosphere
permissions:
contents: read
🤖 Prompt for AI Agents
.github/workflows/integration-tests.yml lines 1-1: the workflow currently has no
explicit permissions declared; add a minimal permissions block scoped to only
the token scopes the job requires (e.g., read-only for contents, packages,
pull-requests, or write only if the workflow needs to create status/comments)
instead of leaving defaults. Update the workflow YAML to include a top-level
permissions mapping with the least-privilege scopes required by the integration
tests and narrow any job-level or step-level permissions further if possible.


on:
push:
branches: [main]
paths:
- 'integration-tests/**'
- 'state-manager/**'
- 'python-sdk/**'
- '.github/workflows/integration-tests.yml'

Comment on lines +3 to +11
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Quote “on” and fix indentation to satisfy yamllint and avoid parse surprises

YAML treats on as a boolean unless quoted. Also several blocks are indented inconsistently.

-name: Integration Tests for Exosphere
-
-on:
-    push:
-        branches: [main]
-        paths:
-          - 'integration-tests/**'
-          - 'state-manager/**'
-          - 'python-sdk/**'
-          - '.github/workflows/integration-tests.yml'
+name: Integration Tests for Exosphere
+
+'on':
+  push:
+    branches: [main]
+    paths:
+      - integration-tests/**
+      - state-manager/**
+      - python-sdk/**
+      - .github/workflows/integration-tests.yml
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
on:
push:
branches: [main]
paths:
- 'integration-tests/**'
- 'state-manager/**'
- 'python-sdk/**'
- '.github/workflows/integration-tests.yml'
name: Integration Tests for Exosphere
'on':
push:
branches: [main]
paths:
- integration-tests/**
- state-manager/**
- python-sdk/**
- .github/workflows/integration-tests.yml
🧰 Tools
🪛 YAMLlint (1.37.1)

[warning] 3-3: truthy value should be one of [false, true]

(truthy)


[error] 7-7: wrong indentation: expected 12 but found 10

(indentation)

🤖 Prompt for AI Agents
.github/workflows/integration-tests.yml lines 3-11: the YAML key on must be
quoted and the nested blocks need consistent indentation to satisfy yamllint and
avoid parsing surprises; update the file to quote "on" (e.g. "on":) and
re-indent the following block using a consistent 2-space indentation for push,
branches, paths and each path item so sequences and mappings align properly.

jobs:
test:
runs-on: ubuntu-latest
services:
mongodb:
image: mongo:7
ports:
- 27017:27017
options: >-
Comment on lines +12 to +20
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Tighten job config: timeout, consistent indentation, and uv install groups

  • Add a timeout to avoid hung CI.
  • Use uv sync --all-groups (or plain uv sync) because dependency-groups.dev is empty; --group dev may exclude core deps depending on uv config.
  • Fix indentation to two spaces consistently.
 jobs:
-    test:
-        runs-on: ubuntu-latest
-        services:
-            mongodb:
-              image: mongo:7
-              ports:
-                - 27017:27017
+  test:
+    runs-on: ubuntu-latest
+    timeout-minutes: 20
+    services:
+      mongodb:
+        image: mongo:7
+        ports:
+          - 27017:27017
         ...
-        steps:
-        - name: Checkout code
-          uses: actions/checkout@v4
+    steps:
+      - name: Checkout code
+        uses: actions/checkout@v4
         ...
-      - name: Install dev dependencies with uv
+      - name: Install dependencies with uv
         working-directory: integration-tests
         run: |
-            uv sync --group dev
+          uv sync --all-groups

If you intend to install only the default group (not dev), use just uv sync. Please confirm your intended uv behavior.

Also applies to: 30-58


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== First 100 lines of .github/workflows/integration-tests.yml =="
sed -n '1,100p' .github/workflows/integration-tests.yml

echo
echo "== uv sync occurrences =="
grep -n "uv sync" .github/workflows/integration-tests.yml || true

Length of output: 2064


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== dependency-groups occurrences =="
grep -R "dependency-groups" -n . || true

echo
echo "== pyproject.toml files =="
find . -type f | grep pyproject.toml || echo "none"

echo
echo "== setup.cfg for dependency-groups =="
grep -R "dependency_groups" -n . || true

Length of output: 734


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== List of workflow files =="
ls .github/workflows

for f in .github/workflows/*.yml; do
  echo
  echo "== $f first 5 lines =="
  sed -n '1,5p' "$f"
done

echo
echo "== integration-tests pyproject.toml [dependency-groups] section =="
grep -n '^\[dependency-groups\]' -n integration-tests/pyproject.toml
sed -n '1,50p' integration-tests/pyproject.toml

Length of output: 6290


Tighten integration-tests workflow: add timeout, fix indentation, and adjust uv invocation

  • Under the test job, add
    timeout-minutes: 20
    directly beneath runs-on: ubuntu-latest to prevent CI hangs.
  • Reformat .github/workflows/integration-tests.yml to use 2-space indentation consistently.
  • In the “Install dependencies with uv” step, change
    uv sync --group dev
    to
    uv sync --all-groups
    (or simply uv sync if you only intend to install the default group), since [dependency-groups].dev = [] in integration-tests/pyproject.toml. Confirm which groups you mean to install.
🧰 Tools
🪛 YAMLlint (1.37.1)

[error] 17-17: wrong indentation: expected 16 but found 14

(indentation)


[error] 19-19: wrong indentation: expected 18 but found 16

(indentation)

🤖 Prompt for AI Agents
.github/workflows/integration-tests.yml lines 12-20: the workflow needs a job
timeout, consistent 2-space indentation, and the uv invocation adjusted; add
"timeout-minutes: 20" directly beneath "runs-on: ubuntu-latest", reformat the
surrounding YAML to use 2-space indentation everywhere in this file, and in the
"Install dependencies with uv" step replace "uv sync --group dev" with "uv sync
--all-groups" (or "uv sync" if you intend only the default group) — verify which
dependency groups you want installed and update the command accordingly.

--health-cmd "mongosh --eval 'db.runCommand(\"ping\")'"
--health-interval 10s
--health-timeout 5s
--health-retries 5
Comment on lines +21 to +24
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix MongoDB healthcheck: wrong command and missing auth cause unhealthy service

db.runCommand("ping") is invalid and the check doesn’t authenticate against the root user you create, so the service may never become healthy.

Apply:

-              options: >-
-                --health-cmd "mongosh --eval 'db.runCommand(\"ping\")'"
+              options: >-
+                --health-cmd "mongosh --username admin --password password --authenticationDatabase admin --quiet --eval 'db.adminCommand({ ping: 1 }).ok'"
                 --health-interval 10s
                 --health-timeout 5s
                 --health-retries 5
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
--health-cmd "mongosh --eval 'db.runCommand(\"ping\")'"
--health-interval 10s
--health-timeout 5s
--health-retries 5
options: >-
--health-cmd "mongosh --username admin --password password --authenticationDatabase admin --quiet --eval 'db.adminCommand({ ping: 1 }).ok'"
--health-interval 10s
--health-timeout 5s
--health-retries 5
🤖 Prompt for AI Agents
.github/workflows/integration-tests.yml around lines 21-24: the healthcheck uses
an invalid db.runCommand("ping") and doesn’t authenticate the root user, so
container stays unhealthy; change the command to authenticate against the admin
user and call adminCommand({ping:1}) instead, e.g. invoke mongosh with the root
username/password from the environment (MONGO_INITDB_ROOT_USERNAME /
MONGO_INITDB_ROOT_PASSWORD) and --authenticationDatabase admin and run --eval
'db.adminCommand({ping:1})' so the healthcheck can succeed.

env:
MONGO_INITDB_ROOT_USERNAME: admin
MONGO_INITDB_ROOT_PASSWORD: password
MONGO_INITDB_DATABASE: integration_tests

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'

- name: Install uv
uses: astral-sh/setup-uv@v2
with:
cache: true

- name: Install dev dependencies with uv
working-directory: integration-tests
run: |
uv sync --group dev

- name: Run integration tests
working-directory: integration-tests
env:
MONGO_URI: mongodb://admin:password@localhost:27017
MONGO_DATABASE_NAME: integration_tests
STATE_MANAGER_SECRET: test-secret-key
EXOSPHERE_API_KEY: test-secret-key
SECRETS_ENCRYPTION_KEY: YTzpUlBGLSwm-3yKJRJTZnb0_aQuQQHyz64s8qAERVU=
run: |
Comment on lines +52 to +57
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Do not commit secrets to the repo; use GitHub Secrets and add authSource to URI

Hard-coded API keys and encryption keys will be leaked to forks and logs. Also add authSource=admin to the URI since you create the root user in admin.

-            MONGO_URI: mongodb://admin:password@localhost:27017
+            MONGO_URI: mongodb://admin:password@127.0.0.1:27017/?authSource=admin
             MONGO_DATABASE_NAME: integration_tests
-            STATE_MANAGER_SECRET: test-secret-key
-            EXOSPHERE_API_KEY: test-secret-key
-            SECRETS_ENCRYPTION_KEY: YTzpUlBGLSwm-3yKJRJTZnb0_aQuQQHyz64s8qAERVU=
+            STATE_MANAGER_SECRET: ${{ secrets.STATE_MANAGER_SECRET }}
+            EXOSPHERE_API_KEY: ${{ secrets.EXOSPHERE_API_KEY }}
+            SECRETS_ENCRYPTION_KEY: ${{ secrets.SECRETS_ENCRYPTION_KEY }}

If you intentionally use throwaway defaults for local runs, write them to a .env during CI instead of committing them:

         - name: Run integration tests
           working-directory: integration-tests
           env:
-            MONGO_URI: mongodb://admin:password@127.0.0.1:27017/?authSource=admin
-            MONGO_DATABASE_NAME: integration_tests
-            STATE_MANAGER_SECRET: ${{ secrets.STATE_MANAGER_SECRET }}
-            EXOSPHERE_API_KEY: ${{ secrets.EXOSPHERE_API_KEY }}
-            SECRETS_ENCRYPTION_KEY: ${{ secrets.SECRETS_ENCRYPTION_KEY }}
+            MONGO_URI: mongodb://admin:password@127.0.0.1:27017/?authSource=admin
+            MONGO_DATABASE_NAME: integration_tests
+            STATE_MANAGER_SECRET: ${{ secrets.STATE_MANAGER_SECRET }}
+            EXOSPHERE_API_KEY: ${{ secrets.EXOSPHERE_API_KEY }}
+            SECRETS_ENCRYPTION_KEY: ${{ secrets.SECRETS_ENCRYPTION_KEY }}
           run: |
-            uv run pytest
+            : "${STATE_MANAGER_SECRET:=test-secret-key}"
+            : "${EXOSPHERE_API_KEY:=test-secret-key}"
+            : "${SECRETS_ENCRYPTION_KEY:=insecure-test-key}"
+            uv run pytest
🧰 Tools
🪛 Gitleaks (8.27.2)

56-56: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🪛 Checkov (3.2.334)

[MEDIUM] 52-53: Basic Auth Credentials

(CKV_SECRET_4)


[LOW] 56-57: Base64 High Entropy String

(CKV_SECRET_6)

🪛 YAMLlint (1.37.1)

[error] 52-52: wrong indentation: expected 14 but found 12

(indentation)

uv run pytest
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Add trailing newline

Satisfies linters and keeps diffs cleaner.

-            uv run pytest
+            uv run pytest
+
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
uv run pytest
uv run pytest
🧰 Tools
🪛 YAMLlint (1.37.1)

[error] 58-58: no new line character at the end of file

(new-line-at-end-of-file)

🤖 Prompt for AI Agents
.github/workflows/integration-tests.yml around line 58: the file is missing a
trailing newline after the final line ("uv run pytest"), which fails linters and
makes diffs noisy; open the file and add a single newline character at the end
of the file (ensure the last line ends with '\n'), save and commit the change.

2 changes: 0 additions & 2 deletions .github/workflows/publish-state-mangaer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ on:
branches: [main]
paths:
- 'state-manager/**'
release:
types: [published]
workflow_dispatch:

env:
Expand Down
66 changes: 66 additions & 0 deletions integration-tests/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg

# Virtual Environment
venv/
env/
ENV/
.env
.venv/

# IDE
.vscode/
*.swp
*.swo
.idea/
*.iws
*.iml
*.ipr


# Local development
.env.local
.env.development.local
.env.test.local
.env.production.local

# Database
*.db
*.sqlite3

# OS generated files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db

#logs
*.log
logs/*.*
!logs/.gitkeep

# local files
files/
!files/.gitkeep
1 change: 1 addition & 0 deletions integration-tests/.python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.12
207 changes: 207 additions & 0 deletions integration-tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
# Integration Testing with Uvicorn Server

This directory contains examples of how to properly run and stop uvicorn servers for integration testing. Unlike the problematic approach of using `uvicorn.run()` (which blocks forever), these examples show you how to create real HTTP endpoints that you can send requests to.

## 🚨 The Problem with `uvicorn.run()`

The original code had this issue:

```python
@pytest.mark.asyncio
async def test_basic():
uvicorn.run(app, host="127.0.0.1", port=8000) # ❌ This blocks forever!
async with ClientSession() as session: # ❌ This never executes
# ... test code that never runs
```

**Problem**: `uvicorn.run()` is a blocking call that never returns, so your test code after it never executes.

## ✅ Proper Solutions

### 1. UvicornTestServer Class (Recommended)

The `UvicornTestServer` class in `test_basic.py` provides a clean way to start and stop uvicorn servers:

```python
from test_basic import UvicornTestServer

# Create and start server
server = UvicornTestServer(app, host="127.0.0.1", port=8000)
server.start()

# Make HTTP requests
async with ClientSession() as session:
async with session.get(f"{server.base_url}/health") as response:
assert response.status == 200

# Stop server
server.stop()
```
Comment on lines +23 to +39
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

File references don’t match the repo (class now lives in conftest.py)

You reference UvicornTestServer in test_basic.py, but the implementation is in integration-tests/conftest.py. Update paths to avoid confusing contributors.

-from test_basic import UvicornTestServer
+from conftest import UvicornTestServer
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
The `UvicornTestServer` class in `test_basic.py` provides a clean way to start and stop uvicorn servers:
```python
from test_basic import UvicornTestServer
# Create and start server
server = UvicornTestServer(app, host="127.0.0.1", port=8000)
server.start()
# Make HTTP requests
async with ClientSession() as session:
async with session.get(f"{server.base_url}/health") as response:
assert response.status == 200
# Stop server
server.stop()
```
from conftest import UvicornTestServer
# Create and start server
server = UvicornTestServer(app, host="127.0.0.1", port=8000)
server.start()
# Make HTTP requests
async with ClientSession() as session:
async with session.get(f"{server.base_url}/health") as response:
assert response.status == 200
# Stop server
server.stop()
🧰 Tools
🪛 LanguageTool

[grammar] ~23-~23: Use correct spacing
Context: ...n way to start and stop uvicorn servers: python from test_basic import UvicornTestServer # Create and start server server = UvicornTestServer(app, host="127.0.0.1", port=8000) server.start() # Make HTTP requests async with ClientSession() as session: async with session.get(f"{server.base_url}/health") as response: assert response.status == 200 # Stop server server.stop() Features: - ✅ Automatic port detection...

(QB_NEW_EN_OTHER_ERROR_IDS_5)

🤖 Prompt for AI Agents
In integration-tests/README.md around lines 23 to 39, the README references
UvicornTestServer as if it lives in test_basic.py but the class actually resides
in integration-tests/conftest.py; update the README text and example to point to
integration-tests/conftest.py (or show importing/using it from conftest) and
adjust any import path or usage example so contributors are directed to the
correct file.


**Features:**
- ✅ Automatic port detection (avoids conflicts)
- ✅ Proper startup/shutdown lifecycle
- ✅ Thread-based server execution
- ✅ Waits for server to be ready
- ✅ Graceful cleanup

### 2. Pytest Fixtures

Use pytest fixtures for automatic server management:

```python
@pytest.fixture(scope="session")
def running_server():
"""Server shared across all tests in the session."""
server = UvicornTestServer(app)
server.start()
yield server
server.stop()

@pytest.fixture(scope="function")
def fresh_server():
"""Fresh server for each test."""
server = UvicornTestServer(app)
server.start()
yield server
server.stop()
```

### 3. Manual Server Management

For full control over server lifecycle:

```python
@pytest.mark.asyncio
async def test_manual_server():
server = UvicornTestServer(app)

try:
server.start()
# Your test code here
async with ClientSession() as session:
async with session.get(f"{server.base_url}/health") as response:
assert response.status == 200
finally:
server.stop() # Always cleanup
```

## 🏃‍♂️ Running the Examples

### Install Dependencies

```bash
cd integration-tests
uv sync
```

### Run Tests

```bash
# Run all tests
uv run python -m pytest test_basic.py -v

# Run specific test
uv run python -m pytest test_basic.py::test_basic_with_session_server -v -s

# Run with output
uv run python -m pytest test_basic.py -v -s
```

### Run Demo Server

```bash
# Start a server you can send requests to
uv run python demo_server.py
```

Then in another terminal:
```bash
curl http://127.0.0.1:8000/health
curl http://127.0.0.1:8000/test
```

## 📁 File Overview

- **`test_basic.py`** - Main test file with UvicornTestServer class and examples
- **`test_server_examples.py`** - Comprehensive examples of different testing approaches
- **`demo_server.py`** - Simple script to run a server manually
- **`pyproject.toml`** - Project dependencies
Comment thread
NiveditJain marked this conversation as resolved.

Comment on lines +124 to +130
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Remove/adjust file overview entries that aren’t shipped in this PR

If test_basic.py, test_server_examples.py, and demo_server.py aren’t present, drop or gate them behind a future work note.

🧰 Tools
🪛 LanguageTool

[grammar] ~124-~124: There might be a problem here.
Context: ...0.0.1:8000/test ``` ## 📁 File Overview - test_basic.py - Main test file with UvicornTestServer ...

(QB_NEW_EN_MERGED_MATCH)


[grammar] ~126-~126: Use correct spacing
Context: ...ith UvicornTestServer class and examples - test_server_examples.py - Comprehensive examples of different te...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~127-~127: Use correct spacing
Context: ...examples of different testing approaches - demo_server.py - Simple script to run a server manually...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~128-~128: Use correct spacing
Context: ...- Simple script to run a server manually - pyproject.toml - Project dependencies ## 🎯 When to Us...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~129-~129: Use correct spacing
Context: ...pyproject.toml`** - Project dependencies ## 🎯 When to Use Each Approach ### Sessio...

(QB_NEW_EN_OTHER_ERROR_IDS_5)

🤖 Prompt for AI Agents
In integration-tests/README.md around lines 124 to 130, the file overview lists
test_basic.py, test_server_examples.py, and demo_server.py which are not
included in this PR; either remove those entries or change them to a “future
work / not shipped in this PR” note and, if appropriate, only document files
that are actually present in the repo for this PR; update the list so it
accurately reflects shipped files or clearly indicates missing examples are
planned for a later change.

## 🎯 When to Use Each Approach

### Session-Scoped Server (`running_server` fixture)
- ✅ Fast test execution (server starts once)
- ✅ Good for multiple tests that don't interfere
- ❌ Tests share state
- **Use for**: Most integration tests

### Function-Scoped Server (`fresh_server` fixture)
- ✅ Complete isolation between tests
- ✅ Clean state for each test
- ❌ Slower (starts server for each test)
- **Use for**: Tests that modify server state

### Manual Server Management
- ✅ Full control over lifecycle
- ✅ Can test server startup/shutdown
- ❌ More verbose
- **Use for**: Complex scenarios, debugging

### Demo Server Script
- ✅ Perfect for development and debugging
- ✅ Can send real HTTP requests
- ✅ Easy to test endpoints manually
- **Use for**: Development, manual testing

## 🔧 Key Features of UvicornTestServer

1. **Automatic Port Detection**: Finds free ports to avoid conflicts
2. **Proper Lifecycle**: Clean startup and shutdown
3. **Thread Safety**: Runs server in background thread
4. **Health Checking**: Waits for server to be ready
5. **Graceful Shutdown**: Proper cleanup on exit
6. **Error Handling**: Robust error handling and timeouts

## 🚀 Making HTTP Requests

Once you have a running server, you can make requests using:

### With aiohttp (async)
```python
async with ClientSession() as session:
async with session.get(f"{server.base_url}/health") as response:
data = await response.json()
assert data["message"] == "OK"
```

### With httpx (async)
```python
async with httpx.AsyncClient() as client:
response = await client.get(f"{server.base_url}/health")
assert response.status_code == 200
```

### With curl (command line)
```bash
curl http://127.0.0.1:8000/health
curl -X POST http://127.0.0.1:8000/api/data -H "Content-Type: application/json" -d '{"key": "value"}'
```

### With requests (sync)
```python
import requests
response = requests.get(f"{server.base_url}/health")
assert response.status_code == 200
```

## 🎉 Success!

Now you have a proper way to run uvicorn servers for integration testing that:
- ✅ Actually starts and stops properly
- ✅ Provides real HTTP endpoints
- ✅ Handles cleanup automatically
- ✅ Avoids port conflicts
- ✅ Works reliably in CI/CD

No more blocking `uvicorn.run()` calls or tests that never execute!
Comment on lines +1 to +207
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Fix markdown spacing and trailing newline to satisfy markdownlint

Add blank lines around headings, lists, and fenced blocks; remove trailing spaces; end file with a newline.

If you want, I can run a pass and push a formatted version.

🧰 Tools
🪛 LanguageTool

[grammar] ~1-~1: Use correct spacing
Context: ... Integration Testing with Uvicorn Server This directory contains examples of how ...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~3-~3: Use correct spacing
Context: ...endpoints that you can send requests to. ## 🚨 The Problem with uvicorn.run() The...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~5-~5: There might be a mistake here.
Context: ...nd requests to. ## 🚨 The Problem with uvicorn.run() The original code had this issue: ```py...

(QB_NEW_EN)


[grammar] ~7-~7: Use correct spacing
Context: ...un() The original code had this issue: ```python @pytest.mark.asyncio async def test_basic(): uvicorn.run(app, host="127.0.0.1", port=8000) # ❌ This blocks forever! async with ClientSession() as session: # ❌ This never executes # ... test code that never runs ``` **Problem**:uvicorn.run()` is a blocking...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~17-~17: Use correct spacing
Context: ... your test code after it never executes. ## ✅ Proper Solutions ### 1. UvicornTestSe...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~19-~19: Use correct spacing
Context: ...t never executes. ## ✅ Proper Solutions ### 1. UvicornTestServer Class (Recommended)...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~21-~21: Use correct spacing
Context: ...1. UvicornTestServer Class (Recommended) The UvicornTestServer class in `test_b...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~23-~23: Use correct spacing
Context: ...n way to start and stop uvicorn servers: python from test_basic import UvicornTestServer # Create and start server server = UvicornTestServer(app, host="127.0.0.1", port=8000) server.start() # Make HTTP requests async with ClientSession() as session: async with session.get(f"{server.base_url}/health") as response: assert response.status == 200 # Stop server server.stop() Features: - ✅ Automatic port detection...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~41-~41: There might be a mistake here.
Context: ...op server server.stop() ``` Features: - ✅ Automatic port detection (avoids confl...

(QB_NEW_EN)


[grammar] ~42-~42: There might be a mistake here.
Context: ...omatic port detection (avoids conflicts) - ✅ Proper startup/shutdown lifecycle - ✅ ...

(QB_NEW_EN)


[grammar] ~43-~43: There might be a mistake here.
Context: ...s) - ✅ Proper startup/shutdown lifecycle - ✅ Thread-based server execution - ✅ Wait...

(QB_NEW_EN)


[grammar] ~44-~44: There might be a mistake here.
Context: ...ecycle - ✅ Thread-based server execution - ✅ Waits for server to be ready - ✅ Grace...

(QB_NEW_EN)


[grammar] ~45-~45: There might be a mistake here.
Context: ...ecution - ✅ Waits for server to be ready - ✅ Graceful cleanup ### 2. Pytest Fixtur...

(QB_NEW_EN)


[grammar] ~46-~46: Use correct spacing
Context: ... server to be ready - ✅ Graceful cleanup ### 2. Pytest Fixtures Use pytest fixtures ...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~48-~48: Use correct spacing
Context: ...Graceful cleanup ### 2. Pytest Fixtures Use pytest fixtures for automatic server...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~50-~50: Use correct spacing
Context: ...ixtures for automatic server management: python @pytest.fixture(scope="session") def running_server(): """Server shared across all tests in the session.""" server = UvicornTestServer(app) server.start() yield server server.stop() @pytest.fixture(scope="function") def fresh_server(): """Fresh server for each test.""" server = UvicornTestServer(app) server.start() yield server server.stop() ### 3. Manual Server Management For full co...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~70-~70: Use correct spacing
Context: ...p() ``` ### 3. Manual Server Management For full control over server lifecycle: ...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~72-~72: Use correct spacing
Context: ... For full control over server lifecycle: python @pytest.mark.asyncio async def test_manual_server(): server = UvicornTestServer(app) try: server.start() # Your test code here async with ClientSession() as session: async with session.get(f"{server.base_url}/health") as response: assert response.status == 200 finally: server.stop() # Always cleanup ## 🏃‍♂️ Running the Examples ### Install ...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~89-~89: There might be a mistake here.
Context: ...eanup ## 🏃‍♂️ Running the Examples ### Install Dependencies bash cd integra...

(QB_NEW_EN)


[grammar] ~91-~91: Use correct spacing
Context: ...g the Examples ### Install Dependencies bash cd integration-tests uv sync ### Run Tests ```bash # Run all tests uv ru...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~98-~98: Use correct spacing
Context: ...gration-tests uv sync ### Run Tests bash # Run all tests uv run python -m pytest test_basic.py -v # Run specific test uv run python -m pytest test_basic.py::test_basic_with_session_server -v -s # Run with output uv run python -m pytest test_basic.py -v -s ### Run Demo Server bash # Start a serve...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~111-~111: Use correct spacing
Context: ..._basic.py -v -s ### Run Demo Server bash # Start a server you can send requests to uv run python demo_server.py Then in another terminal:bash curl h...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~118-~118: Use correct spacing
Context: ...server.py Then in another terminal:bash curl http://127.0.0.1:8000/health curl http://127.0.0.1:8000/test ``` ## 📁 File Overview - test_basic.py ...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~124-~124: There might be a problem here.
Context: ...0.0.1:8000/test ``` ## 📁 File Overview - test_basic.py - Main test file with UvicornTestServer ...

(QB_NEW_EN_MERGED_MATCH)


[grammar] ~126-~126: Use correct spacing
Context: ...ith UvicornTestServer class and examples - test_server_examples.py - Comprehensive examples of different te...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~127-~127: Use correct spacing
Context: ...examples of different testing approaches - demo_server.py - Simple script to run a server manually...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~128-~128: Use correct spacing
Context: ...- Simple script to run a server manually - pyproject.toml - Project dependencies ## 🎯 When to Us...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~129-~129: Use correct spacing
Context: ...pyproject.toml`** - Project dependencies ## 🎯 When to Use Each Approach ### Sessio...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~131-~131: There might be a mistake here.
Context: ...dencies ## 🎯 When to Use Each Approach ### Session-Scoped Server (running_server ...

(QB_NEW_EN)


[grammar] ~133-~133: There might be a mistake here.
Context: ...Scoped Server (running_server fixture) - ✅ Fast test execution (server starts onc...

(QB_NEW_EN)


[grammar] ~134-~134: There might be a mistake here.
Context: ...Fast test execution (server starts once) - ✅ Good for multiple tests that don't int...

(QB_NEW_EN)


[grammar] ~135-~135: There might be a mistake here.
Context: ... for multiple tests that don't interfere - ❌ Tests share state - Use for: Most ...

(QB_NEW_EN)


[grammar] ~136-~136: There might be a mistake here.
Context: ...at don't interfere - ❌ Tests share state - Use for: Most integration tests ### F...

(QB_NEW_EN)


[grammar] ~137-~137: Use correct spacing
Context: ...te - Use for: Most integration tests ### Function-Scoped Server (fresh_server f...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~139-~139: There might be a mistake here.
Context: ...n-Scoped Server (fresh_server fixture) - ✅ Complete isolation between tests - ✅ C...

(QB_NEW_EN)


[grammar] ~140-~140: There might be a mistake here.
Context: ...) - ✅ Complete isolation between tests - ✅ Clean state for each test - ❌ Slower (...

(QB_NEW_EN)


[grammar] ~141-~141: There might be a mistake here.
Context: ...ween tests - ✅ Clean state for each test - ❌ Slower (starts server for each test) -...

(QB_NEW_EN)


[grammar] ~142-~142: There might be a mistake here.
Context: ...- ❌ Slower (starts server for each test) - Use for: Tests that modify server stat...

(QB_NEW_EN)


[grammar] ~143-~143: Use correct spacing
Context: ...se for**: Tests that modify server state ### Manual Server Management - ✅ Full contro...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~145-~145: There might be a mistake here.
Context: ...rver state ### Manual Server Management - ✅ Full control over lifecycle - ✅ Can te...

(QB_NEW_EN)


[grammar] ~146-~146: There might be a mistake here.
Context: ...nagement - ✅ Full control over lifecycle - ✅ Can test server startup/shutdown - ❌ M...

(QB_NEW_EN)


[grammar] ~147-~147: There might be a mistake here.
Context: ...cle - ✅ Can test server startup/shutdown - ❌ More verbose - Use for: Complex sc...

(QB_NEW_EN)


[grammar] ~148-~148: There might be a mistake here.
Context: ...server startup/shutdown - ❌ More verbose - Use for: Complex scenarios, debugging ...

(QB_NEW_EN)


[grammar] ~149-~149: Use correct spacing
Context: ...Use for*: Complex scenarios, debugging ### Demo Server Script - ✅ Perfect for devel...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~151-~151: There might be a mistake here.
Context: ...arios, debugging ### Demo Server Script - ✅ Perfect for development and debugging ...

(QB_NEW_EN)


[grammar] ~152-~152: There might be a mistake here.
Context: ... ✅ Perfect for development and debugging - ✅ Can send real HTTP requests - ✅ Easy t...

(QB_NEW_EN)


[grammar] ~153-~153: There might be a mistake here.
Context: ...ebugging - ✅ Can send real HTTP requests - ✅ Easy to test endpoints manually - **Us...

(QB_NEW_EN)


[grammar] ~154-~154: There might be a mistake here.
Context: ...ests - ✅ Easy to test endpoints manually - Use for: Development, manual testing ...

(QB_NEW_EN)


[grammar] ~155-~155: Use correct spacing
Context: ...Use for: Development, manual testing ## 🔧 Key Features of UvicornTestServer 1....

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~157-~157: There might be a mistake here.
Context: ... ## 🔧 Key Features of UvicornTestServer 1. Automatic Port Detection: Finds free p...

(QB_NEW_EN)


[grammar] ~159-~159: There might be a mistake here.
Context: ...n**: Finds free ports to avoid conflicts 2. Proper Lifecycle: Clean startup and sh...

(QB_NEW_EN_OTHER)


[grammar] ~164-~164: There might be a mistake here.
Context: ...ng**: Robust error handling and timeouts ## 🚀 Making HTTP Requests Once you have a...

(QB_NEW_EN_OTHER)


[grammar] ~166-~166: There might be a mistake here.
Context: ...and timeouts ## 🚀 Making HTTP Requests Once you have a running server, you can ...

(QB_NEW_EN)


[grammar] ~168-~168: There might be a mistake here.
Context: ...a running server, you can make requests using: ### With aiohttp (async) ```python async wit...

(QB_NEW_EN_OTHER)


[grammar] ~170-~170: Use correct spacing
Context: ...equests using: ### With aiohttp (async) python async with ClientSession() as session: async with session.get(f"{server.base_url}/health") as response: data = await response.json() assert data["message"] == "OK" ### With httpx (async) ```python async with ...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~178-~178: Use correct spacing
Context: ...ge"] == "OK" ### With httpx (async)python async with httpx.AsyncClient() as client: response = await client.get(f"{server.base_url}/health") assert response.status_code == 200 ### With curl (command line)bash curl ht...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~185-~185: Use correct spacing
Context: ...== 200 ### With curl (command line)bash curl http://127.0.0.1:8000/health curl -X POST http://127.0.0.1:8000/api/data -H "Content-Type: application/json" -d '{"key": "value"}' ### With requests (sync)python import re...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~191-~191: Use correct spacing
Context: ... "value"}' ### With requests (sync)python import requests response = requests.get(f"{server.base_url}/health") assert response.status_code == 200 ``` ## 🎉 Success! Now you have a proper way t...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~198-~198: There might be a mistake here.
Context: ...e.status_code == 200 ``` ## 🎉 Success! Now you have a proper way to run uvicorn...

(QB_NEW_EN)


[grammar] ~200-~200: There might be a mistake here.
Context: ...rn servers for integration testing that: - ✅ Actually starts and stops properly - ✅...

(QB_NEW_EN)


[grammar] ~201-~201: There might be a mistake here.
Context: ...: - ✅ Actually starts and stops properly - ✅ Provides real HTTP endpoints - ✅ Handl...

(QB_NEW_EN)


[grammar] ~202-~202: There might be a mistake here.
Context: ...roperly - ✅ Provides real HTTP endpoints - ✅ Handles cleanup automatically - ✅ Avoi...

(QB_NEW_EN)


[grammar] ~203-~203: There might be a mistake here.
Context: ...points - ✅ Handles cleanup automatically - ✅ Avoids port conflicts - ✅ Works reliab...

(QB_NEW_EN)


[grammar] ~204-~204: There might be a mistake here.
Context: ... automatically - ✅ Avoids port conflicts - ✅ Works reliably in CI/CD No more block...

(QB_NEW_EN)


[grammar] ~205-~205: Use correct spacing
Context: ...rt conflicts - ✅ Works reliably in CI/CD No more blocking uvicorn.run() calls o...

(QB_NEW_EN_OTHER_ERROR_IDS_5)

🪛 markdownlint-cli2 (0.17.2)

42-42: Lists should be surrounded by blank lines

(MD032, blanks-around-lists)


119-119: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


133-133: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


134-134: Lists should be surrounded by blank lines

(MD032, blanks-around-lists)


139-139: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


140-140: Lists should be surrounded by blank lines

(MD032, blanks-around-lists)


145-145: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


146-146: Lists should be surrounded by blank lines

(MD032, blanks-around-lists)


151-151: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


152-152: Lists should be surrounded by blank lines

(MD032, blanks-around-lists)


170-170: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


171-171: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


178-178: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


179-179: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


185-185: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


186-186: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


191-191: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


192-192: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


201-201: Lists should be surrounded by blank lines

(MD032, blanks-around-lists)


207-207: Trailing spaces
Expected: 0 or 2; Actual: 1

(MD009, no-trailing-spaces)


207-207: Files should end with a single newline character

(MD047, single-trailing-newline)

🤖 Prompt for AI Agents
In integration-tests/README.md lines 1 to 207, the file fails markdownlint
rules: missing blank lines before/after headings, lists and fenced code blocks,
contains trailing spaces, and lacks a final newline. Fix by inserting a single
blank line above and below each top-level heading, subheading, list block, and
fenced code block; remove any trailing spaces at end of lines; ensure the file
ends with exactly one newline character; then run markdownlint or a formatter to
verify compliance.

Loading