personal memory agent
1# SPDX-License-Identifier: AGPL-3.0-only
2# Copyright (c) 2026 sol pbc
3
4"""Tests for stale facet cookie validation in convey."""
5
6import json
7
8import pytest
9
10from solstone.convey import create_app
11
12
13@pytest.fixture
14def client(journal_copy):
15 """Create Flask test client with isolated journal copy."""
16 app = create_app(str(journal_copy))
17 return app.test_client()
18
19
20def _cookie_deleted(response) -> bool:
21 """Check if selectedFacet cookie was deleted in response."""
22 for header in response.headers.getlist("Set-Cookie"):
23 if header.startswith("selectedFacet=") and "Max-Age=0" in header:
24 return True
25 return False
26
27
28class TestFacetCookieValidation:
29 """Tests for _get_selected_facet() stale cookie validation."""
30
31 def test_valid_cookie_returned(self, client):
32 """Valid cookie for active facet -> returned as-is, no cookie cleared."""
33 client.set_cookie("selectedFacet", "montague")
34 response = client.get("/api/shell")
35 assert response.status_code == 200
36 assert not _cookie_deleted(response)
37
38 def test_stale_cookie_cleared(self, client, journal_copy):
39 """Stale cookie (nonexistent facet) -> cookie expired, config cleared."""
40 client.set_cookie("selectedFacet", "nonexistent-facet")
41 response = client.get("/api/shell")
42 assert response.status_code == 200
43 assert _cookie_deleted(response)
44
45 config = json.loads((journal_copy / "config" / "convey.json").read_text())
46 assert config["facets"]["selected"] is None
47
48 def test_no_cookie_uses_config(self, client, journal_copy):
49 """No cookie -> returns config default, no cookie modification."""
50 config_path = journal_copy / "config" / "convey.json"
51 config = json.loads(config_path.read_text())
52 config["facets"]["selected"] = "montague"
53 config_path.write_text(json.dumps(config, indent=2))
54
55 response = client.get("/api/shell")
56 assert response.status_code == 200
57 assert not _cookie_deleted(response)
58
59 def test_muted_facet_cookie_stale(self, client, journal_copy):
60 """Muted facet in cookie -> treated as stale, cookie expired."""
61 # muted-test fixture already exists in journal_copy (copied from fixtures)
62 client.set_cookie("selectedFacet", "muted-test")
63 response = client.get("/api/shell")
64 assert response.status_code == 200
65 assert _cookie_deleted(response)
66
67 config = json.loads((journal_copy / "config" / "convey.json").read_text())
68 assert config["facets"]["selected"] is None
69
70 def test_empty_cookie_cleared(self, client, journal_copy):
71 """Empty string cookie -> treated as stale, cookie expired, config cleared."""
72 client.set_cookie("selectedFacet", "")
73 response = client.get("/api/shell")
74 assert response.status_code == 200
75 assert _cookie_deleted(response)
76
77 config = json.loads((journal_copy / "config" / "convey.json").read_text())
78 assert config["facets"]["selected"] is None
79
80 def test_stale_config_no_cookie(self, client, journal_copy):
81 """Stale config value with no cookie -> config cleared."""
82 config_path = journal_copy / "config" / "convey.json"
83 config = json.loads(config_path.read_text())
84 config["facets"]["selected"] = "nonexistent-facet"
85 config_path.write_text(json.dumps(config, indent=2))
86
87 response = client.get("/api/shell")
88 assert response.status_code == 200
89
90 config = json.loads(config_path.read_text())
91 assert config["facets"]["selected"] is None