Files
onyx/backend/tests
Wenxi e9c36400a7 feat(craft): enforce personal and task action policies (#16179)
## Description

Give each user and scheduled task its own action settings.
Workspace-disabled actions always remain blocked. Admins can require
workspace settings or let users choose their own settings.

Scheduled tasks now show app and MCP cards. Apps start disabled. When
creating a task, enabling an app copies the user's current settings.
Existing tasks keep their saved choices when apps are enabled again;
missing choices require approval. Later requests use the task's saved
settings and current workspace restrictions.

One migration replaces app-wide task pre-approvals with per-action
settings. Existing tasks receive auto-approval only for known actions in
apps they already pre-approved. Apps remain enabled even if they have no
known actions. New actions require approval. The migration remains with
the code that reads the new tables.

Action recognition identifies the request. One policy resolver then
applies workspace restrictions, task membership, personal or task
settings, and session grants. Failed recognition or policy lookup blocks
the request. A completed scheduled session cannot use personal settings
for later requests.

The next PR adds app settings editors. This PR includes the task editor
because its payload changes with the task API.

## How Has This Been Tested?

The complete stack passed 71 local Kind scenario checks, covering
browser editing, persistence, access restrictions, real proxy requests,
and scheduled execution. Checks include a real 180-second approval
timeout, live policy changes, and completed-task request rejection.

This PR’s intermediate tree passed 1,053 backend unit tests, 148
database tests, five route-contract tests, and 39 frontend tests. Python
and TypeScript checks and changed-file pre-commit checks passed.

No migration was applied to the user's database during this
verification.

## Additional Options

- [ ] [Optional] Please cherry-pick this PR to the latest release
version.
- [x] [Optional] Override Linear Check


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added per-action approval settings for connected apps and MCP tools,
with workspace, personal, and scheduled-task controls.
* Scheduled tasks can enable apps and configure individual actions when
creating or editing tasks; settings are also visible on task details.
New tasks start with apps disabled.
* Policy editors support searching, bulk changes, and resetting editable
choices.
* **Security & Permissions**
* Workspace restrictions remain binding, and task settings are checked
during active runs.
* Actions requiring approval still prompt users when app credentials are
unavailable.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-10-19 18:44:13 +00:00
..

Backend Tests

Test Types

There are four test categories, ordered by increasing scope:

Unit Tests (tests/unit/)

No external services. Mock all I/O with unittest.mock. Use for complex, isolated logic (e.g. citation processing, encryption).

pytest -xv backend/tests/unit

External Dependency Unit Tests (tests/external_dependency_unit/)

Real Postgres, Redis, MinIO, and OpenSearch available. Real OpenAI key when set. Real Docker daemon when a Docker-backend test opts into it. Onyx application processes (API server, Celery workers) are not running. Tests import and call functions directly and can mock selectively.

Conditional external dependencies such as OpenAI are gated by skipif at the top of the test file so the suite stays runnable in environments that lack the relevant env var or credential.

Use when you need a real database or real API calls but want control over setup.

uv run --env-file .vscode/.env pytest backend/tests/external_dependency_unit

Integration Tests (tests/integration/)

Full Onyx deployment running. Do not mock Onyx code. Faking an external service (for example the scripted LLM server behind the mock_llm fixture) is fine. Prefer this over other test types when possible. Most integration tests exercise the product through HTTP API manager helpers under tests/integration/common_utils.

Craft Kubernetes coverage lives in tests/integration/tests/craft/k8s/ and runs in the dedicated Helm-installed kind job (pr-craft-k8s-tests.yml). That suite is a full deployed Craft integration lane: the chart provides Postgres, Redis, MinIO, OpenSearch, api_server, web_server, Celery workers, sandbox-proxy, and real sandbox pods in kind. API-facing setup goes through the deployed api_server; direct manager calls are reserved for low-level Kubernetes contracts that do not have an HTTP API. Direct task/stub checks belong in tests/external_dependency_unit/craft/.

uv run --env-file .vscode/.env pytest backend/tests/integration

Playwright / E2E Tests (web/tests/e2e/)

Full stack including web server. Use for frontend-backend coordination.

cd web && bun run playwright <TEST_NAME>

Shared Fixtures

Shared fixtures live in backend/tests/conftest.py. Test subdirectories can define their own conftest.py for directory-scoped fixtures.

Running Tests Repeatedly (pytest-repeat)

Use pytest-repeat to catch flaky tests by running them multiple times:

# Run a specific test 50 times
pytest --count=50 backend/tests/unit/path/to/test.py::test_name

# Stop on first failure with -x
pytest --count=50 -x backend/tests/unit/path/to/test.py::test_name

# Repeat an entire test file
pytest --count=10 backend/tests/unit/path/to/test_file.py

Best Practices

Leave nothing behind

A test may reset or wipe shared state before it runs. It must not leave its own state behind for a later test to see: rows, changed global defaults, Redis keys, OpenSearch indexes, running threads, or settings on a pooled database connection. Undo each change in a finally or a yield-fixture teardown.

External dependency unit tests check one case of this automatically. After each test, an autouse fixture (no_leaked_db_session_settings) fails the test if it returned a pooled connection with a session-level Postgres setting on it, such as a plain SET lock_timeout. To change session state, use SET LOCAL, reset the setting, or run on the isolated_engine fixture. Migration tests that run migrations in-process must use isolated_engine.

Use enable_ee fixture instead of inlining

Enables EE mode for a test, with proper teardown and cache clearing.

# Whole file (in a test module, NOT in conftest.py)
pytestmark = pytest.mark.usefixtures("enable_ee")


# Whole directory — add an autouse wrapper to the directory's conftest.py
@pytest.fixture(autouse=True)
def _enable_ee_for_directory(enable_ee: None) -> None:
    """Wraps the shared enable_ee fixture with autouse for this directory."""


# Single test
def test_something(enable_ee: None) -> None: ...

Note: pytestmark in a conftest.py does NOT apply markers to tests in that directory — it only affects tests defined in the conftest itself (which is none). Use the autouse fixture wrapper pattern shown above instead.

Do NOT inline global_version.set_ee() — always use the fixture.

Craft sandbox network enforcement

Run the deployed Kubernetes networking suite against a disposable cluster with an enforcing CNI. Test both sandboxPod.netAdminLockdown=true and false through the deployment's Helm values. Set these variables for the target deployment:

export SANDBOX_TEST_FRONTEND_URL=http://localhost:3000
export SANDBOX_TEST_KUBE_CONTEXT=your-test-context
export SANDBOX_TEST_IP_FAMILY=ipv4
export SANDBOX_TEST_NET_ADMIN_LOCKDOWN=false
uv run pytest backend/tests/integration/tests/craft/k8s/networking

Set the expected lockdown value to match each Helm configuration. The suite checks the actual pod configuration, proxy requests, and blocked direct API, public internet, and DNS connections. DNS and public connection checks first verify that the target responds from the proxy. Use an IPv6 cluster and SANDBOX_TEST_IP_FAMILY=ipv6 to check that family.