test: rename SearchSpace -> Workspace across tests + fixtures (Phase 2 Wave F)

Apply the same rename to surfsense_backend/tests: workspace_id fields/vars,
Workspace* classes/schemas, table name searchspaces -> workspaces in raw SQL,
and the API URL spellings -> /workspaces. Preserves the carve-out wire literals
tests assert (Celery task names, OTel key "search_space.id").
This commit is contained in:
CREDO23 2026-06-26 18:36:46 +02:00
parent 56826a63bc
commit ca9bd28934
124 changed files with 1269 additions and 1269 deletions

View file

@ -18,7 +18,7 @@ from app.agents.chat.multi_agent_chat.shared.retrieval.hybrid_search import (
)
from app.agents.chat.multi_agent_chat.shared.retrieval.models import SearchScope
from app.config import config
from app.db import Chunk, Document, DocumentType, SearchSpace
from app.db import Chunk, Document, DocumentType, Workspace
pytestmark = pytest.mark.integration
@ -35,7 +35,7 @@ def _axis(index: int) -> list[float]:
async def _add_document(
db_session,
*,
search_space_id: int,
workspace_id: int,
title: str = "Doc",
document_type: DocumentType = DocumentType.FILE,
state: str = "ready",
@ -47,7 +47,7 @@ async def _add_document(
document_type=document_type,
content="\n".join(content for content, _, _ in chunks),
content_hash=uuid.uuid4().hex,
search_space_id=search_space_id,
workspace_id=workspace_id,
status={"state": state},
)
db_session.add(document)
@ -65,17 +65,17 @@ async def _add_document(
return document
async def test_keyword_relevant_document_is_retrieved(db_session, db_search_space):
async def test_keyword_relevant_document_is_retrieved(db_session, db_workspace):
document = await _add_document(
db_session,
search_space_id=db_search_space.id,
workspace_id=db_workspace.id,
title="Asyncio Guide",
chunks=[("The asyncio library enables concurrency.", 0, _axis(0))],
)
results = await search_chunks(
db_session,
search_space_id=db_search_space.id,
workspace_id=db_workspace.id,
query="asyncio",
scope=SearchScope(),
top_k=5,
@ -85,23 +85,23 @@ async def test_keyword_relevant_document_is_retrieved(db_session, db_search_spac
assert document.id in {hit.document_id for hit in results}
async def test_semantically_closest_document_ranks_first(db_session, db_search_space):
async def test_semantically_closest_document_ranks_first(db_session, db_workspace):
aligned = await _add_document(
db_session,
search_space_id=db_search_space.id,
workspace_id=db_workspace.id,
title="Background Work",
chunks=[("Parallel execution of background work.", 0, _axis(0))],
)
await _add_document(
db_session,
search_space_id=db_search_space.id,
workspace_id=db_workspace.id,
title="Dessert",
chunks=[("Recipes for chocolate cake.", 0, _axis(1))],
)
results = await search_chunks(
db_session,
search_space_id=db_search_space.id,
workspace_id=db_workspace.id,
query="asynchronous coroutines",
scope=SearchScope(),
top_k=5,
@ -111,25 +111,25 @@ async def test_semantically_closest_document_ranks_first(db_session, db_search_s
assert results[0].document_id == aligned.id
async def test_results_stay_within_the_search_space(db_session, db_search_space):
other_space = SearchSpace(name="Other Space", user_id=db_search_space.user_id)
async def test_results_stay_within_the_workspace(db_session, db_workspace):
other_space = Workspace(name="Other Space", user_id=db_workspace.user_id)
db_session.add(other_space)
await db_session.flush()
mine = await _add_document(
db_session,
search_space_id=db_search_space.id,
workspace_id=db_workspace.id,
chunks=[("Shared keyword asyncio here.", 0, _axis(0))],
)
foreign = await _add_document(
db_session,
search_space_id=other_space.id,
workspace_id=other_space.id,
chunks=[("Shared keyword asyncio here.", 0, _axis(0))],
)
results = await search_chunks(
db_session,
search_space_id=db_search_space.id,
workspace_id=db_workspace.id,
query="asyncio",
scope=SearchScope(),
top_k=5,
@ -140,21 +140,21 @@ async def test_results_stay_within_the_search_space(db_session, db_search_space)
assert mine.id in found and foreign.id not in found
async def test_document_ids_scope_pins_results(db_session, db_search_space):
async def test_document_ids_scope_pins_results(db_session, db_workspace):
pinned = await _add_document(
db_session,
search_space_id=db_search_space.id,
workspace_id=db_workspace.id,
chunks=[("asyncio appears in the pinned doc.", 0, _axis(0))],
)
await _add_document(
db_session,
search_space_id=db_search_space.id,
workspace_id=db_workspace.id,
chunks=[("asyncio appears in the other doc too.", 0, _axis(0))],
)
results = await search_chunks(
db_session,
search_space_id=db_search_space.id,
workspace_id=db_workspace.id,
query="asyncio",
scope=SearchScope(document_ids=(pinned.id,)),
top_k=5,
@ -164,22 +164,22 @@ async def test_document_ids_scope_pins_results(db_session, db_search_space):
assert {hit.document_id for hit in results} == {pinned.id}
async def test_deleting_documents_are_excluded(db_session, db_search_space):
async def test_deleting_documents_are_excluded(db_session, db_workspace):
ready = await _add_document(
db_session,
search_space_id=db_search_space.id,
workspace_id=db_workspace.id,
chunks=[("asyncio in a ready document.", 0, _axis(0))],
)
deleting = await _add_document(
db_session,
search_space_id=db_search_space.id,
workspace_id=db_workspace.id,
state="deleting",
chunks=[("asyncio in a deleting document.", 0, _axis(0))],
)
results = await search_chunks(
db_session,
search_space_id=db_search_space.id,
workspace_id=db_workspace.id,
query="asyncio",
scope=SearchScope(),
top_k=5,
@ -190,12 +190,12 @@ async def test_deleting_documents_are_excluded(db_session, db_search_space):
assert ready.id in found and deleting.id not in found
async def test_matched_chunks_are_ordered_for_reading(db_session, db_search_space):
async def test_matched_chunks_are_ordered_for_reading(db_session, db_workspace):
# Insert out of order, and give the later-position chunk the stronger
# semantic score, so reading order differs from both insertion and score.
document = await _add_document(
db_session,
search_space_id=db_search_space.id,
workspace_id=db_workspace.id,
chunks=[
("asyncio paragraph two.", 1, _axis(0)),
("asyncio paragraph one.", 0, _axis(50)),
@ -204,7 +204,7 @@ async def test_matched_chunks_are_ordered_for_reading(db_session, db_search_spac
results = await search_chunks(
db_session,
search_space_id=db_search_space.id,
workspace_id=db_workspace.id,
query="asyncio",
scope=SearchScope(),
top_k=5,
@ -215,18 +215,18 @@ async def test_matched_chunks_are_ordered_for_reading(db_session, db_search_spac
assert [chunk.position for chunk in hit.chunks] == [0, 1]
async def test_top_k_caps_the_number_of_documents(db_session, db_search_space):
async def test_top_k_caps_the_number_of_documents(db_session, db_workspace):
for index in range(3):
await _add_document(
db_session,
search_space_id=db_search_space.id,
workspace_id=db_workspace.id,
title=f"Doc {index}",
chunks=[(f"asyncio mentioned in doc {index}.", 0, _axis(index))],
)
results = await search_chunks(
db_session,
search_space_id=db_search_space.id,
workspace_id=db_workspace.id,
query="asyncio",
scope=SearchScope(),
top_k=2,

View file

@ -48,7 +48,7 @@ def _axis(index: int) -> list[float]:
async def _add_document(
db_session,
*,
search_space_id: int,
workspace_id: int,
title: str,
text: str,
folder_id: int | None = None,
@ -58,7 +58,7 @@ async def _add_document(
document_type=DocumentType.FILE,
content=text,
content_hash=uuid.uuid4().hex,
search_space_id=search_space_id,
workspace_id=workspace_id,
folder_id=folder_id,
status={"state": "ready"},
)
@ -71,8 +71,8 @@ async def _add_document(
return document
async def _add_folder(db_session, *, search_space_id: int, name: str = "Folder"):
folder = Folder(name=name, position="0", search_space_id=search_space_id)
async def _add_folder(db_session, *, workspace_id: int, name: str = "Folder"):
folder = Folder(name=name, position="0", workspace_id=workspace_id)
db_session.add(folder)
await db_session.flush()
return folder
@ -109,15 +109,15 @@ def _mentions(*, document_ids=(), folder_ids=()):
async def test_tool_returns_retrieved_context_with_numbered_passages(
db_session, db_search_space, _tool_uses_test_session, _pinned_embedding
db_session, db_workspace, _tool_uses_test_session, _pinned_embedding
):
await _add_document(
db_session,
search_space_id=db_search_space.id,
workspace_id=db_workspace.id,
title="Asyncio Guide",
text="The asyncio library enables concurrency.",
)
tool = create_search_knowledge_base_tool(search_space_id=db_search_space.id)
tool = create_search_knowledge_base_tool(workspace_id=db_workspace.id)
result = await _invoke(tool, "asyncio")
@ -129,15 +129,15 @@ async def test_tool_returns_retrieved_context_with_numbered_passages(
async def test_tool_populates_citation_registry_on_state(
db_session, db_search_space, _tool_uses_test_session, _pinned_embedding
db_session, db_workspace, _tool_uses_test_session, _pinned_embedding
):
await _add_document(
db_session,
search_space_id=db_search_space.id,
workspace_id=db_workspace.id,
title="Asyncio Guide",
text="The asyncio library enables concurrency.",
)
tool = create_search_knowledge_base_tool(search_space_id=db_search_space.id)
tool = create_search_knowledge_base_tool(workspace_id=db_workspace.id)
result = await _invoke(tool, "asyncio")
@ -147,15 +147,15 @@ async def test_tool_populates_citation_registry_on_state(
async def test_tool_reuses_existing_registry_numbering(
db_session, db_search_space, _tool_uses_test_session, _pinned_embedding
db_session, db_workspace, _tool_uses_test_session, _pinned_embedding
):
await _add_document(
db_session,
search_space_id=db_search_space.id,
workspace_id=db_workspace.id,
title="Asyncio Guide",
text="The asyncio library enables concurrency.",
)
tool = create_search_knowledge_base_tool(search_space_id=db_search_space.id)
tool = create_search_knowledge_base_tool(workspace_id=db_workspace.id)
first = await _invoke(tool, "asyncio")
carried = first.update["citation_registry"]
@ -166,9 +166,9 @@ async def test_tool_reuses_existing_registry_numbering(
async def test_tool_reports_no_matches_without_touching_state(
db_session, db_search_space, _tool_uses_test_session, _pinned_embedding
db_session, db_workspace, _tool_uses_test_session, _pinned_embedding
):
tool = create_search_knowledge_base_tool(search_space_id=db_search_space.id)
tool = create_search_knowledge_base_tool(workspace_id=db_workspace.id)
result = await _invoke(tool, "nonexistent-term-zzz")
@ -177,9 +177,9 @@ async def test_tool_reports_no_matches_without_touching_state(
async def test_tool_rejects_empty_query(
db_search_space, _tool_uses_test_session, _pinned_embedding
db_workspace, _tool_uses_test_session, _pinned_embedding
):
tool = create_search_knowledge_base_tool(search_space_id=db_search_space.id)
tool = create_search_knowledge_base_tool(workspace_id=db_workspace.id)
result = await _invoke(tool, " ")
@ -188,21 +188,21 @@ async def test_tool_rejects_empty_query(
async def test_document_mention_confines_search_to_pinned_doc(
db_session, db_search_space, _tool_uses_test_session, _pinned_embedding
db_session, db_workspace, _tool_uses_test_session, _pinned_embedding
):
pinned = await _add_document(
db_session,
search_space_id=db_search_space.id,
workspace_id=db_workspace.id,
title="Pinned",
text="asyncio appears in the pinned doc.",
)
await _add_document(
db_session,
search_space_id=db_search_space.id,
workspace_id=db_workspace.id,
title="Other",
text="asyncio appears in the other doc.",
)
tool = create_search_knowledge_base_tool(search_space_id=db_search_space.id)
tool = create_search_knowledge_base_tool(workspace_id=db_workspace.id)
result = await _invoke(tool, "asyncio", context=_mentions(document_ids=[pinned.id]))
@ -213,23 +213,23 @@ async def test_document_mention_confines_search_to_pinned_doc(
async def test_folder_mention_confines_search_to_folder_documents(
db_session, db_search_space, _tool_uses_test_session, _pinned_embedding
db_session, db_workspace, _tool_uses_test_session, _pinned_embedding
):
folder = await _add_folder(db_session, search_space_id=db_search_space.id)
folder = await _add_folder(db_session, workspace_id=db_workspace.id)
await _add_document(
db_session,
search_space_id=db_search_space.id,
workspace_id=db_workspace.id,
title="Inside",
text="asyncio appears inside the folder.",
folder_id=folder.id,
)
await _add_document(
db_session,
search_space_id=db_search_space.id,
workspace_id=db_workspace.id,
title="Outside",
text="asyncio appears outside the folder.",
)
tool = create_search_knowledge_base_tool(search_space_id=db_search_space.id)
tool = create_search_knowledge_base_tool(workspace_id=db_workspace.id)
result = await _invoke(tool, "asyncio", context=_mentions(folder_ids=[folder.id]))
@ -240,7 +240,7 @@ async def test_folder_mention_confines_search_to_folder_documents(
async def test_document_mention_via_state_confines_search(
db_session, db_search_space, _tool_uses_test_session, _pinned_embedding
db_session, db_workspace, _tool_uses_test_session, _pinned_embedding
):
"""The real subagent path: mentions arrive on ``runtime.state`` (no context).
@ -250,17 +250,17 @@ async def test_document_mention_via_state_confines_search(
"""
pinned = await _add_document(
db_session,
search_space_id=db_search_space.id,
workspace_id=db_workspace.id,
title="Pinned",
text="asyncio appears in the pinned doc.",
)
await _add_document(
db_session,
search_space_id=db_search_space.id,
workspace_id=db_workspace.id,
title="Other",
text="asyncio appears in the other doc.",
)
tool = create_search_knowledge_base_tool(search_space_id=db_search_space.id)
tool = create_search_knowledge_base_tool(workspace_id=db_workspace.id)
result = await _invoke(
tool,
@ -275,24 +275,24 @@ async def test_document_mention_via_state_confines_search(
async def test_folder_mention_via_state_confines_search(
db_session, db_search_space, _tool_uses_test_session, _pinned_embedding
db_session, db_workspace, _tool_uses_test_session, _pinned_embedding
):
"""Folder pins delivered via state (subagent path) scope to the folder's docs."""
folder = await _add_folder(db_session, search_space_id=db_search_space.id)
folder = await _add_folder(db_session, workspace_id=db_workspace.id)
await _add_document(
db_session,
search_space_id=db_search_space.id,
workspace_id=db_workspace.id,
title="Inside",
text="asyncio appears inside the folder.",
folder_id=folder.id,
)
await _add_document(
db_session,
search_space_id=db_search_space.id,
workspace_id=db_workspace.id,
title="Outside",
text="asyncio appears outside the folder.",
)
tool = create_search_knowledge_base_tool(search_space_id=db_search_space.id)
tool = create_search_knowledge_base_tool(workspace_id=db_workspace.id)
result = await _invoke(
tool,
@ -307,22 +307,22 @@ async def test_folder_mention_via_state_confines_search(
async def test_state_mentions_take_precedence_over_context(
db_session, db_search_space, _tool_uses_test_session, _pinned_embedding
db_session, db_workspace, _tool_uses_test_session, _pinned_embedding
):
"""When both carry pins, state wins (the forwarded subagent pin is authoritative)."""
state_doc = await _add_document(
db_session,
search_space_id=db_search_space.id,
workspace_id=db_workspace.id,
title="StatePinned",
text="asyncio appears in the state-pinned doc.",
)
context_doc = await _add_document(
db_session,
search_space_id=db_search_space.id,
workspace_id=db_workspace.id,
title="ContextPinned",
text="asyncio appears in the context-pinned doc.",
)
tool = create_search_knowledge_base_tool(search_space_id=db_search_space.id)
tool = create_search_knowledge_base_tool(workspace_id=db_workspace.id)
result = await _invoke(
tool,

View file

@ -35,18 +35,18 @@ def _last_ai_text(messages: list) -> str | None:
@pytest.mark.asyncio
async def test_agent_runs_a_scripted_text_turn(db_session, db_user, db_search_space):
async def test_agent_runs_a_scripted_text_turn(db_session, db_user, db_workspace):
"""A freshly assembled agent streams a scripted final-text turn to completion."""
harness = build_scripted_harness(turns=[ScriptedTurn(text="done")])
agent = await create_multi_agent_chat_deep_agent(
llm=harness.model,
search_space_id=db_search_space.id,
workspace_id=db_workspace.id,
db_session=db_session,
connector_service=ConnectorService(db_session),
checkpointer=InMemorySaver(),
user_id=str(db_user.id),
thread_id=db_search_space.id,
thread_id=db_workspace.id,
agent_config=None,
)
@ -59,7 +59,7 @@ async def test_agent_runs_a_scripted_text_turn(db_session, db_user, db_search_sp
@pytest.mark.asyncio
async def test_agent_routes_a_scripted_tool_call(db_session, db_user, db_search_space):
async def test_agent_routes_a_scripted_tool_call(db_session, db_user, db_workspace):
"""The compiled graph routes a model tool call to its tool and resumes."""
harness = build_scripted_harness(
turns=[
@ -79,12 +79,12 @@ async def test_agent_routes_a_scripted_tool_call(db_session, db_user, db_search_
agent = await create_multi_agent_chat_deep_agent(
llm=harness.model,
search_space_id=db_search_space.id,
workspace_id=db_workspace.id,
db_session=db_session,
connector_service=ConnectorService(db_session),
checkpointer=InMemorySaver(),
user_id=str(db_user.id),
thread_id=db_search_space.id,
thread_id=db_workspace.id,
agent_config=None,
additional_tools=harness.tools,
)
@ -101,7 +101,7 @@ async def test_agent_routes_a_scripted_tool_call(db_session, db_user, db_search_
@pytest.mark.asyncio
async def test_agent_checkpoint_round_trips_across_turns(
db_session, db_user, db_search_space
db_session, db_user, db_workspace
):
"""Turn 2 sees turn 1's history, proving the checkpoint serializes and reloads.
@ -118,12 +118,12 @@ async def test_agent_checkpoint_round_trips_across_turns(
async def _build():
return await create_multi_agent_chat_deep_agent(
llm=harness.model,
search_space_id=db_search_space.id,
workspace_id=db_workspace.id,
db_session=db_session,
connector_service=ConnectorService(db_session),
checkpointer=checkpointer,
user_id=str(db_user.id),
thread_id=db_search_space.id,
thread_id=db_workspace.id,
agent_config=None,
)

View file

@ -40,16 +40,16 @@ _SEARCH_SPACE_ID = 1
def _build_cloud_fs_mw():
"""Build the production filesystem middleware in cloud mode.
A non-None ``search_space_id`` makes the resolver hand out a
A non-None ``workspace_id`` makes the resolver hand out a
``KBPostgresBackend``, exactly as production does. Staging operations never
touch the DB, so a dummy id is sufficient for these tests.
"""
selection = FilesystemSelection(mode=FilesystemMode.CLOUD)
resolver = build_backend_resolver(selection, search_space_id=_SEARCH_SPACE_ID)
resolver = build_backend_resolver(selection, workspace_id=_SEARCH_SPACE_ID)
return build_filesystem_mw(
backend_resolver=resolver,
filesystem_mode=FilesystemMode.CLOUD,
search_space_id=_SEARCH_SPACE_ID,
workspace_id=_SEARCH_SPACE_ID,
user_id="00000000-0000-0000-0000-000000000001",
thread_id=_SEARCH_SPACE_ID,
read_only=False,

View file

@ -51,7 +51,7 @@ def _build_desktop_fs_mw(root: Path):
return build_filesystem_mw(
backend_resolver=resolver,
filesystem_mode=FilesystemMode.DESKTOP_LOCAL_FOLDER,
search_space_id=1,
workspace_id=1,
user_id="00000000-0000-0000-0000-000000000001",
thread_id=1,
read_only=False,