Files
noteflow/tests/grpc/test_diarization_lifecycle.py

138 lines
5.0 KiB
Python

"""Tests for diarization job lifecycle improvements.
Sprint GAP-004: Diarization Job Lifecycle Issues.
Tests cover:
- Database requirement enforcement for diarization jobs
- Error handling when database unavailable
"""
from __future__ import annotations
import grpc
import pytest
from noteflow.grpc._config import ServicesConfig
from noteflow.grpc.proto import noteflow_pb2
from noteflow.grpc.service import NoteFlowServicer
from noteflow.infrastructure.diarization import DiarizationEngine
class _FakeDiarizationEngine(DiarizationEngine):
"""Lightweight diarization engine stub for service config."""
def __init__(self) -> None:
super().__init__()
class _AbortCalled(Exception):
"""Exception raised when gRPC abort is called."""
def __init__(self, code: grpc.StatusCode, details: str) -> None:
self.code = code
self.details = details
super().__init__(f"abort({code}, {details})")
class _MockGrpcContext:
"""Minimal async gRPC context for tests."""
def __init__(self) -> None:
"""Initialize context."""
self.abort_code: grpc.StatusCode | None = None
self.abort_details: str | None = None
def set_code(self, code: grpc.StatusCode) -> None:
"""Set response status code."""
self.abort_code = code
def set_details(self, details: str) -> None:
"""Set response status details."""
self.abort_details = details
async def abort(self, code: grpc.StatusCode, details: str) -> None:
"""Simulate gRPC abort by raising exception."""
self.abort_code = code
self.abort_details = details
raise _AbortCalled(code, details)
@pytest.fixture
def servicer() -> NoteFlowServicer:
"""Create servicer with mock diarization engine but no DB support."""
return NoteFlowServicer(services=ServicesConfig(diarization_engine=_FakeDiarizationEngine()))
class TestDatabaseRequirement:
"""Tests verifying diarization requires database support."""
@pytest.mark.asyncio
async def test_refine_requires_database_support(self, servicer: NoteFlowServicer) -> None:
"""RefineSpeakerDiarization returns error when database unavailable."""
store = servicer.get_memory_store()
meeting = store.create("Test meeting")
meeting.start_recording()
meeting.begin_stopping()
meeting.stop_recording()
store.update(meeting)
context = _MockGrpcContext()
response = await servicer.RefineSpeakerDiarization(
noteflow_pb2.RefineSpeakerDiarizationRequest(meeting_id=str(meeting.id)),
context,
)
# Should return error response, not job_id
assert not response.job_id, "Should not return job_id without database"
assert response.status == noteflow_pb2.JOB_STATUS_FAILED, "Status should be FAILED"
assert context.abort_code == grpc.StatusCode.FAILED_PRECONDITION, "Error code should be FAILED_PRECONDITION"
@pytest.mark.asyncio
async def test_refine_error_mentions_database(self, servicer: NoteFlowServicer) -> None:
"""Error message should mention database requirement."""
store = servicer.get_memory_store()
meeting = store.create("Test meeting")
meeting.start_recording()
meeting.begin_stopping()
meeting.stop_recording()
store.update(meeting)
context = _MockGrpcContext()
response = await servicer.RefineSpeakerDiarization(
noteflow_pb2.RefineSpeakerDiarizationRequest(meeting_id=str(meeting.id)),
context,
)
# Error message should explain database is required
error_msg = response.error_message.lower()
assert "database" in error_msg, "Error should mention 'database'"
@pytest.mark.asyncio
async def test_get_status_requires_database(self, servicer: NoteFlowServicer) -> None:
"""GetDiarizationJobStatus aborts when database unavailable."""
context = _MockGrpcContext()
# Should abort because no DB support
with pytest.raises(_AbortCalled, match="database") as exc_info:
await servicer.GetDiarizationJobStatus(
noteflow_pb2.GetDiarizationJobStatusRequest(job_id="any-job-id"),
context,
)
# Verify abort was called with NOT_FOUND (no DB support means jobs can't be found)
assert exc_info.value.code == grpc.StatusCode.NOT_FOUND, "Should abort with NOT_FOUND"
assert "database" in exc_info.value.details.lower(), "Error should mention database"
@pytest.mark.asyncio
async def test_cancel_requires_database(self, servicer: NoteFlowServicer) -> None:
"""CancelDiarizationJob returns error when database unavailable."""
context = _MockGrpcContext()
response = await servicer.CancelDiarizationJob(
noteflow_pb2.CancelDiarizationJobRequest(job_id="any-job-id"),
context,
)
assert response.success is False, "Cancel should fail without database"
assert "database" in response.error_message.lower(), "Error should mention database"