test: use PostgreSQL and live model integrations
This commit is contained in:
@@ -1,17 +1,22 @@
|
||||
import json
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
from PIL import Image, ImageDraw
|
||||
from sqlalchemy import select
|
||||
|
||||
from wisegrade.llm import DeepSeekClient, ModelError, ScoreResult, validate_score
|
||||
from wisegrade.models import AppSetting, KnowledgeChunk
|
||||
from wisegrade.llm import DeepSeekClient, FactResult, ModelError, ScoreResult, validate_score
|
||||
from wisegrade.models import AppSetting, KnowledgeChunk, Project, Student
|
||||
from wisegrade.rag import ingest, local_encoder, retrieve, split_pages
|
||||
from wisegrade.settings_store import model_config, save_model_config
|
||||
|
||||
|
||||
def fake_embed(texts):
|
||||
return [[1.0] + [0.0] * 1023 for _ in texts]
|
||||
def make_project(db, number):
|
||||
student = Student(student_no=f"90000000{number}", name=f"测试学生{number}")
|
||||
db.add(student)
|
||||
db.flush()
|
||||
project = Project(course_version_id=1, student_id=student.id, topic=f"测试项目{number}",
|
||||
source_dir=f"tests/student-{number}")
|
||||
db.add(project)
|
||||
db.flush()
|
||||
return project.id
|
||||
|
||||
|
||||
def test_json_validation_and_evidence():
|
||||
@@ -37,33 +42,28 @@ def test_evidence_quote_resolves_pdf_line_breaks_and_page():
|
||||
assert checked.evidence[0].quote in chunks[0]["text"]
|
||||
|
||||
|
||||
def test_deepseek_adapter_mock_and_key_not_in_error(tmp_path):
|
||||
@pytest.mark.integration
|
||||
def test_deepseek_adapter_live_with_image(tmp_path):
|
||||
image = tmp_path / "page.png"
|
||||
image.write_bytes(b"png fixture")
|
||||
def handler(request):
|
||||
assert request.url.path == "/chat/completions"
|
||||
assert request.headers["Authorization"] == "Bearer fake-secret"
|
||||
content = json.loads(request.content)["messages"][1]["content"]
|
||||
assert content[1]["type"] == "image_url" and content[1]["image_url"]["url"].startswith("data:image/png;base64,")
|
||||
return httpx.Response(200, json={"choices": [{"message": {"content": json.dumps({"criterion_id": "x", "score": 0,
|
||||
"max_score": 10, "reason": "缺失", "evidence": [], "confidence": .2})}}]})
|
||||
client = DeepSeekClient(base_url="https://example.test", api_key="fake-secret", retries=0, transport=httpx.MockTransport(handler))
|
||||
assert client.json_call("prompt", {}, ScoreResult, images=[str(image)]).score == 0
|
||||
bad = DeepSeekClient(base_url="https://example.test", api_key="fake-secret", retries=0,
|
||||
transport=httpx.MockTransport(lambda _: httpx.Response(500, text="fake-secret")))
|
||||
with pytest.raises(ModelError) as error:
|
||||
bad.json_call("prompt", {}, ScoreResult)
|
||||
assert "fake-secret" not in str(error.value)
|
||||
picture = Image.new("RGB", (320, 100), "white")
|
||||
ImageDraw.Draw(picture).text((20, 30), "TEST 123", fill="black")
|
||||
picture.save(image)
|
||||
result = DeepSeekClient().json_call(
|
||||
"从用户提供的JSON中复制topic字段,原样返回到JSON对象的topic字段。",
|
||||
{"topic": "真实模型连通测试"}, FactResult, images=[str(image)])
|
||||
assert result.topic == "真实模型连通测试"
|
||||
|
||||
|
||||
def test_project_isolation_and_chunk_metadata(db):
|
||||
project_a = make_project(db, 1)
|
||||
project_b = make_project(db, 2)
|
||||
pages = [{"page_number": 3, "section": "实验", "text": "机器人实验数据和分析"}]
|
||||
ingest(db, course_version_id=1, project_id=101, submission_file_id=None, title="学生A", category="stage3",
|
||||
source_hash="a", pages=pages, embedder=fake_embed)
|
||||
ingest(db, course_version_id=1, project_id=102, submission_file_id=None, title="学生B", category="stage3",
|
||||
source_hash="b", pages=[{**pages[0], "text": "另一个学生的秘密"}], embedder=fake_embed)
|
||||
hits = retrieve(db, course_version_id=1, project_id=101, category="stage3", query="实验数据", embedder=fake_embed)
|
||||
assert hits and all(hit["project_id"] == 101 and hit["page"] == 3 for hit in hits)
|
||||
ingest(db, course_version_id=1, project_id=project_a, submission_file_id=None, title="学生A", category="stage3",
|
||||
source_hash="a", pages=pages)
|
||||
ingest(db, course_version_id=1, project_id=project_b, submission_file_id=None, title="学生B", category="stage3",
|
||||
source_hash="b", pages=[{**pages[0], "text": "另一个学生的秘密"}])
|
||||
hits = retrieve(db, course_version_id=1, project_id=project_a, category="stage3", query="实验数据")
|
||||
assert hits and all(hit["project_id"] == project_a and hit["page"] == 3 for hit in hits)
|
||||
assert "秘密" not in str(hits)
|
||||
|
||||
|
||||
@@ -76,15 +76,16 @@ def test_short_paragraphs_share_a_page_chunk():
|
||||
|
||||
|
||||
def test_stale_knowledge_index_reuses_document_id(db):
|
||||
project_id = make_project(db, 1)
|
||||
pages = [{"page_number": 1, "text": "实验方案与测量结果"}]
|
||||
document = ingest(db, course_version_id=1, project_id=101, submission_file_id=None,
|
||||
title="报告", category="stage3", source_hash="same-source", pages=pages, embedder=fake_embed)
|
||||
document = ingest(db, course_version_id=1, project_id=project_id, submission_file_id=None,
|
||||
title="报告", category="stage3", source_hash="same-source", pages=pages)
|
||||
old_chunk = db.scalar(select(KnowledgeChunk).where(KnowledgeChunk.document_id == document.id))
|
||||
old_chunk.text = "过时片段"
|
||||
document.index_version = 1
|
||||
db.commit()
|
||||
reindexed = ingest(db, course_version_id=1, project_id=101, submission_file_id=None,
|
||||
title="报告", category="stage3", source_hash="same-source", pages=pages, embedder=fake_embed)
|
||||
reindexed = ingest(db, course_version_id=1, project_id=project_id, submission_file_id=None,
|
||||
title="报告", category="stage3", source_hash="same-source", pages=pages)
|
||||
assert reindexed.id == document.id and reindexed.index_version == 2
|
||||
assert db.scalar(select(KnowledgeChunk.text).where(KnowledgeChunk.document_id == document.id)) == pages[0]["text"]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user