forked from HAL9000/cleveragents-core
31472b5413
Add Behave feature/step pairs that exercise uncovered branches across handlers, LSP, CLI, and service layers to reach the coverage gate. ISSUES CLOSED: #1232
343 lines
11 KiB
Python
343 lines
11 KiB
Python
"""Step definitions for fvcov3 - FAISS vector backend coverage round 3."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
from unittest.mock import MagicMock
|
|
|
|
from behave import given, then, when
|
|
|
|
from cleveragents.application.services.faiss_vector_backend import (
|
|
FAISSVectorBackend,
|
|
FAISSVectorIndexBackend,
|
|
build_vector_backend,
|
|
build_vector_index_backend,
|
|
)
|
|
from cleveragents.domain.models.acms.index_stubs import InMemoryVectorIndexBackend
|
|
from cleveragents.domain.models.acms.stubs import InMemoryVectorBackend
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helpers
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def _parse_vector(raw: str) -> list[float]:
|
|
return [float(part.strip()) for part in raw.split(",") if part.strip()]
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Givens
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@given("fvcov3 a mock vector store service")
|
|
def step_fvcov3_mock_service(context: Any) -> None:
|
|
mock_svc = MagicMock()
|
|
mock_svc.acms_search_by_vector.return_value = []
|
|
mock_svc.acms_index_embedding.return_value = None
|
|
mock_svc.acms_backend_name.return_value = "faiss"
|
|
context.fvcov3_service = mock_svc
|
|
context.fvcov3_error = None
|
|
|
|
|
|
@given("fvcov3 a FAISSVectorBackend backed by it")
|
|
def step_fvcov3_read_backend(context: Any) -> None:
|
|
context.fvcov3_read_backend = FAISSVectorBackend(context.fvcov3_service)
|
|
|
|
|
|
@given("fvcov3 a FAISSVectorIndexBackend backed by it")
|
|
def step_fvcov3_write_backend(context: Any) -> None:
|
|
context.fvcov3_write_backend = FAISSVectorIndexBackend(context.fvcov3_service)
|
|
|
|
|
|
@given("fvcov3 a mock vector store service that returns hits without doc_id")
|
|
def step_fvcov3_mock_service_no_docid(context: Any) -> None:
|
|
mock_svc = MagicMock()
|
|
mock_svc.acms_search_by_vector.return_value = [
|
|
{
|
|
"content": "some text",
|
|
"score": 0.95,
|
|
"metadata": {"other_key": "val"}, # no doc_id
|
|
},
|
|
{
|
|
"content": "another",
|
|
"score": 0.90,
|
|
"metadata": {"doc_id": ""}, # empty doc_id
|
|
},
|
|
]
|
|
context.fvcov3_service = mock_svc
|
|
context.fvcov3_error = None
|
|
|
|
|
|
@given("fvcov3 a mock vector store service that returns hits with low scores")
|
|
def step_fvcov3_mock_service_low_scores(context: Any) -> None:
|
|
mock_svc = MagicMock()
|
|
mock_svc.acms_search_by_vector.return_value = [
|
|
{
|
|
"content": "low score hit",
|
|
"score": 0.2,
|
|
"metadata": {"doc_id": "doc1"},
|
|
},
|
|
{
|
|
"content": "another low",
|
|
"score": 0.3,
|
|
"metadata": {"doc_id": "doc2"},
|
|
},
|
|
]
|
|
context.fvcov3_service = mock_svc
|
|
context.fvcov3_error = None
|
|
|
|
|
|
@given("fvcov3 a mock vector store service that returns search hits without doc_id")
|
|
def step_fvcov3_mock_service_search_no_docid(context: Any) -> None:
|
|
mock_svc = MagicMock()
|
|
mock_svc.acms_search_by_vector.return_value = [
|
|
{
|
|
"content": "hit text",
|
|
"score": 0.95,
|
|
"metadata": {"some_key": "val"}, # no doc_id
|
|
},
|
|
{
|
|
"content": "hit2",
|
|
"score": 0.90,
|
|
"metadata": {"doc_id": ""}, # empty doc_id
|
|
},
|
|
]
|
|
context.fvcov3_service = mock_svc
|
|
context.fvcov3_error = None
|
|
|
|
|
|
@given('fvcov3 a mock vector store service with backend name "{name}"')
|
|
def step_fvcov3_mock_service_backend_name(context: Any, name: str) -> None:
|
|
mock_svc = MagicMock()
|
|
mock_svc.acms_backend_name.return_value = name
|
|
context.fvcov3_service = mock_svc
|
|
context.fvcov3_error = None
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Whens
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@when(
|
|
'fvcov3 I index embedding with project "{project}" and doc_id "{doc_id}" and vector "{vector}"'
|
|
)
|
|
def step_fvcov3_index_embedding(
|
|
context: Any, project: str, doc_id: str, vector: str
|
|
) -> None:
|
|
try:
|
|
context.fvcov3_write_backend.index_embedding(
|
|
project,
|
|
doc_id,
|
|
_parse_vector(vector),
|
|
{"location": "src/foo.py"},
|
|
)
|
|
context.fvcov3_error = None
|
|
except (ValueError, Exception) as exc:
|
|
context.fvcov3_error = exc
|
|
|
|
|
|
@when(
|
|
'fvcov3 I index embedding with empty project and doc_id "{doc_id}" and vector "{vector}"'
|
|
)
|
|
def step_fvcov3_index_embedding_empty_project(
|
|
context: Any, doc_id: str, vector: str
|
|
) -> None:
|
|
try:
|
|
context.fvcov3_write_backend.index_embedding(
|
|
"",
|
|
doc_id,
|
|
_parse_vector(vector),
|
|
{"location": "src/foo.py"},
|
|
)
|
|
context.fvcov3_error = None
|
|
except (ValueError, Exception) as exc:
|
|
context.fvcov3_error = exc
|
|
|
|
|
|
@when("fvcov3 I similarity search with empty embedding")
|
|
def step_fvcov3_sim_search_empty_embedding(context: Any) -> None:
|
|
try:
|
|
context.fvcov3_read_backend.similarity_search(
|
|
[],
|
|
scope=frozenset({"res1"}),
|
|
top_k=5,
|
|
)
|
|
context.fvcov3_error = None
|
|
except ValueError as exc:
|
|
context.fvcov3_error = exc
|
|
|
|
|
|
@when("fvcov3 I similarity search with top_k {top_k:d}")
|
|
def step_fvcov3_sim_search_bad_topk(context: Any, top_k: int) -> None:
|
|
try:
|
|
context.fvcov3_read_backend.similarity_search(
|
|
[1.0, 0.0],
|
|
scope=frozenset({"res1"}),
|
|
top_k=top_k,
|
|
)
|
|
context.fvcov3_error = None
|
|
except ValueError as exc:
|
|
context.fvcov3_error = exc
|
|
|
|
|
|
@when("fvcov3 I perform a valid similarity search")
|
|
def step_fvcov3_valid_sim_search(context: Any) -> None:
|
|
context.fvcov3_sim_results = context.fvcov3_read_backend.similarity_search(
|
|
[1.0, 0.0],
|
|
scope=frozenset({"res1"}),
|
|
top_k=10,
|
|
)
|
|
|
|
|
|
@when(
|
|
'fvcov3 I index embedding with project "{project}" and doc_id "{doc_id}" and empty embedding'
|
|
)
|
|
def step_fvcov3_index_empty_embedding(context: Any, project: str, doc_id: str) -> None:
|
|
try:
|
|
context.fvcov3_write_backend.index_embedding(
|
|
project,
|
|
doc_id,
|
|
[],
|
|
{"location": "src/foo.py"},
|
|
)
|
|
context.fvcov3_error = None
|
|
except ValueError as exc:
|
|
context.fvcov3_error = exc
|
|
|
|
|
|
@when(
|
|
'fvcov3 I index embedding with metadata having resource_type "{rtype}" but no location'
|
|
)
|
|
def step_fvcov3_index_with_resource_type(context: Any, rtype: str) -> None:
|
|
context.fvcov3_write_backend.index_embedding(
|
|
"proj",
|
|
"doc1",
|
|
[1.0, 0.0],
|
|
{"resource_type": rtype},
|
|
)
|
|
# Capture the content= kwarg passed to acms_index_embedding
|
|
call_args = context.fvcov3_service.acms_index_embedding.call_args
|
|
context.fvcov3_indexed_content = call_args.kwargs.get("content") or call_args[
|
|
1
|
|
].get("content")
|
|
|
|
|
|
@when('fvcov3 I index embedding with empty metadata for doc_id "{doc_id}"')
|
|
def step_fvcov3_index_empty_metadata(context: Any, doc_id: str) -> None:
|
|
context.fvcov3_write_backend.index_embedding(
|
|
"proj",
|
|
doc_id,
|
|
[1.0, 0.0],
|
|
{},
|
|
)
|
|
call_args = context.fvcov3_service.acms_index_embedding.call_args
|
|
context.fvcov3_indexed_content = call_args.kwargs.get("content") or call_args[
|
|
1
|
|
].get("content")
|
|
|
|
|
|
@when('fvcov3 I search similar with empty query embedding in project "{project}"')
|
|
def step_fvcov3_search_similar_empty(context: Any, project: str) -> None:
|
|
try:
|
|
context.fvcov3_write_backend.search_similar(
|
|
project,
|
|
[],
|
|
limit=5,
|
|
)
|
|
context.fvcov3_error = None
|
|
except ValueError as exc:
|
|
context.fvcov3_error = exc
|
|
|
|
|
|
@when('fvcov3 I search similar with limit {limit:d} in project "{project}"')
|
|
def step_fvcov3_search_similar_bad_limit(
|
|
context: Any, limit: int, project: str
|
|
) -> None:
|
|
try:
|
|
context.fvcov3_write_backend.search_similar(
|
|
project,
|
|
[1.0, 0.0],
|
|
limit=limit,
|
|
)
|
|
context.fvcov3_error = None
|
|
except ValueError as exc:
|
|
context.fvcov3_error = exc
|
|
|
|
|
|
@when('fvcov3 I search similar with min_relevance {min_rel} in project "{project}"')
|
|
def step_fvcov3_search_similar_bad_min_relevance(
|
|
context: Any, min_rel: str, project: str
|
|
) -> None:
|
|
min_relevance = float(min_rel)
|
|
try:
|
|
results = context.fvcov3_write_backend.search_similar(
|
|
project,
|
|
[1.0, 0.0],
|
|
limit=5,
|
|
min_relevance=min_relevance,
|
|
)
|
|
context.fvcov3_search_results = results
|
|
context.fvcov3_error = None
|
|
except ValueError as exc:
|
|
context.fvcov3_error = exc
|
|
|
|
|
|
@when("fvcov3 I call build_vector_backend")
|
|
def step_fvcov3_call_build_vector_backend(context: Any) -> None:
|
|
context.fvcov3_build_result = build_vector_backend(context.fvcov3_service)
|
|
|
|
|
|
@when("fvcov3 I call build_vector_index_backend")
|
|
def step_fvcov3_call_build_vector_index_backend(context: Any) -> None:
|
|
context.fvcov3_build_result = build_vector_index_backend(context.fvcov3_service)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Thens
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@then('fvcov3 the error message should contain "{fragment}"')
|
|
def step_fvcov3_assert_error_message(context: Any, fragment: str) -> None:
|
|
assert context.fvcov3_error is not None, "Expected an error but none was raised"
|
|
assert fragment in str(context.fvcov3_error), (
|
|
f"Expected '{fragment}' in error message, got: {context.fvcov3_error}"
|
|
)
|
|
|
|
|
|
@then("fvcov3 the similarity search results should be empty")
|
|
def step_fvcov3_assert_sim_results_empty(context: Any) -> None:
|
|
assert context.fvcov3_sim_results == [], (
|
|
f"Expected empty results, got {context.fvcov3_sim_results}"
|
|
)
|
|
|
|
|
|
@then('fvcov3 the indexed content should be "{expected}"')
|
|
def step_fvcov3_assert_indexed_content(context: Any, expected: str) -> None:
|
|
assert context.fvcov3_indexed_content == expected, (
|
|
f"Expected content '{expected}', got '{context.fvcov3_indexed_content}'"
|
|
)
|
|
|
|
|
|
@then("fvcov3 the search similar results should be empty")
|
|
def step_fvcov3_assert_search_similar_empty(context: Any) -> None:
|
|
assert context.fvcov3_search_results == [], (
|
|
f"Expected empty results, got {context.fvcov3_search_results}"
|
|
)
|
|
|
|
|
|
@then("fvcov3 the result should be an InMemoryVectorBackend")
|
|
def step_fvcov3_assert_inmemory_vector(context: Any) -> None:
|
|
assert isinstance(context.fvcov3_build_result, InMemoryVectorBackend), (
|
|
f"Expected InMemoryVectorBackend, got {type(context.fvcov3_build_result)}"
|
|
)
|
|
|
|
|
|
@then("fvcov3 the result should be an InMemoryVectorIndexBackend")
|
|
def step_fvcov3_assert_inmemory_index(context: Any) -> None:
|
|
assert isinstance(context.fvcov3_build_result, InMemoryVectorIndexBackend), (
|
|
f"Expected InMemoryVectorIndexBackend, got {type(context.fvcov3_build_result)}"
|
|
)
|