Testing Search¶
Deepstaging provides TestSearchIndex<T> and TestVectorIndex<T> — generic test doubles for full-text and vector search with call recording, seedable documents, and configurable search responses. No mocking libraries needed.
TestSearchIndex\<T>¶
Records every IndexAsync, SearchAsync, RemoveAsync, and ClearAsync call. Seed documents and configure search behavior.
var index = new TestSearchIndex<Article>();
// Seed documents
index.Seed("article-1", new Article("Getting Started", "A beginner's guide..."));
index.Seed("article-2", new Article("Advanced Patterns", "Deep dive into..."));
// ... run your handler that searches ...
// Assert search was performed
await Assert.That(index.SearchCalls).Count().IsEqualTo(1);
await Assert.That(index.SearchCalls[0].Query).IsEqualTo("beginner");
Call Recording¶
| Property | Type | Description |
|---|---|---|
IndexCalls |
IReadOnlyList<(string Id, T Document)> |
All IndexAsync calls |
IndexManyCalls |
IReadOnlyList<IReadOnlyDictionary<string, T>> |
All IndexManyAsync batches |
SearchCalls |
IReadOnlyList<(string Query, SearchOptions? Options)> |
All search queries |
RemoveCalls |
IReadOnlyList<string> |
All IDs passed to RemoveAsync |
ClearCallCount |
int |
Number of ClearAsync calls |
Documents |
IReadOnlyDictionary<string, T> |
Current index contents |
Default Search Behavior¶
By default, SearchAsync returns all seeded and indexed documents with score 1.0:
var index = new TestSearchIndex<Product>();
index.Seed("p1", new Product("Widget"));
index.Seed("p2", new Product("Gadget"));
var result = await index.SearchAsync("anything");
// Returns both documents as hits with score 1.0
Custom Search Responses¶
index.OnSearch = (query, options) => new SearchResult<Article>(
[new SearchHit<Article>("article-1", seededArticle, 0.95)],
TotalCount: 1);
TestVectorIndex\<T>¶
Records every UpsertAsync, SearchAsync, RemoveAsync, and ClearAsync call. Seed documents with embeddings and configure search behavior.
var index = new TestVectorIndex<Article>();
// Seed with embeddings
var embedding = new float[] { 0.1f, 0.2f, 0.3f };
index.Seed("article-1", new Article("Getting Started", "..."), embedding);
// ... run your handler that performs vector search ...
await Assert.That(index.SearchCalls).Count().IsEqualTo(1);
Call Recording¶
| Property | Type | Description |
|---|---|---|
UpsertCalls |
IReadOnlyList<(string Id, T Document, ReadOnlyMemory<float> Embedding)> |
All UpsertAsync calls |
UpsertManyCalls |
IReadOnlyList<IReadOnlyList<VectorDocument<T>>> |
All UpsertManyAsync batches |
SearchCalls |
IReadOnlyList<(ReadOnlyMemory<float> QueryEmbedding, VectorSearchOptions? Options)> |
All search queries |
RemoveCalls |
IReadOnlyList<string> |
All IDs passed to RemoveAsync |
ClearCallCount |
int |
Number of ClearAsync calls |
Documents |
IReadOnlyDictionary<string, (T Document, ReadOnlyMemory<float> Embedding)> |
Current index contents |
Custom Vector Search Responses¶
index.OnSearch = (embedding, options) => new VectorSearchResult<Article>(
[new VectorSearchHit<Article>("article-1", seededArticle, 0.98)],
TotalCount: 1);