Files
onyx/backend/tests
Evan LohnandClaude Opus 5.5 ae9b863612 feat(jira): re-fetch failed documents by id (Resolver) (#16102)
## Description

`JiraConnector` now implements `Resolver.reindex`. Targeted reindex can
now re-fetch failed Jira documents by id.

**Document types covered**
- Jira issues. The issue is the only document type the connector writes.
Comments are part of the issue text, and the connector does not index
attachments. The document id is the issue URL (`<base>/browse/<KEY>`).

**Document types not covered**
- None.

**How it works**
- `reindex` parses the issue key from each document URL. The URL must
start with the connector's own `<base>/browse/`, and the key must be a
valid Jira key. Other ids yield a `ConnectorFailure` with the input id.
- It fetches the issues with one JQL search per batch of 50 keys: `key
in (...)`, limited to the configured project or JQL (new `scope_jql`
helper, which `build_jql_query` now uses too). The search uses the same
paths as the crawl (Cloud: enhanced search + bulk fetch; Server/DC: v2
search).
- Server/DC rejects a JQL query that names a missing key. When a batch
fails, the connector fetches each key alone, so one bad key does not
fail the rest. A 401 or 403 still fails the whole run, as in the crawl.
- Each input id that does not give a document yields a
`ConnectorFailure` with the reason: fetch error, conversion error,
skipped by label or size, or not found in scope (deleted, moved, or out
of scope).
- Entity failures (`failed_document is None`) are skipped, as in
Confluence.
- The per-issue conversion (hierarchy nodes, then the document) moves
out of `_load_from_checkpoint` into `_issue_to_items`. The crawl and
`reindex` both use it. `reindex` yields the project node and the Epic
nodes before the documents.

**Permissions**
- With `include_permissions=True`, `reindex` sets `external_access` with
`_get_project_permissions(project_key, add_prefix=True)`. This is the
same call that `load_from_checkpoint_with_perm_sync` uses, and it caches
each project.

## How Has This Been Tested?

- New daily test
`backend/tests/daily/connectors/jira/test_jira_resolver.py`, against the
live CI site (project AS):
`uv run --env-file .vscode/.env pytest -xv
backend/tests/daily/connectors/jira/test_jira_resolver.py` -> 9 passed.
It covers: reindex of one issue matches the crawl output and hierarchy
nodes; Story and Epic together (classic and scoped token); missing key;
out-of-scope issue (JQL scope); unparseable ids; empty input;
entity-only input; `include_permissions=True` sets `external_access`.
- New unit test
`backend/tests/unit/onyx/connectors/jira/test_jira_reindex.py` for the
Server/DC per-key fallback and the 401 path. All Jira unit tests after
the rebase onto main: 141 passed. The daily results below are from
before the rebase.
- All Jira daily tests: 29 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
Adds `Resolver.reindex` to the Jira connector so targeted reindexes can
re-fetch failed issues by document id.

- Parses each document URL's issue key, fetches issues in batches via
`key in (...)` JQL scoped to the configured project or JQL, and yields
the project and Epic hierarchy nodes before each document.
- On Server/DC, a batch that fails on a missing key falls back to
per-key fetches; 401, 403, and 429 errors stop the run as in the crawl.
- Moves per-issue conversion into a shared helper used by both crawl and
reindex; unknown, unparseable, or out-of-scope ids yield a
`ConnectorFailure`.

<sup>Written for commit 5448b4451e.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/onyx-dot-app/onyx/pull/16102?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/16102?returnTo=https%3A%2F%2Fgithub.com%2Fonyx-dot-app%2Fonyx%2Fpull%2F16102&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 Jira issue reindexing from issue URLs.
- Supports scoped and unrestricted Jira searches while preserving custom
queries and project filters.
- Rebuilds Jira document content, hierarchy, and access permissions
where available.

- **Bug Fixes**
- Improved handling of invalid, missing, out-of-scope, and malformed
Jira issues.
  - Reports per-issue failures while preserving successful results.
- Handles batch-search fallback, credential errors, permissions, and
rate limits more reliably.

- **Tests**
- Added unit and live integration coverage for Jira reindexing
scenarios.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 01:42:10 +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.