c889a57b6b
Test Suites / Build CI Environment (push) Has been cancelled
Test Suites / Basic Tests (push) Has been cancelled
Test Suites / End-to-End Tests (push) Has been cancelled
Test Suites / CLI Tests (push) Has been cancelled
Test Suites / Slow End-to-End Tests (push) Has been cancelled
Test Suites / Graph Database Tests (push) Has been cancelled
Test Suites / Vector DB Tests (push) Has been cancelled
Test Suites / Temporal Graph Test (push) Has been cancelled
Test Suites / Search Test on Different DBs (push) Has been cancelled
Test Suites / Example Tests (push) Has been cancelled
Test Suites / Notebook Tests (push) Has been cancelled
Test Suites / OS and Python Tests Ubuntu (push) Has been cancelled
Test Suites / OS and Python Tests Extended (push) Has been cancelled
Test Suites / LLM Test Suite (push) Has been cancelled
Test Suites / S3 File Storage Test (push) Has been cancelled
Test Suites / Run Integration Tests (push) Has been cancelled
Test Suites / MCP Tests (push) Has been cancelled
Test Suites / Docker Compose Test (push) Has been cancelled
Test Suites / Docker CI test (push) Has been cancelled
Test Suites / Relational DB Migration Tests (push) Has been cancelled
Test Suites / Distributed Cognee Test (push) Has been cancelled
Test Suites / DB Examples Tests (push) Has been cancelled
Test Suites / Test Completion Status (push) Has been cancelled
Test Suites / Claude Code Review (push) Has been cancelled
Test Suites / basic checks (push) Has been cancelled
build | Build and Push Cognee MCP Docker Image to dockerhub / docker-build-and-push (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
build | Build and Push Docker Image to dockerhub / docker-build-and-push (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges Core Functionality (3.11) (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges Core Functionality (3.12) (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges with Different Graph Databases (kuzu, kuzu) (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges with Different Graph Databases (neo4j, neo4j) (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges Examples (push) Has been cancelled
Weighted Edges Tests / Code Quality for Weighted Edges (push) Has been cancelled
134 lines
5.5 KiB
Python
134 lines
5.5 KiB
Python
"""End-to-end test: two background cognify runs on one dataset, each isolated to its own file.
|
|
|
|
Flow (exactly as a caller would do it):
|
|
add(file_one) -> cognify(run_in_background=True) # run #1 starts, returns immediately
|
|
add(file_two) -> cognify(run_in_background=True) # run #2 starts, returns immediately
|
|
gather both runs to completion
|
|
|
|
The per-dataset asyncio lock in `run_pipeline_per_dataset` serializes everything
|
|
touching the dataset, so the two runs cannot clobber each other. We assert, purely
|
|
from each run's return value (its PipelineRunCompleted.data_ingestion_info):
|
|
|
|
* both runs complete successfully (no errored run),
|
|
* they are two distinct pipeline runs, and
|
|
* run #1 cognified ONLY file_one and run #2 cognified ONLY file_two
|
|
(incremental loading skips file_one on the second run).
|
|
|
|
The two files are intentionally a bit larger than a single sentence so cognify #1
|
|
is still processing in the background when we add the second file and start
|
|
cognify #2 — i.e. we actually exercise the lock's contention window.
|
|
|
|
Uses the local default databases (SQLite / LanceDB / file-based graph) via
|
|
`config.*_root_directory` + prune, and a real LLM (the credentials CI provides
|
|
through secrets). No mocking.
|
|
"""
|
|
|
|
import asyncio
|
|
import pathlib
|
|
|
|
import pytest
|
|
|
|
import cognee
|
|
from cognee.modules.engine.operations.setup import setup as setup_databases
|
|
from cognee.modules.pipelines.models.PipelineRunInfo import (
|
|
PipelineRunCompleted,
|
|
PipelineRunErrored,
|
|
)
|
|
from cognee.modules.pipelines.queues.pipeline_run_info_queues import get_from_queue
|
|
|
|
|
|
POLL_INTERVAL_SECONDS = 1
|
|
|
|
|
|
def _make_document(subject: str, paragraphs: int = 40) -> str:
|
|
"""Build a distinct, multi-paragraph document so cognify produces several chunks."""
|
|
return "\n\n".join(
|
|
f"{subject} note {i + 1}: In context {i + 1}, {subject} is associated with a "
|
|
f"distinct development number {i + 1}. This particular detail about {subject} "
|
|
f"is documented as item {i + 1} and connects to topic {i + 1} in measurable ways."
|
|
for i in range(paragraphs)
|
|
)
|
|
|
|
|
|
async def _await_run_completion(pipeline_run_id) -> PipelineRunCompleted:
|
|
"""Drain a background run's info queue until it reaches a terminal status.
|
|
|
|
No timeout on purpose: real LLM calls are slow and not always stable, so a
|
|
deadline here would only produce flaky failures. We wait for the run.
|
|
"""
|
|
while True:
|
|
info = get_from_queue(pipeline_run_id)
|
|
if info is None:
|
|
await asyncio.sleep(POLL_INTERVAL_SECONDS)
|
|
continue
|
|
if isinstance(info, PipelineRunErrored):
|
|
raise AssertionError(f"Run {pipeline_run_id} errored: {info!r}.")
|
|
if isinstance(info, PipelineRunCompleted):
|
|
return info
|
|
# Non-terminal yields are ignored; keep draining.
|
|
|
|
|
|
def _started_run_id(started, label: str):
|
|
"""Extract the pipeline_run_id from a background cognify's return value."""
|
|
assert isinstance(started, dict) and started, (
|
|
f"{label}: background cognify must return a non-empty "
|
|
f"{{dataset_id: PipelineRunStarted}} map, got {started!r}."
|
|
)
|
|
return next(iter(started.values())).pipeline_run_id
|
|
|
|
|
|
def _processed_data_ids(completed: PipelineRunCompleted) -> set:
|
|
"""Data ids the run actually cognified (PipelineRunCompleted items, not skipped ones)."""
|
|
processed = set()
|
|
for item in completed.data_ingestion_info or []:
|
|
if isinstance(item.get("run_info"), PipelineRunCompleted):
|
|
processed.add(str(item["data_id"]))
|
|
return processed
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_two_background_cognify_runs_are_isolated_per_file():
|
|
test_root = pathlib.Path(__file__).parent
|
|
cognee.config.data_root_directory(
|
|
str((test_root / ".data_storage" / "test_concurrent_cognify").resolve())
|
|
)
|
|
cognee.config.system_root_directory(
|
|
str((test_root / ".cognee_system" / "test_concurrent_cognify").resolve())
|
|
)
|
|
|
|
await cognee.prune.prune_data()
|
|
await cognee.prune.prune_system(metadata=True)
|
|
await setup_databases()
|
|
|
|
dataset_name = "concurrent_cognify_e2e"
|
|
|
|
# ---- First file: add, then cognify in the background -------------------
|
|
add_one = await cognee.add(_make_document("Alan Turing"), dataset_name)
|
|
data_id_one = str(add_one.data_ingestion_info[0]["data_id"])
|
|
started_one = await cognee.cognify([dataset_name], run_in_background=True)
|
|
run_id_one = _started_run_id(started_one, "cognify #1")
|
|
|
|
# ---- Second file: add NEW file, then a second background cognify -------
|
|
# (cognify #1 is still processing under the per-dataset lock here.)
|
|
add_two = await cognee.add(_make_document("Ada Lovelace"), dataset_name)
|
|
data_id_two = str(add_two.data_ingestion_info[0]["data_id"])
|
|
started_two = await cognee.cognify([dataset_name], run_in_background=True)
|
|
run_id_two = _started_run_id(started_two, "cognify #2")
|
|
|
|
assert data_id_one != data_id_two, "The two adds must produce two distinct files."
|
|
assert run_id_one != run_id_two, "Each cognify must be its own pipeline run."
|
|
|
|
# ---- Gather both background runs to completion -------------------------
|
|
completed_one, completed_two = await asyncio.gather(
|
|
_await_run_completion(run_id_one),
|
|
_await_run_completion(run_id_two),
|
|
)
|
|
|
|
# ---- Each run cognified exactly its own file --------------------------
|
|
assert _processed_data_ids(completed_one) == {data_id_one}, (
|
|
"cognify #1 must have processed only the first file."
|
|
)
|
|
assert _processed_data_ids(completed_two) == {data_id_two}, (
|
|
"cognify #2 must have processed only the second file."
|
|
)
|