source: flexoentity/tests/conftest.py@ e458b5a

Last change on this file since e458b5a was e458b5a, checked in by Enrico Schwass <ennoausberlin@…>, 7 weeks ago

fix Domain and FlexoSignature and tests to reflect changes to serialization

  • Property mode set to 100644
File size: 5.8 KB
Line 
1# tests/stubs/single_choice_question.py
2from dataclasses import dataclass, field
3import pytest
4import platform
5from pathlib import Path
6from datetime import datetime
7from typing import List
8from flexoentity import FlexOID, FlexoEntity, EntityType, EntityState, Domain, get_signing_backend, CertificateReference
9
10
11@pytest.fixture
12def fixed_datetime(monkeypatch):
13 class FixedDate(datetime):
14 @classmethod
15 def now(cls, tz=None):
16 return datetime(2025, 11, 1, tzinfo=tz)
17 monkeypatch.setattr("flexoentity.id_factory.datetime", FixedDate)
18 return FixedDate
19
20@dataclass
21class AnswerOption:
22 id: str
23 text: str
24 points: float = 0.0
25
26 def to_dict(self):
27 return {"id": self.id, "text": self.text, "points": self.points}
28
29 @classmethod
30 def from_dict(cls, data):
31 return cls(
32 id=data.get("id", ""),
33 text=data.get("text", ""),
34 points=data.get("points", 0.0)
35 )
36
37
38@dataclass
39class SingleChoiceQuestion(FlexoEntity):
40 """A minimal stub to test FlexoEntity integration."""
41 ENTITY_TYPE = EntityType.ITEM
42
43 text: str = ""
44 options: List[AnswerOption] = field(default_factory=list)
45
46 def __post_init__(self):
47 # If no FlexOID yet, generate a draft ID now.
48 if not getattr(self, "flexo_id", None):
49 self.flexo_id = FlexOID.safe_generate(
50 domain_id=self.domain_id,
51 entity_type=SingleChoiceQuestion.ENTITY_TYPE.value, # 'I'
52 state=EntityState.DRAFT.value, # 'D'
53 text=self.text_seed or self.text,
54 version=1,
55 )
56
57 @classmethod
58 def default(cls):
59 return cls()
60
61 def _serialize_content(self):
62 return {
63 "text": self.text,
64 "options": [opt.to_dict() for opt in self.options],
65 }
66
67 @property
68 def text_seed(self) -> str:
69 """Include answer options (and points) for deterministic ID generation."""
70
71 joined = "|".join(
72 f"{opt.text.strip()}:{opt.points}"
73 for opt in sorted(self.options, key=lambda o: o.text.strip().lower())
74 )
75 return f"{self.text}{joined}"
76
77 @classmethod
78 def from_dict(cls, data):
79 obj = cls(text=data.get("text", ""),
80 options=[AnswerOption.from_dict(o) for o in data.get("options", [])],
81 )
82 # restore FlexoEntity core fields
83 if "flexo_id" in data:
84 obj.flexo_id = FlexOID.to_dict(data["flexo_id"])
85 return obj
86
87@pytest.fixture
88def sample_domain():
89 domain_id = "PY_ARITHM"
90 return Domain.with_domain_id(domain_id=domain_id,
91 fullname="PYTHON_ARITHMETIC",
92 description="ALL ABOUT ARITHMETIC IN PYTHON")
93
94@pytest.fixture
95def sample_question(sample_domain):
96 q = SingleChoiceQuestion.with_domain_id(domain_id=sample_domain.domain_id,
97 text="What is 2 + 2?",
98 options=[])
99 q._update_fingerprint()
100 return q
101
102SYSTEM = platform.system()
103
104
105# ─────────────────────────────────────────────────────────────
106# Basic test data directory + PEM test files
107# ─────────────────────────────────────────────────────────────
108
109@pytest.fixture(scope="session")
110def test_data_dir():
111 return Path(__file__).parent / "data"
112
113
114@pytest.fixture(scope="session")
115def test_cert(test_data_dir):
116 return test_data_dir / "testcert.pem"
117
118
119@pytest.fixture(scope="session")
120def test_key(test_data_dir):
121 return test_data_dir / "testkey.pem"
122
123
124# ─────────────────────────────────────────────────────────────
125# CertificateReference fixtures for each platform
126# ─────────────────────────────────────────────────────────────
127
128@pytest.fixture(scope="session")
129def cert_ref_linux(test_cert, test_key):
130 """Linux: Uses OpenSSL CMS with PEM cert + PEM private key."""
131 return CertificateReference(
132 platform="LINUX",
133 identifier=str(test_cert),
134 private_key_path=str(test_key),
135 public_cert_path=str(test_cert),
136 )
137
138
139@pytest.fixture(scope="session")
140def cert_ref_macos(test_cert):
141 """
142 macOS: Uses Keychain identity with Common Name (CN).
143 The test cert must be imported into the login keychain with CN=FlexOSignerTest.
144 """
145 return CertificateReference(
146 platform="MACOS",
147 identifier="FlexOSignerTest",
148 public_cert_path=str(test_cert),
149 )
150
151@pytest.fixture(scope="session")
152def backend(test_cert, test_key):
153 """Return the correct backend for the current platform."""
154
155 if SYSTEM == "Linux":
156 cert_ref = CertificateReference(
157 platform="LINUX",
158 identifier=str(test_cert),
159 private_key_path=str(test_key),
160 public_cert_path=str(test_cert),
161 )
162
163 elif SYSTEM == "Darwin":
164 cert_ref = CertificateReference(
165 platform="MACOS",
166 identifier="FlexOSignerTest",
167 public_cert_path=str(test_cert),
168 )
169
170 elif SYSTEM == "Windows":
171 pytest.skip("Windows signing tests not implemented yet")
172
173 else:
174 pytest.skip(f"Unsupported platform: {SYSTEM}")
175
176 try:
177 backend = get_signing_backend(cert_ref)
178 # sanity check: ensures cert exists and command is available
179 _ = backend.certificate_thumbprint
180 return backend
181 except Exception as e:
182 pytest.skip(f"Backend unavailable or misconfigured: {e}")
Note: See TracBrowser for help on using the repository browser.