File size: 3,368 Bytes
889e86b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
"""
Unit tests for routes/account.py's self-delete cascade.

Scope: verifies delete_all_user_data's Firestore traversal logic (every business's
subcollections, xpHistory, the root doc) using a fake Firestore client, per
docs/qa/test-strategy.md's unit layer. The actual Firebase Auth deletion
(fb_auth.delete_user) and the FastAPI route wiring are integration-level concerns,
not covered here.
"""
from unittest.mock import MagicMock
from routes.account import delete_all_user_data


def _make_fake_db():
    """
    Builds a fake Firestore client deep enough to exercise delete_all_user_data:
    one business with two subcollections each holding one doc, plus one
    xpHistory entry. Returns (db, deleted_refs) where deleted_refs collects
    every .delete() call made, in call order.
    """
    deleted = []

    def make_doc(name):
        doc = MagicMock(name=f"doc:{name}")
        doc.reference = MagicMock(name=f"ref:{name}")
        doc.reference.delete = MagicMock(side_effect=lambda n=name: deleted.append(n))
        return doc

    biz_doc = make_doc("business:biz1")

    def sub_collection(name):
        coll = MagicMock(name=f"collection:{name}")
        coll.stream = MagicMock(return_value=iter([make_doc(f"{name}:item1")]))
        return coll

    biz_doc.reference.collection = MagicMock(side_effect=sub_collection)

    businesses_collection = MagicMock(name="collection:businesses")
    businesses_collection.stream = MagicMock(return_value=iter([biz_doc]))

    xp_history_collection = MagicMock(name="collection:xpHistory")
    xp_history_collection.stream = MagicMock(return_value=iter([make_doc("xpHistory:entry1")]))

    user_ref = MagicMock(name="ref:user_data/uid")
    user_ref.delete = MagicMock(side_effect=lambda: deleted.append("user_data/uid"))

    def user_ref_collection(name):
        return {"businesses": businesses_collection, "xpHistory": xp_history_collection}[name]

    user_ref.collection = MagicMock(side_effect=user_ref_collection)

    user_data_collection = MagicMock(name="collection:user_data")
    user_data_collection.document = MagicMock(return_value=user_ref)

    db = MagicMock(name="firestore_client")
    db.collection = MagicMock(return_value=user_data_collection)

    return db, deleted


def test_deletes_every_business_subcollection_doc_and_the_business_itself(monkeypatch):
    db, deleted = _make_fake_db()
    monkeypatch.setattr("routes.account.firestore.client", lambda: db)

    delete_all_user_data("some-uid")

    # Every one of the 7 known subcollections was consulted for the business...
    biz_doc = db.collection().document().collection("businesses").stream.return_value
    # ...and each subcollection's one doc, plus the business doc itself, got deleted.
    assert "bff:item1" in deleted
    assert "documents:item1" in deleted
    assert "team:item1" in deleted
    assert "business:biz1" in deleted


def test_deletes_xp_history_entries(monkeypatch):
    db, deleted = _make_fake_db()
    monkeypatch.setattr("routes.account.firestore.client", lambda: db)

    delete_all_user_data("some-uid")

    assert "xpHistory:entry1" in deleted


def test_deletes_the_root_user_doc_last(monkeypatch):
    db, deleted = _make_fake_db()
    monkeypatch.setattr("routes.account.firestore.client", lambda: db)

    delete_all_user_data("some-uid")

    assert deleted[-1] == "user_data/uid"