Skip to content

Commit 8907ae1

Browse files
committed
added non lf ingestion test
1 parent d7decc5 commit 8907ae1

1 file changed

Lines changed: 197 additions & 0 deletions

File tree

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
import asyncio
2+
import os
3+
from pathlib import Path
4+
from unittest.mock import AsyncMock, patch
5+
6+
import httpx
7+
import pytest
8+
9+
10+
# Pop cached modules so they reload with modified env vars
11+
def _purge_modules():
12+
import sys
13+
14+
for mod in [
15+
"api.router",
16+
"api.connector_router",
17+
"config.settings",
18+
"auth_middleware",
19+
"main",
20+
"api",
21+
"services",
22+
"services.search_service",
23+
"services.default_docs_service",
24+
"services.startup_orchestrator",
25+
"app.routes.internal",
26+
"app.routes",
27+
"app.container",
28+
"app.factory",
29+
"app.lifespan",
30+
"dependencies",
31+
"utils.opensearch_init",
32+
]:
33+
sys.modules.pop(mod, None)
34+
35+
36+
async def wait_for_service_ready(client: httpx.AsyncClient, timeout_s: float = 30.0):
37+
deadline = asyncio.get_event_loop().time() + timeout_s
38+
last_err = None
39+
while asyncio.get_event_loop().time() < deadline:
40+
try:
41+
r1 = await client.get("/auth/me")
42+
if r1.status_code != 200:
43+
await asyncio.sleep(0.5)
44+
continue
45+
r2 = await client.post("/search", json={"query": "*", "limit": 0})
46+
if r2.status_code == 200:
47+
return
48+
last_err = r2.text
49+
except Exception as e:
50+
last_err = str(e)
51+
await asyncio.sleep(0.5)
52+
raise AssertionError(f"Service not ready in time: {last_err}")
53+
54+
55+
async def wait_for_task_completion(
56+
client: httpx.AsyncClient, task_id: str, timeout_s: float = 60.0
57+
) -> dict:
58+
deadline = asyncio.get_event_loop().time() + timeout_s
59+
last_payload = None
60+
while asyncio.get_event_loop().time() < deadline:
61+
resp = await client.get(f"/tasks/{task_id}")
62+
if resp.status_code == 200:
63+
try:
64+
data = resp.json()
65+
except Exception:
66+
last_payload = resp.text
67+
else:
68+
status = (data.get("status") or "").lower()
69+
if status == "completed":
70+
return data
71+
if status == "failed":
72+
raise AssertionError(f"Task {task_id} failed: {data}")
73+
last_payload = data
74+
await asyncio.sleep(1.0)
75+
raise AssertionError(f"Task {task_id} did not complete in time. Last payload: {last_payload}")
76+
77+
78+
@pytest.mark.asyncio
79+
async def test_non_langflow_csv_ingestion_with_splitting(tmp_path: Path):
80+
"""Validate standard CSV ingestion using standard non-Langflow processing pipeline.
81+
82+
Simulates parsing of a CSV file yielding a large table chunk (exceeding standard 8,000 token limit),
83+
verifying it gets split correctly and indexed into OpenSearch.
84+
"""
85+
os.environ["DISABLE_INGEST_WITH_LANGFLOW"] = "true"
86+
os.environ["DISABLE_STARTUP_INGEST"] = "true"
87+
os.environ["EMBEDDING_MODEL"] = "text-embedding-3-small"
88+
os.environ["EMBEDDING_PROVIDER"] = "openai"
89+
os.environ["GOOGLE_OAUTH_CLIENT_ID"] = ""
90+
os.environ["GOOGLE_OAUTH_CLIENT_SECRET"] = ""
91+
92+
_purge_modules()
93+
94+
from config.settings import clients, get_index_name
95+
from main import create_app, startup_tasks
96+
97+
await clients.initialize()
98+
try:
99+
await clients.opensearch.indices.delete(index=get_index_name())
100+
await asyncio.sleep(1)
101+
except Exception:
102+
pass
103+
104+
app = await create_app()
105+
await startup_tasks(app.state.services)
106+
107+
from main import _ensure_opensearch_index
108+
109+
await _ensure_opensearch_index()
110+
111+
# Mock the DoclingService conversion result
112+
# We want a table chunk whose token count exceeds 8,000 tokens (so it gets split)
113+
# "testlargephrase " is 3 tokens in cl100k_base. Repeating it 4000 times gives ~12,000 tokens.
114+
large_table_text = "testlargephrase " * 4000
115+
116+
mock_docling_result = {
117+
"origin": {
118+
"binary_hash": "sha-csv-integration-123",
119+
"filename": "correlation_report.csv",
120+
"mimetype": "text/csv",
121+
},
122+
"texts": [],
123+
"tables": [
124+
{
125+
"prov": [{"page_no": 1}],
126+
"data": {
127+
"table_cells": [
128+
{
129+
"start_row_offset_idx": 0,
130+
"start_col_offset_idx": 0,
131+
"text": large_table_text,
132+
}
133+
]
134+
},
135+
}
136+
],
137+
}
138+
139+
# Patch convert_file method on DoclingService class
140+
from services.docling_service import DoclingService
141+
142+
with patch.object(DoclingService, "convert_file", AsyncMock(return_value=mock_docling_result)):
143+
transport = httpx.ASGITransport(app=app)
144+
async with httpx.AsyncClient(transport=transport, base_url="http://testserver") as client:
145+
await wait_for_service_ready(client)
146+
147+
# Create mock CSV
148+
csv_path = tmp_path / "correlation_report.csv"
149+
csv_path.write_text("header1,header2\nvalue1,value2")
150+
151+
files = {
152+
"file": (
153+
csv_path.name,
154+
csv_path.read_bytes(),
155+
"text/csv",
156+
)
157+
}
158+
159+
resp = await client.post("/router/upload_ingest", files=files)
160+
assert resp.status_code == 202, resp.text
161+
162+
data = resp.json()
163+
task_id = data.get("task_id")
164+
assert task_id is not None
165+
166+
# Wait for processing to complete
167+
await wait_for_task_completion(client, task_id)
168+
169+
# Wait for search indices to refresh
170+
await asyncio.sleep(1)
171+
172+
# Retrieve results from OpenSearch using search endpoint
173+
search_resp = await client.post(
174+
"/search",
175+
json={"query": "testlargephrase", "limit": 10},
176+
)
177+
assert search_resp.status_code == 200, search_resp.text
178+
179+
results = search_resp.json().get("results", [])
180+
assert len(results) > 0, "No search results returned from indexed chunks"
181+
182+
# Check direct OpenSearch doc count for this document ID
183+
opensearch_resp = await clients.opensearch.search(
184+
index=get_index_name(),
185+
body={"query": {"term": {"document_id": "sha-csv-integration-123"}}},
186+
)
187+
hits = opensearch_resp.get("hits", {}).get("hits", [])
188+
189+
# Since the text is ~12,000 tokens, and max_tokens=8000,
190+
# it should be split into 2 chunks, resulting in 2 documents in OpenSearch.
191+
assert len(hits) == 2, f"Expected 2 indexed chunks, but got {len(hits)}"
192+
for hit in hits:
193+
source = hit.get("_source", {})
194+
assert source.get("document_id") == "sha-csv-integration-123"
195+
assert source.get("filename") == "correlation_report.csv"
196+
assert source.get("mimetype") == "text/csv"
197+
assert "testlargephrase" in source.get("text", "")

0 commit comments

Comments
 (0)