Review of #132 found a blocker: PATCH /calls/{id}/transcript wipes tags/severity/location/units/embedding before re-extraction, but not the new scenes map, and doc_set(merge=True) can only add/overwrite nested map keys, never remove one. A call corrected from 3 scenes to 1 kept scenes.1/scenes.2 with pre-correction transcripts and incident_ids forever -- corrupting the exact per-scene tally #96 exists to make trustworthy, and able to re-feed stale text into #114's summarizer fix if a stale scene's incident_id still names a real incident. Fix: fstore.doc_update(...,{"scenes": fstore.DELETE_FIELD}) -- a real delete, not a merge over an empty map. Added fstore.DELETE_FIELD (re-exports the real firebase_admin sentinel) and stubbed it in the sandboxed test conftest, which didn't have it. Also softened an overclaiming docstring: the Firestore nested-merge behavior is verified against the doc_set wrapper's pass-through, not against live Firestore. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Tbknwttzou4s46PAykmtix
75 lines
3.2 KiB
Python
75 lines
3.2 KiB
Python
# All C2 core settings have defaults — no env setup needed.
|
|
#
|
|
# firebase-admin and google-cloud-firestore are runtime-only dependencies: they
|
|
# are installed in the container but not in the local dev venv, and
|
|
# app/internal/firestore.py calls _init_firebase() at import time. Without the
|
|
# stubs below, importing ANY module that reaches Firestore fails at collection
|
|
# time, which is why test_mqtt_handler and test_node_sweeper could not be run
|
|
# outside the container.
|
|
#
|
|
# The stubs are installed only when the real packages are absent, so the
|
|
# container's real SDK is never shadowed.
|
|
import sys
|
|
from types import ModuleType
|
|
from unittest.mock import MagicMock
|
|
|
|
try: # pragma: no cover - exercised only by which packages are installed
|
|
import firebase_admin # noqa: F401
|
|
except ModuleNotFoundError:
|
|
_firebase = ModuleType("firebase_admin")
|
|
# Falsy so _init_firebase() takes the initialize_app() branch rather than the
|
|
# already-initialised branch, which is itself broken (see DEFERRED.md).
|
|
_firebase._apps = {}
|
|
_firebase.initialize_app = MagicMock()
|
|
_firebase.credentials = MagicMock()
|
|
_firebase.firestore = MagicMock()
|
|
|
|
_credentials = ModuleType("firebase_admin.credentials")
|
|
_credentials.Certificate = MagicMock()
|
|
_credentials.ApplicationDefault = MagicMock()
|
|
|
|
_fs = ModuleType("firebase_admin.firestore")
|
|
_fs.client = MagicMock()
|
|
# A distinct sentinel rather than a MagicMock: production code writes this
|
|
# into dicts that tests compare against, and a MagicMock compares unequal
|
|
# to itself across attribute accesses.
|
|
_fs.SERVER_TIMESTAMP = "__SERVER_TIMESTAMP__"
|
|
# Same reasoning as SERVER_TIMESTAMP above: a distinct sentinel, not a
|
|
# MagicMock, so `fstore.DELETE_FIELD is fs.DELETE_FIELD` and dict/`is`
|
|
# comparisons against it in tests (server-26#96/#114, PR #132) behave.
|
|
_fs.DELETE_FIELD = "__DELETE_FIELD__"
|
|
|
|
_auth = ModuleType("firebase_admin.auth")
|
|
_auth.verify_id_token = MagicMock()
|
|
_auth.set_custom_user_claims = MagicMock()
|
|
_auth.get_user_by_email = MagicMock()
|
|
_auth.get_user = MagicMock()
|
|
# Type used in annotations at import time by routers/users.py, so it has to
|
|
# exist as a name even though nothing here ever instantiates it. Without it,
|
|
# importing app.main -- and therefore testing anything wired at app level,
|
|
# like the CORS policy -- fails at collection.
|
|
_auth.UserRecord = MagicMock()
|
|
_auth.list_users = MagicMock()
|
|
_auth.update_user = MagicMock()
|
|
_auth.create_user = MagicMock()
|
|
_auth.delete_user = MagicMock()
|
|
|
|
_firebase.auth = _auth
|
|
_firebase.credentials = _credentials
|
|
_firebase.firestore = _fs
|
|
|
|
sys.modules["firebase_admin"] = _firebase
|
|
sys.modules["firebase_admin.credentials"] = _credentials
|
|
sys.modules["firebase_admin.firestore"] = _fs
|
|
sys.modules["firebase_admin.auth"] = _auth
|
|
|
|
try: # pragma: no cover
|
|
from google.cloud.firestore_v1.base_query import FieldFilter # noqa: F401
|
|
except ModuleNotFoundError:
|
|
for _name in (
|
|
"google", "google.cloud", "google.cloud.firestore_v1",
|
|
"google.cloud.firestore_v1.base_query",
|
|
):
|
|
sys.modules.setdefault(_name, ModuleType(_name))
|
|
sys.modules["google.cloud.firestore_v1.base_query"].FieldFilter = MagicMock()
|