## Description
`BoxConnector` now implements `Resolver.reindex`. Targeted reindex can
re-fetch Box documents by id, without a crawl or a checkpoint.
**Document types covered**
- Files (`box-file-<id>`): fetched with `files.get_file_by_id`.
- Web links (`box-weblink-<id>`): fetched with
`web_links.get_web_link_by_id`, only when `include_web_links` is on.
The Box connector emits no other document types, so all types are
covered.
**How it works**
- The item's `path_collection` gives its folder chain. The connector
finds the outermost configured entry folder in that chain and rebuilds
the crawl's frontier entries from that folder down to the parent. The
`path` metadata, `parent_hierarchy_raw_node_id` and folder
`HierarchyNode`s match the crawl.
- Documents are built with the crawl's own `_convert_file` /
`_convert_web_link`. The folder `HierarchyNode` builder moved into a
shared `_folder_hierarchy_node` helper.
- Folder entries are cached for the whole call, so targets in the same
folder share upstream calls. Each folder node is yielded once.
**Failures** (each keyed on the input document id)
- Ids that do not parse as `box-file-<digits>` or
`box-weblink-<digits>`.
- Items that are not found (404), not readable (403), trashed, or
deleted.
- Items outside the configured folders.
- Web links when `include_web_links` is off.
- Files that the crawl skips (unsupported type, over the size threshold,
empty download), and conversion errors.
A `BoxAPIError` on one target does not stop the other targets.
Credential errors (HTTP 401, or OAuth `invalid_client` /
`unauthorized_client`) are raised again. Entity failures are skipped, as
in Confluence and Google Drive.
**Permissions**
With `include_permissions=True`, access uses the same EE helpers as the
perm-sync crawl: `resolve_box_ancestor_access` for the folders above the
entry folder, `resolve_box_folder_access` for each folder down to the
parent, then `resolve_box_file_access` / `resolve_box_web_link_access`
for the item. Folder nodes carry the same access too.
## How Has This Been Tested?
New daily test
`backend/tests/daily/connectors/box/test_box_resolver.py`, run against
the Box CI enterprise:
```
uv run --env-file .vscode/.env pytest -xv backend/tests/daily/connectors/box/test_box_resolver.py
13 passed
```
It covers: one file, all files plus the web link,
`include_permissions=True` (the test asserts `external_access` on docs
and nodes equals the perm-sync crawl's), a disabled web link, a file
outside the configured folder, bogus file and web link ids, unparseable
ids, a bad target next to a good one, empty input, and entity-only
input.
Also run: `backend/tests/daily/connectors/box/test_box_basic.py` (6
passed) and `backend/tests/unit/onyx/connectors/box` (61 passed).
## Additional Options
- [ ] [Optional] Please cherry-pick this PR to the latest release
version.
- [x] [Optional] Override Linear Check
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
`BoxConnector` now implements `Resolver.reindex`, so failed documents
can be re-fetched by ID without a crawl or checkpoint.
- File (`box-file-<id>`) and web link (`box-weblink-<id>`) targets are
fetched via the Box API; web links only when `include_web_links` is on.
- The connector rebuilds the crawl's folder chain from the innermost
configured entry folder down to the item's parent, matching how the
crawl roots nested configured folders, and reuses `_convert_file` /
`_convert_web_link` for metadata, `HierarchyNode`s, and permissions.
- Failures are keyed on the input document ID for unparseable IDs,
trashed or inaccessible items, out-of-scope folders, disabled web links,
and items the crawl would skip. One bad target does not abort the
others; credential errors, including from downloads and permission
reads, are re-raised.
- With `include_permissions=True`, folder and document access match the
perm-sync crawl via the existing EE helpers.
- Adds daily and unit tests covering success paths, permission parity,
nested configured folders, and each failure case.
<sup>Written for commit c9c7d09c90.
Summary will update on new commits.</sup>
<a
href="https://cubic.dev/pr/onyx-dot-app/onyx/pull/16099?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="View guided diff"
src="https://www.cubic.dev/buttons/review-in-cubic-light.svg"></picture></a>
<a
href="https://www.cubic.dev/action/auto-fix/pr/onyx-dot-app/onyx/16099?returnTo=https%3A%2F%2Fgithub.com%2Fonyx-dot-app%2Fonyx%2Fpull%2F16099&source=description"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/turn-on-auto-fix-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/turn-on-auto-fix-light.svg"><img
alt="Turn on auto-fix"
src="https://www.cubic.dev/buttons/turn-on-auto-fix-light.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
<!-- coderabbit-ai-generated:v1 -->
## Summary by CodeRabbit
* **New Features**
* Added targeted reindexing for Box files and web links using document
IDs.
* Reindexed items retain their metadata, folder hierarchy, and
permissions.
* Missing parent folders can be rebuilt during reindexing.
* Multiple items can be processed while reporting individual successes
and failures.
* Clear failures are provided for malformed, unknown, inactive,
out-of-scope, or unavailable items.
* Reindexing now respects Box item status and configured folder or
web-link scope.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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.