Skip to content

Commit 0af75ee

Browse files
test(encryption): round-trip summary_json/concept_notes/points TEXT columns
#200 retypes these columns to TEXT (they hold encrypted base64). Add a non-quarantined round-trip test using the exact helpers the routes use (encrypt_json/decrypt_json for summary_json + concept_notes, encrypt_if_present/decrypt_numeric for points) so the encryption boundary for these columns is covered while the route-level documents tests remain quarantined under #210.
1 parent fce41d0 commit 0af75ee

1 file changed

Lines changed: 36 additions & 0 deletions

File tree

backend/tests/test_encryption.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,39 @@ def test_tampered_ciphertext_raises():
104104
tampered = base64.b64encode(bytes(raw)).decode()
105105
with pytest.raises(Exception):
106106
encryption.decrypt(tampered)
107+
108+
109+
# ── #200: columns retyped NUMERIC/JSONB -> TEXT in the canonical schema ─────────
110+
# These hold encrypted base64 *text*, so the value each column stores must survive
111+
# the exact write->read helpers the routes use. The route-level coverage for
112+
# documents.concept_notes lives in the test_documents_routes module quarantined
113+
# under #210, so assert the round-trip here (non-quarantined) before that change
114+
# lands. summary_json/concept_notes -> encrypt_json/decrypt_json; points ->
115+
# encrypt_if_present/decrypt_numeric (see routes/learn.py, routes/documents.py,
116+
# routes/gradebook.py).
117+
118+
def test_summary_json_text_column_round_trip():
119+
# sessions.summary_json — a JSON object (learn.py writes encrypt_json(summary))
120+
summary = {"concepts": ["limits", "derivatives"], "score": 0.82, "notes": "ünïcode"}
121+
assert encryption.decrypt_json(encryption.encrypt_json(summary)) == summary
122+
123+
124+
def test_concept_notes_text_column_round_trip():
125+
# documents.concept_notes — a JSON array of {name, description}
126+
# (documents.py writes encrypt_json(concept_notes))
127+
concept_notes = [
128+
{"name": "Eigenvalue", "description": "Scalar λ with Av = λv."},
129+
{"name": "Span", "description": "All linear combinations of a set."},
130+
]
131+
assert encryption.decrypt_json(encryption.encrypt_json(concept_notes)) == concept_notes
132+
133+
134+
def test_points_text_column_round_trip():
135+
# assignments.points_possible / points_earned — numbers written via
136+
# encrypt_if_present and read back via decrypt_numeric.
137+
for value in (100, 95.5, 0):
138+
ct = encryption.encrypt_if_present(value)
139+
assert isinstance(ct, str) # stored as TEXT, not NUMERIC
140+
assert encryption.decrypt_numeric(ct) == float(value)
141+
assert encryption.encrypt_if_present(None) is None
142+
assert encryption.decrypt_numeric(None) is None

0 commit comments

Comments
 (0)