Coverage for server / services / growthbook / catalog.py: 100%

20 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-10-04 09:33 +0000

1""" 

2Read-only feature catalog helpers — the catalog is GrowthBook UNION the local registry. 

3 

4Modified by Allan Ninal 

5""" 

6 

7from __future__ import annotations 

8 

9from typing import Optional 

10 

11from server.services.growthbook.client import growthbook_client 

12from server.services.growthbook.registry import FEATURE_REGISTRY, FeatureDefinition, get_feature_definition 

13 

14 

15def _catalog_keys() -> list[str]: 

16 """Every feature key the platform knows about — GrowthBook UNION the registry. 

17 

18 This was a swap, not a union: a non-empty GrowthBook list replaced the registry 

19 outright. GrowthBook was seeded with `teacher.*` flags only, so the instant it 

20 answered, all four `student.*` definitions — which exist ONLY here — vanished 

21 from the catalog and `/effective` began reporting `features: []` for every 

22 student, while per-key evaluation kept saying those same features were enabled. 

23 

24 The registry is not a fallback for GrowthBook being unreachable. It is the 

25 declaration of which features exist, and a key GrowthBook has never heard of is 

26 precisely the one that must not be dropped. Evaluation still prefers GrowthBook 

27 per key; only enumeration is unioned. 

28 """ 

29 return sorted(set(growthbook_client.list_feature_keys()) | set(FEATURE_REGISTRY)) 

30 

31 

32def _audience_as_feature_type(feature_key: str, definition: Optional[FeatureDefinition]) -> str: 

33 if definition is not None: 

34 if definition.audience in ("teacher", "student"): 

35 return definition.audience 

36 return "platform" 

37 if feature_key.startswith("teacher."): 

38 return "teacher" 

39 if feature_key.startswith("student."): 

40 return "student" 

41 return "platform" 

42 

43 

44def feature_exists(feature_key: str) -> bool: 

45 if growthbook_client.has_feature(feature_key): 

46 return True 

47 return get_feature_definition(feature_key) is not None