Make the first RAG slice executable without risking production data
Some checks failed
verify / verify (push) Has been cancelled

The Stage 1 foundation now proves provider contracts with mocks and validates PostgreSQL/pgvector ingestion, approval binding, retrieval, reranking, and idempotency using only synthetic data. Live Bailian validation remains gated on rotating the exposed key.

Constraint: The key shown in chat is compromised and cannot be used or committed

Rejected: Mark Stage 1 complete from mock and offline results | real three-model smoke is still required

Confidence: high

Scope-risk: moderate

Reversibility: clean

Directive: Do not enable real-data ingestion until Stage 3 cloud approval and outbound manifest controls are enforced end to end

Tested: make verify; 41 pytest tests; strict mypy; Ruff; Compose config; pinned image build; empty-volume migration; role denial; two idempotent 20-vector seeds; database restart persistence

Not-tested: Live Bailian calls require a newly rotated key; React product UI is not implemented
This commit is contained in:
2026-07-12 15:41:58 +08:00
parent ec1acb36b5
commit f4ba5d5342
61 changed files with 6886 additions and 20 deletions

View File

@@ -1 +0,0 @@

View File

@@ -0,0 +1,175 @@
from __future__ import annotations
import json
import httpx
import pytest
from app.adapters.bailian.chat import BailianChatAdapter
from app.ports.model_providers import ChatMessage, ModelProviderError
BASE_URL = "https://workspace.cn-beijing.maas.aliyuncs.com/compatible-mode/v1"
API_KEY = "sk-test-chat-secret"
@pytest.mark.asyncio
async def test_chat_completion_disables_thinking_and_search() -> None:
messages = [ChatMessage(role="user", content="Give a grounded answer")]
def handler(request: httpx.Request) -> httpx.Response:
assert request.url.path == "/compatible-mode/v1/chat/completions"
assert request.headers["Authorization"] == f"Bearer {API_KEY}"
payload = json.loads(request.content)
assert payload["model"] == "deepseek-v4-flash"
assert payload["messages"] == [{"role": "user", "content": "Give a grounded answer"}]
assert payload["enable_thinking"] is False
assert payload["enable_search"] is False
assert payload["stream"] is False
return httpx.Response(
200,
json={
"id": "req_chat_1",
"model": "deepseek-v4-flash",
"choices": [
{
"message": {"role": "assistant", "content": "grounded"},
"finish_reason": "stop",
}
],
"usage": {
"prompt_tokens": 5,
"completion_tokens": 1,
"total_tokens": 6,
},
},
)
async with httpx.AsyncClient(transport=httpx.MockTransport(handler)) as client:
adapter = BailianChatAdapter(
api_key=API_KEY,
base_url=BASE_URL,
http_client=client,
)
result = await adapter.complete(messages, max_tokens=32)
assert result.content == "grounded"
assert result.finish_reason == "stop"
assert result.usage.total_tokens == 6
@pytest.mark.asyncio
async def test_chat_stream_disables_thinking_and_search_and_parses_sse() -> None:
def handler(request: httpx.Request) -> httpx.Response:
payload = json.loads(request.content)
assert payload["enable_thinking"] is False
assert payload["enable_search"] is False
assert payload["stream"] is True
assert payload["stream_options"] == {"include_usage": True}
chunks = (
'data: {"id":"req_stream","model":"deepseek-v4-flash",'
'"choices":[{"delta":{"content":""},"finish_reason":null}]}\n\n'
'data: {"id":"req_stream","model":"deepseek-v4-flash",'
'"choices":[{"delta":{"content":""},"finish_reason":"stop"}]}\n\n'
'data: {"id":"req_stream","model":"deepseek-v4-flash",'
'"choices":[],"usage":{"prompt_tokens":4,"completion_tokens":2,'
'"total_tokens":6}}\n\n'
"data: [DONE]\n\n"
)
return httpx.Response(
200,
headers={"content-type": "text/event-stream"},
content=chunks.encode(),
)
async with httpx.AsyncClient(transport=httpx.MockTransport(handler)) as client:
adapter = BailianChatAdapter(
api_key=API_KEY,
base_url=BASE_URL,
http_client=client,
)
events = [
event
async for event in adapter.stream(
[ChatMessage(role="user", content="question")],
max_tokens=32,
)
]
assert "".join(event.delta for event in events) == "矿体"
assert events[1].finish_reason == "stop"
assert events[2].usage.total_tokens == 6
@pytest.mark.asyncio
async def test_chat_error_does_not_expose_key_or_message_body() -> None:
sensitive_text = "CONFIDENTIAL_PROMPT_BODY"
def handler(_: httpx.Request) -> httpx.Response:
return httpx.Response(
401,
json={
"error": {
"code": API_KEY,
"message": f"{sensitive_text}: {API_KEY}",
}
},
)
async with httpx.AsyncClient(transport=httpx.MockTransport(handler)) as client:
adapter = BailianChatAdapter(
api_key=API_KEY,
base_url=BASE_URL,
http_client=client,
)
with pytest.raises(ModelProviderError) as exc_info:
await adapter.complete(
[ChatMessage(role="user", content=sensitive_text)],
max_tokens=8,
)
rendered = f"{exc_info.value!s} {exc_info.value!r}"
assert API_KEY not in rendered
assert sensitive_text not in rendered
@pytest.mark.asyncio
async def test_chat_stream_honors_retry_after_before_any_token() -> None:
calls = 0
def handler(_: httpx.Request) -> httpx.Response:
nonlocal calls
calls += 1
if calls == 1:
return httpx.Response(
429,
headers={"Retry-After": "0"},
json={"error": {"code": "rate_limited"}},
)
return httpx.Response(
200,
headers={"content-type": "text/event-stream"},
content=(
'data: {"model":"deepseek-v4-flash",'
'"choices":[{"delta":{"content":"正常"},"finish_reason":"stop"}]}\n\n'
"data: [DONE]\n\n"
).encode(),
)
async with httpx.AsyncClient(transport=httpx.MockTransport(handler)) as client:
adapter = BailianChatAdapter(
api_key=API_KEY,
base_url=BASE_URL,
http_client=client,
max_retries=1,
retry_base_seconds=0,
)
events = [
event
async for event in adapter.stream(
[ChatMessage(role="user", content="question")],
max_tokens=8,
)
]
assert "".join(event.delta for event in events) == "正常"
assert calls == 2

View File

@@ -0,0 +1,208 @@
from __future__ import annotations
import json
import httpx
import pytest
from app.adapters.bailian._base import response_model
from app.adapters.bailian.embedding import BailianEmbeddingAdapter
from app.ports.model_providers import ModelProviderError, ProviderErrorKind
BASE_URL = "https://workspace.cn-beijing.maas.aliyuncs.com/compatible-mode/v1"
API_KEY = "sk-test-embedding-secret"
def _vector(marker: float = 1.0) -> list[float]:
return [marker, *([0.0] * 1_023)]
def test_embedding_rejects_non_bailian_endpoint_before_request() -> None:
with pytest.raises(ModelProviderError) as exc_info:
BailianEmbeddingAdapter(
api_key=API_KEY,
base_url="https://attacker.invalid/compatible-mode/v1",
)
assert exc_info.value.kind is ProviderErrorKind.INVALID_REQUEST
assert exc_info.value.provider_code == "invalid_base_url"
def test_response_model_falls_back_when_provider_echoes_sensitive_value() -> None:
assert (
response_model(
{"model": API_KEY},
"text-embedding-v4",
sensitive_values=(API_KEY,),
)
== "text-embedding-v4"
)
@pytest.mark.asyncio
async def test_embedding_uses_compatible_endpoint_and_restores_index_order() -> None:
def handler(request: httpx.Request) -> httpx.Response:
assert request.url.path == "/compatible-mode/v1/embeddings"
assert request.headers["Authorization"] == f"Bearer {API_KEY}"
payload = json.loads(request.content)
assert payload == {
"model": "text-embedding-v4",
"input": ["first", "second"],
"dimensions": 1_024,
"encoding_format": "float",
}
return httpx.Response(
200,
json={
"id": "req_embedding_1",
"model": "text-embedding-v4",
"data": [
{"index": 1, "embedding": _vector(2.0)},
{"index": 0, "embedding": _vector(1.0)},
],
"usage": {"prompt_tokens": 2, "total_tokens": 2},
},
)
async with httpx.AsyncClient(transport=httpx.MockTransport(handler)) as client:
adapter = BailianEmbeddingAdapter(
api_key=API_KEY,
base_url=BASE_URL,
token_counter=len,
http_client=client,
)
result = await adapter.embed_documents(["first", "second"])
assert len(result.vectors) == 2
assert all(len(vector) == 1_024 for vector in result.vectors)
assert result.vectors[0][0] == 1.0
assert result.vectors[1][0] == 2.0
assert result.request_id == "req_embedding_1"
assert result.usage.input_tokens == 2
@pytest.mark.asyncio
async def test_embedding_enforces_10_8192_and_33000_token_boundaries() -> None:
calls = 0
def handler(request: httpx.Request) -> httpx.Response:
nonlocal calls
calls += 1
payload = json.loads(request.content)
return httpx.Response(
200,
json={
"data": [
{"index": index, "embedding": _vector()}
for index, _ in enumerate(payload["input"])
]
},
)
async with httpx.AsyncClient(transport=httpx.MockTransport(handler)) as client:
adapter = BailianEmbeddingAdapter(
api_key=API_KEY,
base_url=BASE_URL,
token_counter=len,
http_client=client,
)
await adapter.embed_documents(["x"] * 10)
await adapter.embed_documents(["x" * 8_192])
await adapter.embed_documents([*(["x" * 8_192] * 4), "x" * 232])
assert calls == 3
invalid_batches = (
["x"] * 11,
["x" * 8_193],
[*(["x" * 8_192] * 4), "x" * 233],
)
for texts in invalid_batches:
with pytest.raises(ModelProviderError) as exc_info:
await adapter.embed_documents(texts)
assert exc_info.value.kind is ProviderErrorKind.INVALID_REQUEST
assert calls == 3
@pytest.mark.asyncio
async def test_embedding_rejects_non_1024_dimensional_response() -> None:
def handler(_: httpx.Request) -> httpx.Response:
return httpx.Response(
200,
json={"data": [{"index": 0, "embedding": [1.0] * 1_023}]},
)
async with httpx.AsyncClient(transport=httpx.MockTransport(handler)) as client:
adapter = BailianEmbeddingAdapter(
api_key=API_KEY,
base_url=BASE_URL,
token_counter=len,
http_client=client,
)
with pytest.raises(ModelProviderError) as exc_info:
await adapter.embed_query("sensitive geological paragraph")
assert exc_info.value.kind is ProviderErrorKind.INVALID_RESPONSE
assert exc_info.value.provider_code == "invalid_embedding_dimensions"
@pytest.mark.asyncio
async def test_embedding_provider_error_does_not_expose_key_or_body() -> None:
sensitive_text = "CONFIDENTIAL_GEOLOGICAL_BODY"
def handler(_: httpx.Request) -> httpx.Response:
return httpx.Response(
400,
json={
"id": sensitive_text,
"error": {
"code": API_KEY,
"message": f"failed for {sensitive_text} using {API_KEY}",
},
},
)
async with httpx.AsyncClient(transport=httpx.MockTransport(handler)) as client:
adapter = BailianEmbeddingAdapter(
api_key=API_KEY,
base_url=BASE_URL,
token_counter=len,
http_client=client,
)
with pytest.raises(ModelProviderError) as exc_info:
await adapter.embed_query(sensitive_text)
rendered = f"{exc_info.value!s} {exc_info.value!r}"
assert API_KEY not in rendered
assert sensitive_text not in rendered
assert exc_info.value.provider_code is None
assert exc_info.value.request_id is None
@pytest.mark.asyncio
async def test_embedding_retries_timeout_with_a_fixed_bound() -> None:
calls = 0
def handler(request: httpx.Request) -> httpx.Response:
nonlocal calls
calls += 1
if calls == 1:
raise httpx.ReadTimeout("simulated timeout", request=request)
return httpx.Response(
200,
json={"data": [{"index": 0, "embedding": _vector()}]},
)
async with httpx.AsyncClient(transport=httpx.MockTransport(handler)) as client:
adapter = BailianEmbeddingAdapter(
api_key=API_KEY,
base_url=BASE_URL,
token_counter=len,
http_client=client,
max_retries=1,
retry_base_seconds=0,
)
result = await adapter.embed_query("query")
assert len(result.vectors[0]) == 1_024
assert calls == 2

View File

@@ -0,0 +1,149 @@
from __future__ import annotations
import json
import httpx
import pytest
from app.adapters.bailian.rerank import BailianRerankerAdapter
from app.ports.model_providers import ModelProviderError, ProviderErrorKind
BASE_URL = "https://workspace.cn-beijing.maas.aliyuncs.com/compatible-api/v1"
API_KEY = "sk-test-rerank-secret"
@pytest.mark.asyncio
async def test_rerank_uses_separate_endpoint_and_maps_result_indices() -> None:
documents = ["copper evidence", "gold evidence", "irrelevant"]
def handler(request: httpx.Request) -> httpx.Response:
assert request.url.path == "/compatible-api/v1/reranks"
assert request.headers["Authorization"] == f"Bearer {API_KEY}"
payload = json.loads(request.content)
assert payload == {
"model": "qwen3-rerank",
"query": "where is gold",
"documents": documents,
"top_n": 2,
"instruct": "rank geological evidence",
}
return httpx.Response(
200,
json={
"id": "req_rerank_1",
"model": "qwen3-rerank",
"results": [
{"index": 1, "relevance_score": 0.91},
{"index": 0, "relevance_score": 0.72},
],
"usage": {"input_tokens": 42, "total_tokens": 42},
},
)
async with httpx.AsyncClient(transport=httpx.MockTransport(handler)) as client:
adapter = BailianRerankerAdapter(
api_key=API_KEY,
base_url=BASE_URL,
token_counter=len,
http_client=client,
)
result = await adapter.rerank(
"where is gold",
documents,
top_n=2,
instruct="rank geological evidence",
)
assert [item.index for item in result.items] == [1, 0]
assert [item.document for item in result.items] == [documents[1], documents[0]]
assert [item.relevance_score for item in result.items] == [0.91, 0.72]
assert result.request_id == "req_rerank_1"
@pytest.mark.asyncio
async def test_rerank_enforces_500_4000_and_repeated_query_120000_formula() -> None:
calls = 0
def handler(_: httpx.Request) -> httpx.Response:
nonlocal calls
calls += 1
return httpx.Response(
200,
json={"results": [{"index": 0, "relevance_score": 1.0}]},
)
async with httpx.AsyncClient(transport=httpx.MockTransport(handler)) as client:
adapter = BailianRerankerAdapter(
api_key=API_KEY,
base_url=BASE_URL,
token_counter=len,
http_client=client,
)
await adapter.rerank("q" * 4_000, ["d" * 4_000], top_n=1)
exactly_120000 = ["d" * 40] * 500
await adapter.rerank("q" * 200, exactly_120000, top_n=1)
assert calls == 2
invalid_requests = (
("q" * 4_001, ["d"]),
("q", ["d" * 4_001]),
("q", ["d"] * 501),
("q" * 200, [*(["d" * 40] * 499), "d" * 41]),
)
for query, documents in invalid_requests:
with pytest.raises(ModelProviderError) as exc_info:
await adapter.rerank(query, documents, top_n=1)
assert exc_info.value.kind is ProviderErrorKind.INVALID_REQUEST
assert calls == 2
@pytest.mark.asyncio
async def test_rerank_rejects_invalid_provider_index() -> None:
def handler(_: httpx.Request) -> httpx.Response:
return httpx.Response(
200,
json={"results": [{"index": 2, "relevance_score": 0.9}]},
)
async with httpx.AsyncClient(transport=httpx.MockTransport(handler)) as client:
adapter = BailianRerankerAdapter(
api_key=API_KEY,
base_url=BASE_URL,
token_counter=len,
http_client=client,
)
with pytest.raises(ModelProviderError) as exc_info:
await adapter.rerank("query", ["only document"], top_n=1)
assert exc_info.value.kind is ProviderErrorKind.INVALID_RESPONSE
assert exc_info.value.provider_code == "invalid_rerank_index"
@pytest.mark.asyncio
async def test_rerank_retries_5xx_with_a_fixed_bound() -> None:
calls = 0
def handler(_: httpx.Request) -> httpx.Response:
nonlocal calls
calls += 1
if calls == 1:
return httpx.Response(503, json={"error": {"code": "temporarily_unavailable"}})
return httpx.Response(
200,
json={"results": [{"index": 0, "relevance_score": 0.9}]},
)
async with httpx.AsyncClient(transport=httpx.MockTransport(handler)) as client:
adapter = BailianRerankerAdapter(
api_key=API_KEY,
base_url=BASE_URL,
token_counter=len,
http_client=client,
max_retries=1,
retry_base_seconds=0,
)
result = await adapter.rerank("query", ["document"], top_n=1)
assert result.items[0].index == 0
assert calls == 2

View File

@@ -1 +0,0 @@

View File

@@ -0,0 +1,183 @@
from __future__ import annotations
import re
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[3]
BACKEND = ROOT / "backend"
sys.path.insert(0, str(BACKEND))
from app.persistence.job_queue_sql import ( # noqa: E402
CLAIM_JOB_SQL,
COMPLETE_JOB_SQL,
FAIL_OR_RETRY_JOB_SQL,
HEARTBEAT_JOB_SQL,
REAP_EXPIRED_JOBS_SQL,
)
COMPOSE = (ROOT / "compose.yaml").read_text(encoding="utf-8")
BOOTSTRAP = (ROOT / "ops/postgres/init/10-bootstrap-rag.sh").read_text(encoding="utf-8")
MIGRATION = (BACKEND / "migrations/versions/0001_initial_schema.py").read_text(encoding="utf-8")
def _service_block(name: str) -> str:
pattern = re.compile(rf"(?ms)^ {re.escape(name)}:\n(.*?)(?=^ [a-zA-Z0-9_-]+:\n|\Z)")
match = pattern.search(COMPOSE)
assert match is not None, f"missing Compose service: {name}"
return match.group(1)
def test_compose_isolates_database_credentials_and_networks() -> None:
db = _service_block("db")
migrate = _service_block("migrate")
provider_smoke = _service_block("provider-smoke")
seed_demo = _service_block("seed-demo")
seed_demo_offline = _service_block("seed-demo-offline")
assert "postgres_bootstrap_password" in db
assert "postgres_migrator_password" in db
assert "postgres_app_password" in db
assert "postgres_migrator_password" in migrate
assert "postgres_bootstrap_password" not in migrate
assert "postgres_app_password" not in migrate
assert "bailian_api_key" in provider_smoke
assert "postgres_" not in provider_smoke
assert "postgres_app_password" in seed_demo
assert "postgres_bootstrap_password" not in seed_demo
assert "postgres_migrator_password" not in seed_demo
assert "bailian_api_key" in seed_demo
assert "./data/samples/public:/demo:ro" in seed_demo
assert "postgres_app_password" in seed_demo_offline
assert "bailian_api_key" not in seed_demo_offline
assert "DEMO_PROVIDER_MODE: fake" in seed_demo_offline
assert "./data/samples/public:/demo:ro" in seed_demo_offline
assert re.search(r"(?ms)^ data:\n.*?^ internal: true$", COMPOSE)
assert re.search(r"(?m)^ edge:$", COMPOSE)
assert re.search(r"(?m)^ egress:$", COMPOSE)
def test_database_health_requires_tcp_and_atomic_bootstrap_sentinel() -> None:
db = _service_block("db")
assert "pg_isready -h 127.0.0.1 -p 5432" in db
assert ".rag-bootstrap-complete" in db
assert "CREATE EXTENSION IF NOT EXISTS vector" in BOOTSTRAP
assert 'CREATE ROLE :"migrator_user"' in BOOTSTRAP
assert 'CREATE ROLE :"app_user"' in BOOTSTRAP
assert "NOSUPERUSER" in BOOTSTRAP
assert "CREATE SCHEMA rag AUTHORIZATION" in BOOTSTRAP
assert "ALTER DEFAULT PRIVILEGES" in BOOTSTRAP
assert BOOTSTRAP.index("COMMIT;") < BOOTSTRAP.index("mv -f")
assert "\\getenv migrator_password RAG_MIGRATOR_PASSWORD" in BOOTSTRAP
assert "--set=migrator_password" not in BOOTSTRAP
assert "--set=app_password" not in BOOTSTRAP
assert "set -x" not in BOOTSTRAP
assert "official postgres entrypoint resolves POSTGRES_PASSWORD_FILE" in BOOTSTRAP
assert 'bootstrap_password="${POSTGRES_PASSWORD:-}"' in BOOTSTRAP
def test_initial_migration_has_vector_and_activation_contracts() -> None:
normalized = " ".join(MIGRATION.lower().split())
assert "embedding vector(1024)" in normalized
assert "using hnsw (embedding vector_cosine_ops)" in normalized
assert "where searchable" in normalized
assert "chunks_searchable_requires_ready_approved_embedding" in normalized
assert "check (embedding_dimension = 1024)" in normalized
assert "active_version_id uuid" in normalized
assert "documents_active_version_fk" in normalized
assert "deferrable initially deferred" in normalized
assert "foreign key (id, active_version_id)" in normalized
assert "references rag.document_versions (document_id, id)" in normalized
assert "local_parsed_pending_cloud_review" in normalized
assert "cloud_approved" in normalized
assert "outbound_manifest_sha256" in normalized
assert "cloud_processing_allowed" not in normalized
assert "document_versions_cloud_approval_bound" in normalized
assert "chunks_approval_binding_fk" in normalized
assert "create table rag.outbound_manifest_items" in normalized
assert "chunks_manifest_item_binding_fk" in normalized
assert "outbound_manifest_items_immutable_after_approval" in normalized
assert "chunks_guard_approved_mutation" in normalized
assert "chunks_guard_ready_vector_update" in normalized
assert "new.access_scope_id is distinct from old.access_scope_id" in normalized
assert "new.document_version_id is distinct from old.document_version_id" in normalized
assert "cloud_text text not null" in normalized
assert "cloud_text_sha256 char(64) not null" in normalized
assert "embedding_text = embedding_prefix || cloud_text" in normalized
assert "sha256(convert_to(cloud_text, 'utf8'))" in normalized
assert "sha256(convert_to(embedding_text, 'utf8'))" in normalized
assert "approval_status = 'cloud_approved'" in normalized
assert "document_versions_revoke_cloud_approval" in normalized
assert "new.review_state is distinct from old.review_state" in normalized
assert "set searchable = false" in normalized
assert "approval_status = revoked_state" in normalized
assert "embedding = null" in normalized
assert "embedding_profile_hash = null" in normalized
assert "where document_version_id = old.id" in normalized
assert "chunks_enforce_active_search_projection" in normalized
assert "document.active_version_id = new.document_version_id" in normalized
assert "version.status = 'ready'" in normalized
assert "documents_enforce_activation" in normalized
def test_downgrade_drops_manifest_items_before_document_versions() -> None:
normalized = " ".join(MIGRATION.lower().split())
manifest_drop = normalized.index("drop table if exists rag.outbound_manifest_items")
version_drop = normalized.index("drop table if exists rag.document_versions")
assert manifest_drop < version_drop
def test_initial_migration_has_fenced_job_schema_and_indexes() -> None:
normalized = " ".join(MIGRATION.lower().split())
assert "create table rag.background_jobs" in normalized
assert "lease_owner text" in normalized
assert "lease_token uuid" in normalized
assert "lease_until timestamptz" in normalized
assert "attempt <= max_attempts" in normalized
assert "background_jobs_lease_consistent" in normalized
assert "unique (job_type, idempotency_key)" in normalized
assert "background_jobs_claim_queued" in normalized
assert "background_jobs_reap_expired" in normalized
def test_job_claim_and_terminal_updates_are_fenced() -> None:
claim = " ".join(CLAIM_JOB_SQL.lower().split())
heartbeat = " ".join(HEARTBEAT_JOB_SQL.lower().split())
complete = " ".join(COMPLETE_JOB_SQL.lower().split())
failure = " ".join(FAIL_OR_RETRY_JOB_SQL.lower().split())
assert "for update skip locked" in claim
assert "lease_token = gen_random_uuid()" in claim
assert "attempt = job.attempt + 1" in claim
for statement in (heartbeat, complete, failure):
assert "job.status = 'running'" in statement
assert "job.lease_owner = :worker_id" in statement
assert "job.lease_token = :lease_token" in statement
assert "returning" in statement
assert "when job.attempt < job.max_attempts then 'queued'" in failure
assert "else 'failed'" in failure
def test_reaper_uses_advisory_lock_and_handles_exhausted_attempts() -> None:
reaper = " ".join(REAP_EXPIRED_JOBS_SQL.lower().split())
assert "pg_try_advisory_xact_lock" in reaper
assert "job.status = 'running'" in reaper
assert "job.lease_until < now()" in reaper
assert "for update of job skip locked" in reaper
assert "when job.attempt < job.max_attempts then 'queued'" in reaper
assert "else 'failed'" in reaper
assert "lease_owner = null" in reaper
assert "lease_token = null" in reaper
assert "lease_until = null" in reaper

View File

@@ -1 +0,0 @@

View File

@@ -0,0 +1,74 @@
from pathlib import Path
import pytest
from app.core.config import Settings
from app.core.secrets import SecretFileError, read_secret_file
def test_secret_file_strips_single_trailing_newline(tmp_path: Path) -> None:
secret_path = tmp_path / "secret"
secret_path.write_text("test-only-value\n", encoding="utf-8")
assert read_secret_file(secret_path) == "test-only-value"
def test_secret_error_never_contains_secret_value(tmp_path: Path) -> None:
secret_path = tmp_path / "secret"
secret_path.write_text("first\nsecond\n", encoding="utf-8")
with pytest.raises(SecretFileError) as captured:
read_secret_file(secret_path)
assert "first" not in str(captured.value)
assert "second" not in str(captured.value)
def test_database_url_reads_password_from_file(tmp_path: Path) -> None:
password_path = tmp_path / "postgres_password"
password_path.write_text("local-test-password", encoding="utf-8")
settings = Settings(postgres_password_file=password_path)
url = settings.database_url()
assert url.password == "local-test-password"
assert "local-test-password" not in str(url)
def test_base_urls_drop_trailing_slashes() -> None:
settings = Settings(
bailian_openai_base_url="https://example.invalid/compatible-mode/v1/",
bailian_native_base_url="https://example.invalid/api/v1/",
bailian_rerank_base_url="https://example.invalid/compatible-api/v1/",
)
assert settings.bailian_openai_base_url.endswith("/v1")
assert settings.bailian_rerank_base_url.endswith("/v1")
def test_live_endpoints_must_share_one_beijing_workspace(tmp_path: Path) -> None:
key_path = tmp_path / "key"
key_path.write_text("test-key-value", encoding="utf-8")
settings = Settings(
dashscope_api_key_file=key_path,
bailian_openai_base_url=(
"https://workspace-a.cn-beijing.maas.aliyuncs.com/compatible-mode/v1"
),
bailian_rerank_base_url=(
"https://workspace-b.cn-beijing.maas.aliyuncs.com/compatible-api/v1"
),
)
with pytest.raises(ValueError, match="same workspace"):
settings.bailian_api_key()
def test_live_endpoint_rejects_non_bailian_host_before_reading_key(tmp_path: Path) -> None:
settings = Settings(
dashscope_api_key_file=tmp_path / "missing",
bailian_openai_base_url="https://attacker.invalid/compatible-mode/v1",
bailian_rerank_base_url="https://attacker.invalid/compatible-api/v1",
)
with pytest.raises(ValueError, match="approved Beijing"):
settings.bailian_api_key()

View File

@@ -0,0 +1,37 @@
import json
from pathlib import Path
from typing import Any
PROJECT_ROOT = Path(__file__).resolve().parents[3]
SAMPLE_ROOT = PROJECT_ROOT / "data" / "samples" / "public"
def load_jsonl(path: Path) -> list[dict[str, Any]]:
return [json.loads(line) for line in path.read_text(encoding="utf-8").splitlines()]
def test_demo_corpus_is_synthetic_pending_hash_bound_approval_and_unique() -> None:
documents = load_jsonl(SAMPLE_ROOT / "demo_documents.jsonl")
assert len(documents) == 20
assert len({document["doc_id"] for document in documents}) == 20
assert all(document["source_type"] == "synthetic" for document in documents)
assert all(
document["review_state"] == "LOCAL_PARSED_PENDING_CLOUD_REVIEW" for document in documents
)
assert all(document["cloud_policy_id"] == "synthetic-demo-v1" for document in documents)
assert all("cloud_approved" not in document for document in documents)
assert all(
"虚构" in document["content"] or "演示" in document["content"] for document in documents
)
def test_demo_queries_only_reference_existing_documents() -> None:
documents = load_jsonl(SAMPLE_ROOT / "demo_documents.jsonl")
queries = load_jsonl(SAMPLE_ROOT / "demo_queries.jsonl")
document_ids = {document["doc_id"] for document in documents}
assert len(queries) == 10
assert len({query["qid"] for query in queries}) == 10
assert all(set(query["expected_doc_ids"]) <= document_ids for query in queries)
assert any(not query["answerable"] and not query["expected_doc_ids"] for query in queries)

View File

@@ -0,0 +1,47 @@
import math
import pytest
from app.adapters.fake import FakeEmbeddingProvider, FakeReranker
from app.ports.model_providers import ModelProviderError
@pytest.mark.asyncio
async def test_fake_embedding_is_deterministic_normalized_and_1024_dimensional() -> None:
provider = FakeEmbeddingProvider()
first = await provider.embed_documents(["斑岩铜矿 钾化 绢英岩化"])
second = await provider.embed_query("斑岩铜矿 钾化 绢英岩化")
assert first.vectors[0] == second.vectors[0]
assert len(first.vectors[0]) == 1024
assert math.isclose(sum(value * value for value in first.vectors[0]), 1.0)
@pytest.mark.asyncio
async def test_fake_reranker_preserves_original_candidate_index() -> None:
reranker = FakeReranker()
documents = ["煤层测井对比", "斑岩铜矿钾化带", "铝土矿含矿层"]
result = await reranker.rerank("斑岩铜矿的钾化带", documents, top_n=2)
assert result.items[0].index == 1
assert result.items[0].document == documents[1]
assert result.items[0].relevance_score >= result.items[1].relevance_score
@pytest.mark.asyncio
async def test_fake_providers_reject_inputs_the_live_contract_rejects() -> None:
embedder = FakeEmbeddingProvider()
reranker = FakeReranker()
with pytest.raises(ModelProviderError):
await embedder.embed_documents([])
with pytest.raises(ModelProviderError):
await embedder.embed_documents(["x"] * 11)
with pytest.raises(ModelProviderError):
await reranker.rerank("", ["document"], top_n=1)
with pytest.raises(ModelProviderError):
await reranker.rerank("query", [], top_n=1)
with pytest.raises(ModelProviderError):
await reranker.rerank("query", ["document"], top_n=0)

View File

@@ -0,0 +1,30 @@
import httpx
import pytest
from app.main import app
@pytest.mark.asyncio
async def test_liveness_does_not_require_database_or_model_credentials() -> None:
transport = httpx.ASGITransport(app=app)
async with httpx.AsyncClient(transport=transport, base_url="http://test") as client:
response = await client.get("/api/v1/health/live")
assert response.status_code == 200
assert response.json()["status"] == "ok"
@pytest.mark.asyncio
async def test_meta_exposes_model_names_but_no_credentials() -> None:
transport = httpx.ASGITransport(app=app)
async with httpx.AsyncClient(transport=transport, base_url="http://test") as client:
response = await client.get("/api/v1/meta")
assert response.status_code == 200
payload = response.json()
assert payload["models"] == {
"embedding": "text-embedding-v4",
"rerank": "qwen3-rerank",
"generation": "deepseek-v4-flash",
}
assert "key" not in str(payload).lower()

View File

@@ -0,0 +1,50 @@
import json
from pathlib import Path
from typing import Any
import pytest
from app.adapters.fake import FakeEmbeddingProvider, FakeReranker
PROJECT_ROOT = Path(__file__).resolve().parents[3]
SAMPLE_ROOT = PROJECT_ROOT / "data" / "samples" / "public"
def load_jsonl(path: Path) -> list[dict[str, Any]]:
return [json.loads(line) for line in path.read_text(encoding="utf-8").splitlines()]
def cosine(left: tuple[float, ...], right: tuple[float, ...]) -> float:
return sum(
left_value * right_value for left_value, right_value in zip(left, right, strict=True)
)
@pytest.mark.asyncio
async def test_synthetic_questions_retrieve_expected_document_after_rerank() -> None:
documents = load_jsonl(SAMPLE_ROOT / "demo_documents.jsonl")
queries = load_jsonl(SAMPLE_ROOT / "demo_queries.jsonl")
embedder = FakeEmbeddingProvider()
reranker = FakeReranker()
document_texts = [f"{item['title']}\n{item['content']}" for item in documents]
document_vectors: list[tuple[float, ...]] = []
for offset in range(0, len(document_texts), 10):
batch = await embedder.embed_documents(document_texts[offset : offset + 10])
document_vectors.extend(batch.vectors)
hits = 0
answerable_queries = [query for query in queries if query["answerable"]]
for query in answerable_queries:
query_vector = (await embedder.embed_query(query["query"])).vectors[0]
candidate_indexes = sorted(
range(len(documents)),
key=lambda index: cosine(query_vector, document_vectors[index]),
reverse=True,
)[:5]
candidate_texts = [document_texts[index] for index in candidate_indexes]
reranked = await reranker.rerank(query["query"], candidate_texts, top_n=3)
result_ids = [documents[candidate_indexes[item.index]]["doc_id"] for item in reranked.items]
if set(query["expected_doc_ids"]) & set(result_ids):
hits += 1
assert hits / len(answerable_queries) >= 0.8

View File

@@ -0,0 +1,26 @@
from app.ports.model_providers import ModelProviderError, ProviderErrorKind
from app.tools.provider_smoke import failed_probe
def test_failed_probe_only_exposes_sanitized_provider_fields() -> None:
error = ModelProviderError(
operation="embedding.create",
kind=ProviderErrorKind.AUTHENTICATION,
status_code=401,
provider_code="invalid_api_key",
request_id="req-safe-1",
)
result = failed_probe("embedding", error)
assert result.error_kind == "authentication"
assert result.status_code == 401
assert result.request_id == "req-safe-1"
assert "invalid_api_key" not in repr(result)
def test_unexpected_error_is_reduced_to_fixed_category() -> None:
result = failed_probe("chat", RuntimeError("sensitive content"))
assert result.error_kind == "internal_contract_error"
assert "sensitive content" not in repr(result)

View File

@@ -0,0 +1,38 @@
import subprocess
from pathlib import Path
PROJECT_ROOT = Path(__file__).resolve().parents[3]
SCANNER = PROJECT_ROOT / "scripts" / "check-secrets.sh"
def run_git(repository: Path, *arguments: str) -> None:
subprocess.run(
["git", *arguments],
cwd=repository,
check=True,
capture_output=True,
text=True,
)
def test_staged_scan_reads_index_blob_not_modified_worktree(tmp_path: Path) -> None:
run_git(tmp_path, "init", "--quiet")
run_git(tmp_path, "config", "user.email", "test@example.invalid")
run_git(tmp_path, "config", "user.name", "Secret Scanner Test")
candidate = tmp_path / "config.txt"
candidate.write_text("sk-" + "A" * 30, encoding="utf-8")
run_git(tmp_path, "add", "config.txt")
candidate.write_text("safe working-tree replacement", encoding="utf-8")
result = subprocess.run(
[str(SCANNER), "--staged"],
cwd=tmp_path,
check=False,
capture_output=True,
text=True,
)
assert result.returncode == 1
assert "config.txt" in result.stderr
assert "safe working-tree replacement" not in result.stderr
assert "A" * 30 not in result.stderr

View File

@@ -0,0 +1,54 @@
from pathlib import Path
import pytest
from app.adapters.fake import FakeEmbeddingProvider
from app.core.config import Settings
from app.tools.seed_demo import (
embed_in_batches,
embedding_profile_hash,
load_documents,
prepare_chunks,
)
PROJECT_ROOT = Path(__file__).resolve().parents[3]
DOCUMENTS_PATH = PROJECT_ROOT / "data" / "samples" / "public" / "demo_documents.jsonl"
@pytest.mark.asyncio
async def test_seed_preparation_batches_and_hash_binds_twenty_documents() -> None:
documents = load_documents(DOCUMENTS_PATH)
settings = Settings()
provider = FakeEmbeddingProvider()
texts = [
f"标题:{item.title}\n地区:{item.region}\n矿种:{item.mineral}\n正文:{item.content}"
for item in documents
]
vectors, model = await embed_in_batches(provider, texts)
chunks = prepare_chunks(
documents,
vectors,
profile_hash=embedding_profile_hash(settings, "fake"),
embedding_model=model,
)
assert len(chunks) == 20
assert all(len(item.vector) == 1024 for item in chunks)
assert all(item.embedding_text == item.embedding_prefix + item.cloud_text for item in chunks)
assert len({item.chunk_id for item in chunks}) == 20
assert all(len(item.outbound_manifest_sha256) == 64 for item in chunks)
def test_seed_rejects_fixture_not_explicitly_marked_synthetic(tmp_path: Path) -> None:
path = tmp_path / "documents.jsonl"
path.write_text(
'{"doc_id":"x","title":"x","content":"x","region":"x",'
'"mineral":"x","page_no":1,"source_type":"unknown",'
'"review_state":"LOCAL_PARSED_PENDING_CLOUD_REVIEW",'
'"cloud_policy_id":"synthetic-demo-v1"}\n',
encoding="utf-8",
)
with pytest.raises(ValueError, match="non_synthetic_document"):
load_documents(path)