source: flexoentity/tests/conftest.py@ d7499ca

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

Signature support for Linux and MacOS added

  • Property mode set to 100644
File size: 5.9 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 to_dict(self):
62 base = super().to_dict()
63 base.update({
64 "text": self.text,
65 "options": [opt.to_dict() for opt in self.options],
66 })
67 return base
68
69 @property
70 def text_seed(self) -> str:
71 """Include answer options (and points) for deterministic ID generation."""
72
73 joined = "|".join(
74 f"{opt.text.strip()}:{opt.points}"
75 for opt in sorted(self.options, key=lambda o: o.text.strip().lower())
76 )
77 return f"{self.text}{joined}"
78
79 @classmethod
80 def from_dict(cls, data):
81 obj = cls(text=data.get("text", ""),
82 options=[AnswerOption.from_dict(o) for o in data.get("options", [])],
83 )
84 # restore FlexoEntity core fields
85 if "flexo_id" in data:
86 obj.flexo_id = FlexOID.to_dict(data["flexo_id"])
87 return obj
88
89@pytest.fixture
90def sample_domain():
91 domain_id = "PY_ARITHM"
92 return Domain.with_domain_id(domain_id=domain_id,
93 fullname="PYTHON_ARITHMETIC",
94 description="ALL ABOUT ARITHMETIC IN PYTHON")
95
96@pytest.fixture
97def sample_question(sample_domain):
98 q = SingleChoiceQuestion.with_domain_id(domain_id=sample_domain.domain_id,
99 text="What is 2 + 2?",
100 options=[])
101 q._update_fingerprint()
102 return q
103
104SYSTEM = platform.system()
105
106
107# ─────────────────────────────────────────────────────────────
108# Basic test data directory + PEM test files
109# ─────────────────────────────────────────────────────────────
110
111@pytest.fixture(scope="session")
112def test_data_dir():
113 return Path(__file__).parent / "data"
114
115
116@pytest.fixture(scope="session")
117def test_cert(test_data_dir):
118 return test_data_dir / "testcert.pem"
119
120
121@pytest.fixture(scope="session")
122def test_key(test_data_dir):
123 return test_data_dir / "testkey.pem"
124
125
126# ─────────────────────────────────────────────────────────────
127# CertificateReference fixtures for each platform
128# ─────────────────────────────────────────────────────────────
129
130@pytest.fixture(scope="session")
131def cert_ref_linux(test_cert, test_key):
132 """Linux: Uses OpenSSL CMS with PEM cert + PEM private key."""
133 return CertificateReference(
134 platform="LINUX",
135 identifier=str(test_cert),
136 private_key_path=str(test_key),
137 public_cert_path=str(test_cert),
138 )
139
140
141@pytest.fixture(scope="session")
142def cert_ref_macos(test_cert):
143 """
144 macOS: Uses Keychain identity with Common Name (CN).
145 The test cert must be imported into the login keychain with CN=FlexOSignerTest.
146 """
147 return CertificateReference(
148 platform="MACOS",
149 identifier="FlexOSignerTest",
150 public_cert_path=str(test_cert),
151 )
152
153@pytest.fixture(scope="session")
154def backend(test_cert, test_key):
155 """Return the correct backend for the current platform."""
156
157 if SYSTEM == "Linux":
158 cert_ref = CertificateReference(
159 platform="LINUX",
160 identifier=str(test_cert),
161 private_key_path=str(test_key),
162 public_cert_path=str(test_cert),
163 )
164
165 elif SYSTEM == "Darwin":
166 cert_ref = CertificateReference(
167 platform="MACOS",
168 identifier="FlexOSignerTest",
169 public_cert_path=str(test_cert),
170 )
171
172 elif SYSTEM == "Windows":
173 pytest.skip("Windows signing tests not implemented yet")
174
175 else:
176 pytest.skip(f"Unsupported platform: {SYSTEM}")
177
178 try:
179 backend = get_signing_backend(cert_ref)
180 # sanity check: ensures cert exists and command is available
181 _ = backend.certificate_thumbprint
182 return backend
183 except Exception as e:
184 pytest.skip(f"Backend unavailable or misconfigured: {e}")
Note: See TracBrowser for help on using the repository browser.