# Testing Strategies for API Services and LangGraph in Open Notebook: A Complete Guide

> Discover essential testing strategies for API services and LangGraph in Open Notebook. Learn how pytest, FastAPI TestClient, and async mocking ensure robust API contracts and correct LangGraph execution.

- Repository: [Luis Novo/open-notebook](https://github.com/lfnovo/open-notebook)
- Tags: how-to-guide
- Published: 2026-06-27

---

**Open Notebook employs a comprehensive testing strategy that combines pytest, FastAPI TestClient, and extensive async mocking to validate REST API contracts and ensure LangGraph workflows compile and execute correctly.**

The `lfnovo/open-notebook` repository implements rigorous **testing strategies for API services and LangGraph** workflows that verify both REST endpoint contracts and AI pipeline execution. Using `pytest` with Python's `unittest.mock` library, the test suite isolates external dependencies while confirming that HTTP routes return correct status codes and that graph nodes properly mutate state. All tests reside in the `tests/` directory and execute via `uv run pytest tests/`, running continuously on CI to prevent regressions in API behavior or workflow logic.

## FastAPI Service Testing Strategies

The API testing strategy centers on `pytest` with the **FastAPI `TestClient`** to issue real HTTP requests against endpoints without requiring a live server. Tests extensively mock database calls, command-queue submissions, and file handling using `patch`, `AsyncMock`, and `MagicMock` to eliminate external dependencies.

### Testing Synchronous and Asynchronous Endpoints

The suite distinguishes between sync and async processing paths, particularly for source creation workflows. In [`tests/test_sources_api.py`](https://github.com/lfnovo/open-notebook/blob/main/tests/test_sources_api.py), the `test_async_link_source_persists_url_asset` method verifies that when `async_processing=true`, the endpoint correctly submits jobs to the command queue while persisting asset metadata.

```python

# tests/test_sources_api.py

@pytest.mark.asyncio
@patch("api.routers.sources.CommandService.submit_command_job", new_callable=AsyncMock)
@patch("api.routers.sources.Source.add_to_notebook", new_callable=AsyncMock)
@patch("api.routers.sources.Notebook.get", new_callable=AsyncMock)
async def test_async_link_source_persists_url_asset(self, mock_nb_get, mock_add_nb, mock_submit, client):
    mock_nb_get.return_value = MagicMock()
    mock_submit.return_value = "command:123"

    saved_sources = []
    async def capture_save(self_source):
        saved_sources.append(self_source)
        self_source.id = "source:fake"
        self_source.command = None

    # Patch the model‑layer save method

    with patch.object(Source, "save", autospec=True, side_effect=capture_save):
        response = client.post(
            "/api/sources",
            data={"type": "link", "url": "https://example.com/article",
                  "notebooks": '["notebook:1"]', "async_processing": "true"},
        )
    assert response.status_code == 200
    assert saved_sources[0].asset.url == "https://example.com/article"

```

This pattern ensures that endpoints return correct HTTP status codes and payload structures while verifying side effects like Asset persistence, all without depending on a live database or job queue.

## LangGraph Workflow Testing Strategies

Testing strategies for LangGraph workflows focus on three pillars: graph compilation verification, state struct validation, and tool function unit testing. Tests directly import graph objects from `open_notebook/graphs/*.py` to verify they expose the required `invoke` and `ainvoke` methods.

### Graph Compilation and State Management

The `test_prompt_graph_compilation` case in [`tests/test_graphs.py`](https://github.com/lfnovo/open-notebook/blob/main/tests/test_graphs.py) confirms that graph objects compile correctly and maintain the expected interface.

```python
def test_prompt_graph_compilation(self):
    assert graph is not None
    assert hasattr(graph, "invoke")
    assert hasattr(graph, "ainvoke")

```

Async tests marked with `@pytest.mark.asyncio` invoke graph nodes using `await` to verify state mutations in `PatternChainState` and `TransformationState` structures. Tests mock domain objects like `Source` and `Transformation` to isolate graph logic from persistence layers.

### Tool Function Unit Testing

Individual tools used within graphs receive dedicated unit tests. The `get_current_timestamp` function validation confirms output format and decorator metadata.

```python

# tests/test_graphs.py

def test_get_current_timestamp_format(self):
    timestamp = get_current_timestamp.func()
    assert isinstance(timestamp, str)
    assert len(timestamp) == 14               # YYYYMMDDHHmmss

    assert timestamp.isdigit()

```

## Environment Isolation and Fixture Management

Deterministic testing of model-provider discovery requires strict environment control. The [`conftest.py`](https://github.com/lfnovo/open-notebook/blob/main/conftest.py) file contains fixtures that clear environment variables before each test suite runs. Individual tests then patch `os.environ.get` to simulate various provider configurations without affecting the developer's local environment.

In [`tests/test_models_api.py`](https://github.com/lfnovo/open-notebook/blob/main/tests/test_models_api.py), the `test_generic_env_var_enables_all_modes` method demonstrates this pattern by mocking both environment access and the provider factory.

```python
@patch("api.routers.models.os.environ.get")
@patch("api.routers.models.AIFactory.get_available_providers")
def test_generic_env_var_enables_all_modes(self, mock_esperanto, mock_env, client):
    mock_env.side_effect = lambda k: "http://localhost:1234/v1" if k == "OPENAI_COMPATIBLE_BASE_URL" else None
    mock_esperanto.return_value = {"language": ["openai-compatible"], "embedding": ["openai-compatible"],
                                   "speech_to_text": ["openai-compatible"], "text_to_speech": ["openai-compatible"]}

    resp = client.get("/api/models/providers")
    data = resp.json()
    assert "openai_compatible" in data["available"]
    assert set(data["supported_types"]["openai_compatible"]) == {"language", "embedding",
                                                                "speech_to_text", "text_to_speech"}

```

## Edge Case Coverage and Regression Prevention

The testing strategy includes specific assertions for boundary conditions and error handling. Tests verify case-insensitive duplicate model creation prevention, missing asset handling, and title processing during source ingestion. SQL generation receives scrutiny through assertions that specific fragments like `"WHERE in = $source_id"` appear in retry flows. Command-queue integration tests ensure commands are not double-prefixed with `"command:command"`.

Test classes follow descriptive naming conventions such as `TestAsyncSourceAssetPersistence`, `TestGraphTools`, and `TestPromptGraph`, grouping related functionality and improving readability when diagnosing failures.

## Summary

- **FastAPI testing** uses `TestClient` with `AsyncMock` and `MagicMock` to isolate HTTP endpoints from database and queue dependencies.
- **LangGraph validation** confirms graph compilation, state mutations, and tool function correctness through direct imports from `open_notebook/graphs/*.py`.
- **Environment control** via [`conftest.py`](https://github.com/lfnovo/open-notebook/blob/main/conftest.py) fixtures ensures deterministic provider discovery testing regardless of local configuration.
- **Edge case coverage** includes duplicate detection, SQL fragment validation, and command prefix verification to prevent regressions.
- **Test organization** groups related scenarios into descriptive classes within [`tests/test_sources_api.py`](https://github.com/lfnovo/open-notebook/blob/main/tests/test_sources_api.py), [`tests/test_models_api.py`](https://github.com/lfnovo/open-notebook/blob/main/tests/test_models_api.py), and [`tests/test_graphs.py`](https://github.com/lfnovo/open-notebook/blob/main/tests/test_graphs.py).

## Frequently Asked Questions

### How does Open Notebook test async FastAPI endpoints?

Open Notebook tests async endpoints using `@pytest.mark.asyncio` decorators combined with `AsyncMock` patches for database and queue operations. The `test_async_link_source_persists_url_asset` method in [`tests/test_sources_api.py`](https://github.com/lfnovo/open-notebook/blob/main/tests/test_sources_api.py) demonstrates this by mocking `CommandService.submit_command_job` and `Source.save` while asserting that async processing flags trigger correct side effects.

### What testing approach validates LangGraph workflows?

The strategy validates LangGraph workflows by importing graph objects directly from `open_notebook/graphs/*.py` and verifying they expose `invoke` and `ainvoke` methods. Tests execute graph nodes asynchronously to confirm state mutations in `PatternChainState` and `TransformationState` structures while mocking domain objects to isolate logic from persistence layers.

### How are environment variables handled in tests?

A global fixture in [`tests/conftest.py`](https://github.com/lfnovo/open-notebook/blob/main/tests/conftest.py) clears environment variables before each test suite. Individual tests then patch `os.environ.get` to simulate specific provider configurations, as seen in [`tests/test_models_api.py`](https://github.com/lfnovo/open-notebook/blob/main/tests/test_models_api.py) where the `test_generic_env_var_enables_all_modes` method validates model-provider discovery without requiring actual API credentials.

### Where are the test files located in the Open Notebook repository?

Test files reside in the `tests/` directory at the repository root. Key files include [`tests/test_sources_api.py`](https://github.com/lfnovo/open-notebook/blob/main/tests/test_sources_api.py) for source creation endpoints, [`tests/test_models_api.py`](https://github.com/lfnovo/open-notebook/blob/main/tests/test_models_api.py) for model management, [`tests/test_graphs.py`](https://github.com/lfnovo/open-notebook/blob/main/tests/test_graphs.py) for LangGraph workflows, and [`tests/conftest.py`](https://github.com/lfnovo/open-notebook/blob/main/tests/conftest.py) for shared fixtures and environment control.