import unittest from pathlib import Path import sys import psycopg code_path = Path(__file__).parent.parent / "ca_core" sys.path.insert(0, str(code_path)) import entity import property DBNAME = "ca" class TestPropertyFunctions(unittest.TestCase): @classmethod def setUpClass(cls): cls.conn = psycopg.connect(f"dbname={DBNAME}") cls.cur = cls.conn.cursor(row_factory=psycopg.rows.dict_row) # Ensure entity table exists cls.cur.execute(""" CREATE TABLE IF NOT EXISTS entity ( id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, creation_ts TIMESTAMPTZ NOT NULL DEFAULT now(), creator INT REFERENCES entity(id), name VARCHAR(100) NOT NULL, type VARCHAR(10) NOT NULL DEFAULT 'person', geo_offset BIGINT, public_key VARCHAR(300) NOT NULL, expiration DATE, status VARCHAR(10) NOT NULL DEFAULT 'active' ) """) cls.cur.execute(""" CREATE TABLE IF NOT EXISTS property ( id INT NOT NULL REFERENCES entity(id) ON DELETE CASCADE, property_name VARCHAR(100), PRIMARY KEY (id, property_name) ) """) cls.conn.commit() def setUp(self): self.conn.rollback() self.conn.autocommit = False def tearDown(self): self.conn.rollback() def test_set_and_get_property(self): creator_id = entity.insert_creator(self.cur, "Creator1", "pubkey1") person_id = entity.enroll_person(self.cur, "Person1", "pubkey_person", creator_id) property.set_property(self.cur, person_id, "email") props = property.get_properties(self.cur, person_id) self.assertIn("email", props) def test_delete_property(self): creator_id = entity.insert_creator(self.cur, "Creator2", "pubkey2") person_id = entity.enroll_person(self.cur, "Person2", "pubkey_person2", creator_id) property.set_property(self.cur, person_id, "phone") property.delete_property(self.cur, person_id, "phone") props = property.get_properties(self.cur, person_id) self.assertNotIn("phone", props) def test_revoked_entity_has_no_properties(self): creator_id = entity.insert_creator(self.cur, "Creator3", "pubkey3") person_id = entity.enroll_person(self.cur, "Person3", "pubkey_person3", creator_id) property.set_property(self.cur, person_id, "address") entity.revoke_entity(self.cur, person_id, creator_id) props = property.get_properties(self.cur, person_id) # Optional: you can decide whether to return empty or raise; here we return all properties regardless of status # If you want to ignore revoked entities: cursor = self.cur cursor.execute( "SELECT property_name FROM property p JOIN entity e ON e.id=p.id WHERE e.id=%s AND e.status='active'", (person_id,) ) props_active = [r['property_name'] for r in cursor.fetchall()] self.assertNotIn("address", props_active) if __name__ == "__main__": unittest.main()