lex-agentic-affect 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (218) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +12 -0
  3. data/Gemfile +5 -0
  4. data/LICENSE +21 -0
  5. data/README.md +13 -0
  6. data/lex-agentic-affect.gemspec +30 -0
  7. data/lib/legion/extensions/agentic/affect/appraisal/client.rb +20 -0
  8. data/lib/legion/extensions/agentic/affect/appraisal/helpers/appraisal.rb +112 -0
  9. data/lib/legion/extensions/agentic/affect/appraisal/helpers/appraisal_engine.rb +129 -0
  10. data/lib/legion/extensions/agentic/affect/appraisal/helpers/constants.rb +43 -0
  11. data/lib/legion/extensions/agentic/affect/appraisal/runners/appraisal.rb +105 -0
  12. data/lib/legion/extensions/agentic/affect/appraisal/version.rb +13 -0
  13. data/lib/legion/extensions/agentic/affect/appraisal.rb +19 -0
  14. data/lib/legion/extensions/agentic/affect/cognitive_empathy/client.rb +19 -0
  15. data/lib/legion/extensions/agentic/affect/cognitive_empathy/helpers/constants.rb +37 -0
  16. data/lib/legion/extensions/agentic/affect/cognitive_empathy/helpers/empathy_engine.rb +151 -0
  17. data/lib/legion/extensions/agentic/affect/cognitive_empathy/helpers/perspective.rb +92 -0
  18. data/lib/legion/extensions/agentic/affect/cognitive_empathy/runners/cognitive_empathy.rb +93 -0
  19. data/lib/legion/extensions/agentic/affect/cognitive_empathy/version.rb +13 -0
  20. data/lib/legion/extensions/agentic/affect/cognitive_empathy.rb +20 -0
  21. data/lib/legion/extensions/agentic/affect/contagion/client.rb +28 -0
  22. data/lib/legion/extensions/agentic/affect/contagion/helpers/constants.rb +34 -0
  23. data/lib/legion/extensions/agentic/affect/contagion/helpers/contagion_engine.rb +184 -0
  24. data/lib/legion/extensions/agentic/affect/contagion/helpers/meme.rb +97 -0
  25. data/lib/legion/extensions/agentic/affect/contagion/runners/cognitive_contagion.rb +125 -0
  26. data/lib/legion/extensions/agentic/affect/contagion/version.rb +13 -0
  27. data/lib/legion/extensions/agentic/affect/contagion.rb +19 -0
  28. data/lib/legion/extensions/agentic/affect/defusion/client.rb +28 -0
  29. data/lib/legion/extensions/agentic/affect/defusion/helpers/constants.rb +64 -0
  30. data/lib/legion/extensions/agentic/affect/defusion/helpers/defusion_engine.rb +167 -0
  31. data/lib/legion/extensions/agentic/affect/defusion/helpers/thought.rb +92 -0
  32. data/lib/legion/extensions/agentic/affect/defusion/runners/cognitive_defusion.rb +127 -0
  33. data/lib/legion/extensions/agentic/affect/defusion/version.rb +13 -0
  34. data/lib/legion/extensions/agentic/affect/defusion.rb +19 -0
  35. data/lib/legion/extensions/agentic/affect/emotion/actors/momentum_decay.rb +45 -0
  36. data/lib/legion/extensions/agentic/affect/emotion/client.rb +36 -0
  37. data/lib/legion/extensions/agentic/affect/emotion/helpers/baseline.rb +52 -0
  38. data/lib/legion/extensions/agentic/affect/emotion/helpers/momentum.rb +52 -0
  39. data/lib/legion/extensions/agentic/affect/emotion/helpers/valence.rb +92 -0
  40. data/lib/legion/extensions/agentic/affect/emotion/runners/gut.rb +102 -0
  41. data/lib/legion/extensions/agentic/affect/emotion/runners/valence.rb +120 -0
  42. data/lib/legion/extensions/agentic/affect/emotion/version.rb +13 -0
  43. data/lib/legion/extensions/agentic/affect/emotion.rb +20 -0
  44. data/lib/legion/extensions/agentic/affect/empathy/client.rb +21 -0
  45. data/lib/legion/extensions/agentic/affect/empathy/helpers/constants.rb +54 -0
  46. data/lib/legion/extensions/agentic/affect/empathy/helpers/mental_model.rb +185 -0
  47. data/lib/legion/extensions/agentic/affect/empathy/helpers/model_store.rb +88 -0
  48. data/lib/legion/extensions/agentic/affect/empathy/runners/empathy.rb +173 -0
  49. data/lib/legion/extensions/agentic/affect/empathy/version.rb +13 -0
  50. data/lib/legion/extensions/agentic/affect/empathy.rb +20 -0
  51. data/lib/legion/extensions/agentic/affect/fatigue/client.rb +26 -0
  52. data/lib/legion/extensions/agentic/affect/fatigue/helpers/constants.rb +54 -0
  53. data/lib/legion/extensions/agentic/affect/fatigue/helpers/energy_model.rb +181 -0
  54. data/lib/legion/extensions/agentic/affect/fatigue/helpers/fatigue_store.rb +146 -0
  55. data/lib/legion/extensions/agentic/affect/fatigue/runners/fatigue.rb +89 -0
  56. data/lib/legion/extensions/agentic/affect/fatigue/version.rb +13 -0
  57. data/lib/legion/extensions/agentic/affect/fatigue.rb +19 -0
  58. data/lib/legion/extensions/agentic/affect/flow/client.rb +25 -0
  59. data/lib/legion/extensions/agentic/affect/flow/helpers/constants.rb +84 -0
  60. data/lib/legion/extensions/agentic/affect/flow/helpers/flow_detector.rb +166 -0
  61. data/lib/legion/extensions/agentic/affect/flow/runners/flow.rb +129 -0
  62. data/lib/legion/extensions/agentic/affect/flow/version.rb +13 -0
  63. data/lib/legion/extensions/agentic/affect/flow.rb +18 -0
  64. data/lib/legion/extensions/agentic/affect/interoception/actors/decay.rb +45 -0
  65. data/lib/legion/extensions/agentic/affect/interoception/client.rb +28 -0
  66. data/lib/legion/extensions/agentic/affect/interoception/helpers/body_budget.rb +152 -0
  67. data/lib/legion/extensions/agentic/affect/interoception/helpers/constants.rb +68 -0
  68. data/lib/legion/extensions/agentic/affect/interoception/helpers/somatic_marker.rb +75 -0
  69. data/lib/legion/extensions/agentic/affect/interoception/runners/interoception.rb +101 -0
  70. data/lib/legion/extensions/agentic/affect/interoception/version.rb +13 -0
  71. data/lib/legion/extensions/agentic/affect/interoception.rb +20 -0
  72. data/lib/legion/extensions/agentic/affect/mood/client.rb +21 -0
  73. data/lib/legion/extensions/agentic/affect/mood/helpers/constants.rb +78 -0
  74. data/lib/legion/extensions/agentic/affect/mood/helpers/mood_state.rb +154 -0
  75. data/lib/legion/extensions/agentic/affect/mood/runners/mood.rb +122 -0
  76. data/lib/legion/extensions/agentic/affect/mood/version.rb +13 -0
  77. data/lib/legion/extensions/agentic/affect/mood.rb +18 -0
  78. data/lib/legion/extensions/agentic/affect/motivation/client.rb +26 -0
  79. data/lib/legion/extensions/agentic/affect/motivation/helpers/constants.rb +48 -0
  80. data/lib/legion/extensions/agentic/affect/motivation/helpers/drive_state.rb +98 -0
  81. data/lib/legion/extensions/agentic/affect/motivation/helpers/motivation_store.rb +106 -0
  82. data/lib/legion/extensions/agentic/affect/motivation/runners/motivation.rb +165 -0
  83. data/lib/legion/extensions/agentic/affect/motivation/version.rb +13 -0
  84. data/lib/legion/extensions/agentic/affect/motivation.rb +19 -0
  85. data/lib/legion/extensions/agentic/affect/reappraisal/actors/auto_regulate.rb +45 -0
  86. data/lib/legion/extensions/agentic/affect/reappraisal/client.rb +28 -0
  87. data/lib/legion/extensions/agentic/affect/reappraisal/helpers/constants.rb +82 -0
  88. data/lib/legion/extensions/agentic/affect/reappraisal/helpers/emotional_event.rb +98 -0
  89. data/lib/legion/extensions/agentic/affect/reappraisal/helpers/llm_enhancer.rb +88 -0
  90. data/lib/legion/extensions/agentic/affect/reappraisal/helpers/reappraisal_engine.rb +153 -0
  91. data/lib/legion/extensions/agentic/affect/reappraisal/runners/cognitive_reappraisal.rb +164 -0
  92. data/lib/legion/extensions/agentic/affect/reappraisal/version.rb +13 -0
  93. data/lib/legion/extensions/agentic/affect/reappraisal.rb +20 -0
  94. data/lib/legion/extensions/agentic/affect/regulation/client.rb +25 -0
  95. data/lib/legion/extensions/agentic/affect/regulation/helpers/constants.rb +71 -0
  96. data/lib/legion/extensions/agentic/affect/regulation/helpers/regulation_model.rb +175 -0
  97. data/lib/legion/extensions/agentic/affect/regulation/runners/emotional_regulation.rb +127 -0
  98. data/lib/legion/extensions/agentic/affect/regulation/version.rb +13 -0
  99. data/lib/legion/extensions/agentic/affect/regulation.rb +18 -0
  100. data/lib/legion/extensions/agentic/affect/resilience/client.rb +27 -0
  101. data/lib/legion/extensions/agentic/affect/resilience/helpers/adversity_tracker.rb +130 -0
  102. data/lib/legion/extensions/agentic/affect/resilience/helpers/constants.rb +79 -0
  103. data/lib/legion/extensions/agentic/affect/resilience/helpers/resilience_model.rb +165 -0
  104. data/lib/legion/extensions/agentic/affect/resilience/runners/resilience.rb +150 -0
  105. data/lib/legion/extensions/agentic/affect/resilience/version.rb +13 -0
  106. data/lib/legion/extensions/agentic/affect/resilience.rb +19 -0
  107. data/lib/legion/extensions/agentic/affect/resonance/client.rb +24 -0
  108. data/lib/legion/extensions/agentic/affect/resonance/helpers/category.rb +75 -0
  109. data/lib/legion/extensions/agentic/affect/resonance/helpers/constants.rb +47 -0
  110. data/lib/legion/extensions/agentic/affect/resonance/helpers/resonance_engine.rb +115 -0
  111. data/lib/legion/extensions/agentic/affect/resonance/runners/cognitive_resonance.rb +94 -0
  112. data/lib/legion/extensions/agentic/affect/resonance/version.rb +13 -0
  113. data/lib/legion/extensions/agentic/affect/resonance.rb +19 -0
  114. data/lib/legion/extensions/agentic/affect/reward/client.rb +26 -0
  115. data/lib/legion/extensions/agentic/affect/reward/helpers/constants.rb +67 -0
  116. data/lib/legion/extensions/agentic/affect/reward/helpers/reward_signal.rb +178 -0
  117. data/lib/legion/extensions/agentic/affect/reward/helpers/reward_store.rb +142 -0
  118. data/lib/legion/extensions/agentic/affect/reward/runners/reward.rb +92 -0
  119. data/lib/legion/extensions/agentic/affect/reward/version.rb +13 -0
  120. data/lib/legion/extensions/agentic/affect/reward.rb +19 -0
  121. data/lib/legion/extensions/agentic/affect/somatic_marker/actors/decay.rb +45 -0
  122. data/lib/legion/extensions/agentic/affect/somatic_marker/client.rb +29 -0
  123. data/lib/legion/extensions/agentic/affect/somatic_marker/helpers/body_state.rb +69 -0
  124. data/lib/legion/extensions/agentic/affect/somatic_marker/helpers/constants.rb +43 -0
  125. data/lib/legion/extensions/agentic/affect/somatic_marker/helpers/marker_store.rb +160 -0
  126. data/lib/legion/extensions/agentic/affect/somatic_marker/helpers/somatic_marker.rb +74 -0
  127. data/lib/legion/extensions/agentic/affect/somatic_marker/runners/somatic_marker.rb +132 -0
  128. data/lib/legion/extensions/agentic/affect/somatic_marker/version.rb +13 -0
  129. data/lib/legion/extensions/agentic/affect/somatic_marker.rb +20 -0
  130. data/lib/legion/extensions/agentic/affect/version.rb +11 -0
  131. data/lib/legion/extensions/agentic/affect.rb +34 -0
  132. data/spec/legion/extensions/agentic/affect/appraisal/client_spec.rb +52 -0
  133. data/spec/legion/extensions/agentic/affect/appraisal/helpers/appraisal_engine_spec.rb +161 -0
  134. data/spec/legion/extensions/agentic/affect/appraisal/helpers/appraisal_spec.rb +175 -0
  135. data/spec/legion/extensions/agentic/affect/appraisal/helpers/constants_spec.rb +49 -0
  136. data/spec/legion/extensions/agentic/affect/appraisal/runners/appraisal_spec.rb +116 -0
  137. data/spec/legion/extensions/agentic/affect/cognitive_empathy/client_spec.rb +62 -0
  138. data/spec/legion/extensions/agentic/affect/cognitive_empathy/helpers/empathy_engine_spec.rb +316 -0
  139. data/spec/legion/extensions/agentic/affect/cognitive_empathy/helpers/perspective_spec.rb +132 -0
  140. data/spec/legion/extensions/agentic/affect/cognitive_empathy/runners/cognitive_empathy_spec.rb +200 -0
  141. data/spec/legion/extensions/agentic/affect/contagion/client_spec.rb +63 -0
  142. data/spec/legion/extensions/agentic/affect/contagion/helpers/constants_spec.rb +86 -0
  143. data/spec/legion/extensions/agentic/affect/contagion/helpers/contagion_engine_spec.rb +241 -0
  144. data/spec/legion/extensions/agentic/affect/contagion/helpers/meme_spec.rb +160 -0
  145. data/spec/legion/extensions/agentic/affect/contagion/runners/cognitive_contagion_spec.rb +211 -0
  146. data/spec/legion/extensions/agentic/affect/defusion/client_spec.rb +80 -0
  147. data/spec/legion/extensions/agentic/affect/defusion/helpers/constants_spec.rb +84 -0
  148. data/spec/legion/extensions/agentic/affect/defusion/helpers/defusion_engine_spec.rb +250 -0
  149. data/spec/legion/extensions/agentic/affect/defusion/helpers/thought_spec.rb +178 -0
  150. data/spec/legion/extensions/agentic/affect/defusion/runners/cognitive_defusion_spec.rb +185 -0
  151. data/spec/legion/extensions/agentic/affect/emotion/actors/momentum_decay_spec.rb +46 -0
  152. data/spec/legion/extensions/agentic/affect/emotion/client_spec.rb +46 -0
  153. data/spec/legion/extensions/agentic/affect/emotion/helpers/baseline_spec.rb +48 -0
  154. data/spec/legion/extensions/agentic/affect/emotion/helpers/momentum_spec.rb +45 -0
  155. data/spec/legion/extensions/agentic/affect/emotion/helpers/valence_spec.rb +91 -0
  156. data/spec/legion/extensions/agentic/affect/emotion/runners/gut_spec.rb +73 -0
  157. data/spec/legion/extensions/agentic/affect/emotion/runners/valence_spec.rb +67 -0
  158. data/spec/legion/extensions/agentic/affect/empathy/client_spec.rb +20 -0
  159. data/spec/legion/extensions/agentic/affect/empathy/helpers/constants_spec.rb +23 -0
  160. data/spec/legion/extensions/agentic/affect/empathy/helpers/mental_model_spec.rb +150 -0
  161. data/spec/legion/extensions/agentic/affect/empathy/helpers/model_store_spec.rb +94 -0
  162. data/spec/legion/extensions/agentic/affect/empathy/runners/empathy_spec.rb +127 -0
  163. data/spec/legion/extensions/agentic/affect/fatigue/client_spec.rb +66 -0
  164. data/spec/legion/extensions/agentic/affect/fatigue/helpers/constants_spec.rb +130 -0
  165. data/spec/legion/extensions/agentic/affect/fatigue/helpers/energy_model_spec.rb +281 -0
  166. data/spec/legion/extensions/agentic/affect/fatigue/helpers/fatigue_store_spec.rb +157 -0
  167. data/spec/legion/extensions/agentic/affect/fatigue/runners/fatigue_spec.rb +127 -0
  168. data/spec/legion/extensions/agentic/affect/flow/client_spec.rb +58 -0
  169. data/spec/legion/extensions/agentic/affect/flow/helpers/constants_spec.rb +112 -0
  170. data/spec/legion/extensions/agentic/affect/flow/helpers/flow_detector_spec.rb +268 -0
  171. data/spec/legion/extensions/agentic/affect/flow/runners/flow_spec.rb +222 -0
  172. data/spec/legion/extensions/agentic/affect/interoception/client_spec.rb +52 -0
  173. data/spec/legion/extensions/agentic/affect/interoception/helpers/body_budget_spec.rb +178 -0
  174. data/spec/legion/extensions/agentic/affect/interoception/helpers/somatic_marker_spec.rb +120 -0
  175. data/spec/legion/extensions/agentic/affect/interoception/runners/interoception_spec.rb +108 -0
  176. data/spec/legion/extensions/agentic/affect/mood/client_spec.rb +20 -0
  177. data/spec/legion/extensions/agentic/affect/mood/helpers/constants_spec.rb +29 -0
  178. data/spec/legion/extensions/agentic/affect/mood/helpers/mood_state_spec.rb +94 -0
  179. data/spec/legion/extensions/agentic/affect/mood/runners/mood_spec.rb +71 -0
  180. data/spec/legion/extensions/agentic/affect/motivation/client_spec.rb +35 -0
  181. data/spec/legion/extensions/agentic/affect/motivation/helpers/constants_spec.rb +111 -0
  182. data/spec/legion/extensions/agentic/affect/motivation/helpers/drive_state_spec.rb +183 -0
  183. data/spec/legion/extensions/agentic/affect/motivation/helpers/motivation_store_spec.rb +185 -0
  184. data/spec/legion/extensions/agentic/affect/motivation/runners/motivation_spec.rb +248 -0
  185. data/spec/legion/extensions/agentic/affect/reappraisal/actors/auto_regulate_spec.rb +46 -0
  186. data/spec/legion/extensions/agentic/affect/reappraisal/client_spec.rb +64 -0
  187. data/spec/legion/extensions/agentic/affect/reappraisal/helpers/constants_spec.rb +102 -0
  188. data/spec/legion/extensions/agentic/affect/reappraisal/helpers/emotional_event_spec.rb +177 -0
  189. data/spec/legion/extensions/agentic/affect/reappraisal/helpers/llm_enhancer_spec.rb +161 -0
  190. data/spec/legion/extensions/agentic/affect/reappraisal/helpers/reappraisal_engine_spec.rb +211 -0
  191. data/spec/legion/extensions/agentic/affect/reappraisal/runners/cognitive_reappraisal_spec.rb +312 -0
  192. data/spec/legion/extensions/agentic/affect/regulation/client_spec.rb +61 -0
  193. data/spec/legion/extensions/agentic/affect/regulation/helpers/constants_spec.rb +108 -0
  194. data/spec/legion/extensions/agentic/affect/regulation/helpers/regulation_model_spec.rb +200 -0
  195. data/spec/legion/extensions/agentic/affect/regulation/runners/emotional_regulation_spec.rb +190 -0
  196. data/spec/legion/extensions/agentic/affect/resilience/client_spec.rb +36 -0
  197. data/spec/legion/extensions/agentic/affect/resilience/helpers/adversity_tracker_spec.rb +164 -0
  198. data/spec/legion/extensions/agentic/affect/resilience/helpers/constants_spec.rb +78 -0
  199. data/spec/legion/extensions/agentic/affect/resilience/helpers/resilience_model_spec.rb +133 -0
  200. data/spec/legion/extensions/agentic/affect/resilience/runners/resilience_spec.rb +150 -0
  201. data/spec/legion/extensions/agentic/affect/resonance/client_spec.rb +66 -0
  202. data/spec/legion/extensions/agentic/affect/resonance/cognitive_resonance_spec.rb +27 -0
  203. data/spec/legion/extensions/agentic/affect/resonance/helpers/category_spec.rb +146 -0
  204. data/spec/legion/extensions/agentic/affect/resonance/helpers/constants_spec.rb +104 -0
  205. data/spec/legion/extensions/agentic/affect/resonance/helpers/resonance_engine_spec.rb +189 -0
  206. data/spec/legion/extensions/agentic/affect/resonance/runners/cognitive_resonance_spec.rb +197 -0
  207. data/spec/legion/extensions/agentic/affect/reward/client_spec.rb +42 -0
  208. data/spec/legion/extensions/agentic/affect/reward/helpers/constants_spec.rb +91 -0
  209. data/spec/legion/extensions/agentic/affect/reward/helpers/reward_signal_spec.rb +296 -0
  210. data/spec/legion/extensions/agentic/affect/reward/helpers/reward_store_spec.rb +167 -0
  211. data/spec/legion/extensions/agentic/affect/reward/runners/reward_spec.rb +149 -0
  212. data/spec/legion/extensions/agentic/affect/somatic_marker/client_spec.rb +83 -0
  213. data/spec/legion/extensions/agentic/affect/somatic_marker/helpers/body_state_spec.rb +155 -0
  214. data/spec/legion/extensions/agentic/affect/somatic_marker/helpers/marker_store_spec.rb +233 -0
  215. data/spec/legion/extensions/agentic/affect/somatic_marker/helpers/somatic_marker_spec.rb +172 -0
  216. data/spec/legion/extensions/agentic/affect/somatic_marker/runners/somatic_marker_spec.rb +181 -0
  217. data/spec/spec_helper.rb +46 -0
  218. metadata +302 -0
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Affect::Contagion::Helpers::Constants do
4
+ describe 'numeric constants' do
5
+ it 'defines MAX_AGENTS' do
6
+ expect(described_class::MAX_AGENTS).to eq(200)
7
+ end
8
+
9
+ it 'defines MAX_MEMES' do
10
+ expect(described_class::MAX_MEMES).to eq(500)
11
+ end
12
+
13
+ it 'defines DEFAULT_VIRULENCE' do
14
+ expect(described_class::DEFAULT_VIRULENCE).to eq(0.3)
15
+ end
16
+
17
+ it 'defines DEFAULT_RESISTANCE' do
18
+ expect(described_class::DEFAULT_RESISTANCE).to eq(0.5)
19
+ end
20
+
21
+ it 'defines TRANSMISSION_RATE' do
22
+ expect(described_class::TRANSMISSION_RATE).to eq(0.15)
23
+ end
24
+
25
+ it 'defines RECOVERY_RATE' do
26
+ expect(described_class::RECOVERY_RATE).to eq(0.05)
27
+ end
28
+
29
+ it 'defines IMMUNITY_BOOST' do
30
+ expect(described_class::IMMUNITY_BOOST).to eq(0.1)
31
+ end
32
+ end
33
+
34
+ describe 'VIRULENCE_LABELS' do
35
+ let(:labels) { described_class::VIRULENCE_LABELS }
36
+
37
+ it 'maps 0.9 to pandemic' do
38
+ result = labels.find { |range, _| range.cover?(0.9) }&.last
39
+ expect(result).to eq(:pandemic)
40
+ end
41
+
42
+ it 'maps 0.7 to epidemic' do
43
+ result = labels.find { |range, _| range.cover?(0.7) }&.last
44
+ expect(result).to eq(:epidemic)
45
+ end
46
+
47
+ it 'maps 0.5 to endemic' do
48
+ result = labels.find { |range, _| range.cover?(0.5) }&.last
49
+ expect(result).to eq(:endemic)
50
+ end
51
+
52
+ it 'maps 0.3 to sporadic' do
53
+ result = labels.find { |range, _| range.cover?(0.3) }&.last
54
+ expect(result).to eq(:sporadic)
55
+ end
56
+
57
+ it 'maps 0.1 to contained' do
58
+ result = labels.find { |range, _| range.cover?(0.1) }&.last
59
+ expect(result).to eq(:contained)
60
+ end
61
+
62
+ it 'is frozen' do
63
+ expect(labels).to be_frozen
64
+ end
65
+ end
66
+
67
+ describe 'STATUS_LABELS' do
68
+ it 'contains all five states' do
69
+ expect(described_class::STATUS_LABELS).to eq(%i[susceptible exposed infected recovered immune])
70
+ end
71
+
72
+ it 'is frozen' do
73
+ expect(described_class::STATUS_LABELS).to be_frozen
74
+ end
75
+ end
76
+
77
+ describe 'CONTAGION_TYPES' do
78
+ it 'contains four types' do
79
+ expect(described_class::CONTAGION_TYPES).to eq(%i[emotional belief behavioral cognitive])
80
+ end
81
+
82
+ it 'is frozen' do
83
+ expect(described_class::CONTAGION_TYPES).to be_frozen
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,241 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Affect::Contagion::Helpers::ContagionEngine do
4
+ subject(:engine) { described_class.new }
5
+
6
+ let(:meme) { engine.create_meme(label: 'panic', contagion_type: :emotional, virulence: 0.9) }
7
+ let(:meme_id) { meme.id }
8
+
9
+ before do
10
+ engine.register_agent(agent_id: 'alice', resistance: 0.0)
11
+ engine.register_agent(agent_id: 'bob', resistance: 0.0)
12
+ engine.register_agent(agent_id: 'carol', resistance: 0.0)
13
+ end
14
+
15
+ describe '#create_meme' do
16
+ it 'returns a Meme object' do
17
+ expect(meme).to be_a(Legion::Extensions::Agentic::Affect::Contagion::Helpers::Meme)
18
+ end
19
+
20
+ it 'stores the meme in the engine' do
21
+ expect(engine.memes[meme_id]).to eq(meme)
22
+ end
23
+
24
+ it 'applies default virulence when not specified' do
25
+ m = engine.create_meme(label: 'quiet')
26
+ expect(m.virulence).to eq(Legion::Extensions::Agentic::Affect::Contagion::Helpers::Constants::DEFAULT_VIRULENCE)
27
+ end
28
+ end
29
+
30
+ describe '#register_agent' do
31
+ it 'stores agent resistance' do
32
+ expect(engine.agent_resistance['alice']).to eq(0.0)
33
+ end
34
+
35
+ it 'clamps resistance to [0,1]' do
36
+ engine.register_agent(agent_id: 'dave', resistance: 2.0)
37
+ expect(engine.agent_resistance['dave']).to eq(1.0)
38
+ end
39
+
40
+ it 'applies default resistance when not specified' do
41
+ engine.register_agent(agent_id: 'eve')
42
+ expect(engine.agent_resistance['eve']).to eq(
43
+ Legion::Extensions::Agentic::Affect::Contagion::Helpers::Constants::DEFAULT_RESISTANCE
44
+ )
45
+ end
46
+ end
47
+
48
+ describe '#attempt_transmission' do
49
+ before { meme.infect!(agent_id: 'alice') }
50
+
51
+ it 'returns meme_not_found for unknown meme' do
52
+ result = engine.attempt_transmission(meme_id: 'bad-id', source_agent_id: 'alice',
53
+ target_agent_id: 'bob')
54
+ expect(result[:reason]).to eq(:meme_not_found)
55
+ end
56
+
57
+ it 'returns source_not_carrying if source does not carry' do
58
+ result = engine.attempt_transmission(meme_id: meme_id, source_agent_id: 'bob',
59
+ target_agent_id: 'carol')
60
+ expect(result[:reason]).to eq(:source_not_carrying)
61
+ end
62
+
63
+ it 'includes probability in result' do
64
+ result = engine.attempt_transmission(meme_id: meme_id, source_agent_id: 'alice',
65
+ target_agent_id: 'bob')
66
+ expect(result[:probability]).to be_a(Float)
67
+ expect(result[:probability]).to be >= 0.0
68
+ end
69
+
70
+ it 'transmits with probability approaching 1.0 at max virulence and zero resistance' do
71
+ transmitted = (1..100).count do
72
+ e = described_class.new
73
+ e.register_agent(agent_id: 'src', resistance: 0.0)
74
+ e.register_agent(agent_id: 'tgt', resistance: 0.0)
75
+ m = e.create_meme(label: 'x', virulence: 1.0)
76
+ m.infect!(agent_id: 'src')
77
+ r = e.attempt_transmission(meme_id: m.id, source_agent_id: 'src', target_agent_id: 'tgt')
78
+ r[:transmitted]
79
+ end
80
+ expect(transmitted).to be > 0
81
+ end
82
+ end
83
+
84
+ describe '#recover_agent' do
85
+ before { meme.infect!(agent_id: 'alice') }
86
+
87
+ it 'recovers the agent from the meme' do
88
+ result = engine.recover_agent(meme_id: meme_id, agent_id: 'alice')
89
+ expect(result[:recovered]).to be true
90
+ end
91
+
92
+ it 'boosts agent resistance after recovery' do
93
+ before_resistance = engine.agent_resistance['alice']
94
+ engine.recover_agent(meme_id: meme_id, agent_id: 'alice')
95
+ expect(engine.agent_resistance['alice']).to be > before_resistance
96
+ end
97
+
98
+ it 'returns meme_not_found for unknown meme' do
99
+ result = engine.recover_agent(meme_id: 'bad-id', agent_id: 'alice')
100
+ expect(result[:recovered]).to be false
101
+ expect(result[:reason]).to eq(:meme_not_found)
102
+ end
103
+ end
104
+
105
+ describe '#immunize_agent' do
106
+ it 'immunizes the agent' do
107
+ result = engine.immunize_agent(meme_id: meme_id, agent_id: 'alice')
108
+ expect(result[:immunized]).to be true
109
+ end
110
+
111
+ it 'prevents future infection' do
112
+ engine.immunize_agent(meme_id: meme_id, agent_id: 'alice')
113
+ infect_result = meme.infect!(agent_id: 'alice')
114
+ expect(infect_result).to eq(:immune)
115
+ end
116
+ end
117
+
118
+ describe '#spread_step' do
119
+ before { meme.infect!(agent_id: 'alice') }
120
+
121
+ it 'returns a result hash with transmissions key' do
122
+ result = engine.spread_step(meme_id: meme_id)
123
+ expect(result).to have_key(:transmissions)
124
+ end
125
+
126
+ it 'returns meme_not_found for unknown meme' do
127
+ result = engine.spread_step(meme_id: 'bad-id')
128
+ expect(result[:reason]).to eq(:meme_not_found)
129
+ end
130
+
131
+ it 'tracks carrier_count in result' do
132
+ result = engine.spread_step(meme_id: meme_id)
133
+ expect(result[:carrier_count]).to be_a(Integer)
134
+ end
135
+ end
136
+
137
+ describe '#epidemic_report' do
138
+ before { meme.infect!(agent_id: 'alice') }
139
+
140
+ it 'returns SIR counts' do
141
+ report = engine.epidemic_report(meme_id: meme_id)
142
+ expect(report[:infected]).to eq(1)
143
+ expect(report[:susceptible]).to be >= 0
144
+ end
145
+
146
+ it 'includes virulence_label' do
147
+ report = engine.epidemic_report(meme_id: meme_id)
148
+ expect(report[:virulence_label]).to eq(:pandemic)
149
+ end
150
+
151
+ it 'returns meme_not_found for unknown meme' do
152
+ result = engine.epidemic_report(meme_id: 'bad-id')
153
+ expect(result[:error]).to eq(:meme_not_found)
154
+ end
155
+ end
156
+
157
+ describe '#most_viral' do
158
+ before do
159
+ engine.create_meme(label: 'high', virulence: 0.9)
160
+ engine.create_meme(label: 'medium', virulence: 0.5)
161
+ engine.create_meme(label: 'low', virulence: 0.1)
162
+ end
163
+
164
+ it 'returns hashes sorted by virulence descending' do
165
+ results = engine.most_viral(limit: 3)
166
+ virulences = results.map { |m| m[:virulence] }
167
+ expect(virulences).to eq(virulences.sort.reverse)
168
+ end
169
+
170
+ it 'respects the limit' do
171
+ results = engine.most_viral(limit: 2)
172
+ expect(results.size).to be <= 2
173
+ end
174
+ end
175
+
176
+ describe '#agent_status' do
177
+ it 'returns susceptible for uninfected agent' do
178
+ result = engine.agent_status(agent_id: 'bob', meme_id: meme_id)
179
+ expect(result[:status]).to eq(:susceptible)
180
+ end
181
+
182
+ it 'returns infected for carrying agent' do
183
+ meme.infect!(agent_id: 'bob')
184
+ result = engine.agent_status(agent_id: 'bob', meme_id: meme_id)
185
+ expect(result[:status]).to eq(:infected)
186
+ end
187
+
188
+ it 'returns recovered for recovered agent' do
189
+ meme.infect!(agent_id: 'bob')
190
+ meme.recover!(agent_id: 'bob')
191
+ result = engine.agent_status(agent_id: 'bob', meme_id: meme_id)
192
+ expect(result[:status]).to eq(:recovered)
193
+ end
194
+
195
+ it 'returns immune for immunized agent' do
196
+ engine.immunize_agent(meme_id: meme_id, agent_id: 'bob')
197
+ result = engine.agent_status(agent_id: 'bob', meme_id: meme_id)
198
+ expect(result[:status]).to eq(:immune)
199
+ end
200
+
201
+ it 'returns unknown with meme_not_found for bad meme id' do
202
+ result = engine.agent_status(agent_id: 'bob', meme_id: 'bad-id')
203
+ expect(result[:status]).to eq(:unknown)
204
+ end
205
+ end
206
+
207
+ describe '#susceptible_agents' do
208
+ it 'returns all agents when no one is infected or immune' do
209
+ agents = engine.susceptible_agents(meme_id: meme_id)
210
+ expect(agents).to match_array(%w[alice bob carol])
211
+ end
212
+
213
+ it 'excludes carriers' do
214
+ meme.infect!(agent_id: 'alice')
215
+ agents = engine.susceptible_agents(meme_id: meme_id)
216
+ expect(agents).not_to include('alice')
217
+ end
218
+
219
+ it 'excludes immune agents' do
220
+ engine.immunize_agent(meme_id: meme_id, agent_id: 'bob')
221
+ agents = engine.susceptible_agents(meme_id: meme_id)
222
+ expect(agents).not_to include('bob')
223
+ end
224
+
225
+ it 'returns empty array for unknown meme' do
226
+ expect(engine.susceptible_agents(meme_id: 'bad-id')).to eq([])
227
+ end
228
+ end
229
+
230
+ describe '#to_h' do
231
+ it 'returns meme_count and agent_count' do
232
+ h = engine.to_h
233
+ expect(h[:meme_count]).to eq(engine.memes.size)
234
+ expect(h[:agent_count]).to eq(engine.agent_resistance.size)
235
+ end
236
+
237
+ it 'includes memes array' do
238
+ expect(engine.to_h[:memes]).to be_an(Array)
239
+ end
240
+ end
241
+ end
@@ -0,0 +1,160 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Affect::Contagion::Helpers::Meme do
4
+ subject(:meme) { described_class.new(label: 'test-meme', contagion_type: :belief, virulence: 0.6) }
5
+
6
+ describe '#initialize' do
7
+ it 'assigns a uuid id' do
8
+ expect(meme.id).to match(/\A[0-9a-f-]{36}\z/)
9
+ end
10
+
11
+ it 'stores the label' do
12
+ expect(meme.label).to eq('test-meme')
13
+ end
14
+
15
+ it 'stores the contagion_type' do
16
+ expect(meme.contagion_type).to eq(:belief)
17
+ end
18
+
19
+ it 'clamps virulence to [0,1]' do
20
+ m = described_class.new(label: 'x', virulence: 1.5)
21
+ expect(m.virulence).to eq(1.0)
22
+ end
23
+
24
+ it 'clamps virulence lower bound' do
25
+ m = described_class.new(label: 'x', virulence: -0.5)
26
+ expect(m.virulence).to eq(0.0)
27
+ end
28
+
29
+ it 'starts with empty carrier set' do
30
+ expect(meme.carriers).to be_empty
31
+ end
32
+
33
+ it 'starts with empty recovered set' do
34
+ expect(meme.recovered).to be_empty
35
+ end
36
+
37
+ it 'starts with empty immune set' do
38
+ expect(meme.immune).to be_empty
39
+ end
40
+
41
+ it 'starts with zero total_transmissions' do
42
+ expect(meme.total_transmissions).to eq(0)
43
+ end
44
+
45
+ it 'defaults unknown contagion_type to :cognitive' do
46
+ m = described_class.new(label: 'x', contagion_type: :invalid)
47
+ expect(m.contagion_type).to eq(:cognitive)
48
+ end
49
+ end
50
+
51
+ describe '#virulence_label' do
52
+ it 'returns :epidemic for 0.6' do
53
+ expect(meme.virulence_label).to eq(:epidemic)
54
+ end
55
+
56
+ it 'returns :pandemic for 0.9' do
57
+ m = described_class.new(label: 'x', virulence: 0.9)
58
+ expect(m.virulence_label).to eq(:pandemic)
59
+ end
60
+
61
+ it 'returns :contained for 0.1' do
62
+ m = described_class.new(label: 'x', virulence: 0.1)
63
+ expect(m.virulence_label).to eq(:contained)
64
+ end
65
+ end
66
+
67
+ describe '#infect!' do
68
+ it 'adds agent to carriers and returns :infected' do
69
+ result = meme.infect!(agent_id: 'agent-1')
70
+ expect(result).to eq(:infected)
71
+ expect(meme.carriers).to include('agent-1')
72
+ end
73
+
74
+ it 'returns :already_carrier for a second infect call' do
75
+ meme.infect!(agent_id: 'agent-1')
76
+ expect(meme.infect!(agent_id: 'agent-1')).to eq(:already_carrier)
77
+ end
78
+
79
+ it 'returns :immune if agent is immune' do
80
+ meme.immunize!(agent_id: 'agent-1')
81
+ expect(meme.infect!(agent_id: 'agent-1')).to eq(:immune)
82
+ end
83
+
84
+ it 'increments total_transmissions' do
85
+ meme.infect!(agent_id: 'agent-1')
86
+ expect(meme.total_transmissions).to eq(1)
87
+ end
88
+ end
89
+
90
+ describe '#recover!' do
91
+ before { meme.infect!(agent_id: 'agent-1') }
92
+
93
+ it 'removes from carriers and adds to recovered' do
94
+ result = meme.recover!(agent_id: 'agent-1')
95
+ expect(result).to eq(:recovered)
96
+ expect(meme.carriers).not_to include('agent-1')
97
+ expect(meme.recovered).to include('agent-1')
98
+ end
99
+
100
+ it 'returns :not_a_carrier if not carrying' do
101
+ expect(meme.recover!(agent_id: 'nobody')).to eq(:not_a_carrier)
102
+ end
103
+ end
104
+
105
+ describe '#immunize!' do
106
+ it 'adds to immune set' do
107
+ meme.immunize!(agent_id: 'agent-1')
108
+ expect(meme.immune).to include('agent-1')
109
+ end
110
+
111
+ it 'removes from carriers if carrying' do
112
+ meme.infect!(agent_id: 'agent-1')
113
+ meme.immunize!(agent_id: 'agent-1')
114
+ expect(meme.carriers).not_to include('agent-1')
115
+ end
116
+ end
117
+
118
+ describe '#carrying?' do
119
+ it 'returns true when agent is a carrier' do
120
+ meme.infect!(agent_id: 'agent-1')
121
+ expect(meme.carrying?(agent_id: 'agent-1')).to be true
122
+ end
123
+
124
+ it 'returns false when agent is not a carrier' do
125
+ expect(meme.carrying?(agent_id: 'nobody')).to be false
126
+ end
127
+ end
128
+
129
+ describe '#carrier_count' do
130
+ it 'returns zero initially' do
131
+ expect(meme.carrier_count).to eq(0)
132
+ end
133
+
134
+ it 'returns count after infection' do
135
+ meme.infect!(agent_id: 'a')
136
+ meme.infect!(agent_id: 'b')
137
+ expect(meme.carrier_count).to eq(2)
138
+ end
139
+ end
140
+
141
+ describe '#transmission_rate' do
142
+ it 'returns 0.0 with no carriers' do
143
+ expect(meme.transmission_rate).to eq(0.0)
144
+ end
145
+
146
+ it 'returns ratio of transmissions to carriers' do
147
+ meme.infect!(agent_id: 'a')
148
+ expect(meme.transmission_rate).to be > 0.0
149
+ end
150
+ end
151
+
152
+ describe '#to_h' do
153
+ it 'returns a hash with expected keys' do
154
+ h = meme.to_h
155
+ expect(h.keys).to include(:id, :label, :contagion_type, :virulence, :virulence_label,
156
+ :carrier_count, :recovered_count, :immune_count,
157
+ :total_transmissions, :transmission_rate, :created_at)
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,211 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/affect/contagion/client'
4
+
5
+ RSpec.describe Legion::Extensions::Agentic::Affect::Contagion::Runners::CognitiveContagion do
6
+ let(:client) { Legion::Extensions::Agentic::Affect::Contagion::Client.new }
7
+
8
+ let(:meme_result) { client.create_meme(label: 'test-meme', contagion_type: :belief, virulence: 0.7) }
9
+ let(:meme_id) { meme_result[:id] }
10
+
11
+ before do
12
+ client.register_agent(agent_id: 'alice', resistance: 0.0)
13
+ client.register_agent(agent_id: 'bob', resistance: 0.0)
14
+ client.register_agent(agent_id: 'carol', resistance: 0.8)
15
+ end
16
+
17
+ describe '#create_meme' do
18
+ it 'returns a hash with id' do
19
+ expect(meme_result[:id]).to match(/\A[0-9a-f-]{36}\z/)
20
+ end
21
+
22
+ it 'returns the label' do
23
+ expect(meme_result[:label]).to eq('test-meme')
24
+ end
25
+
26
+ it 'returns virulence_label' do
27
+ expect(meme_result[:virulence_label]).to eq(:epidemic)
28
+ end
29
+
30
+ it 'uses default virulence when not provided' do
31
+ result = client.create_meme(label: 'quiet')
32
+ expect(result[:virulence]).to eq(Legion::Extensions::Agentic::Affect::Contagion::Helpers::Constants::DEFAULT_VIRULENCE)
33
+ end
34
+ end
35
+
36
+ describe '#register_agent' do
37
+ it 'returns agent_id and resistance' do
38
+ result = client.register_agent(agent_id: 'dave', resistance: 0.3)
39
+ expect(result[:agent_id]).to eq('dave')
40
+ expect(result[:resistance]).to eq(0.3)
41
+ end
42
+
43
+ it 'uses default resistance when not provided' do
44
+ result = client.register_agent(agent_id: 'eve')
45
+ expect(result[:resistance]).to eq(
46
+ Legion::Extensions::Agentic::Affect::Contagion::Helpers::Constants::DEFAULT_RESISTANCE
47
+ )
48
+ end
49
+ end
50
+
51
+ describe '#infect_agent' do
52
+ it 'infects the agent and returns infected: true' do
53
+ result = client.infect_agent(meme_id: meme_id, agent_id: 'alice')
54
+ expect(result[:infected]).to be true
55
+ expect(result[:result]).to eq(:infected)
56
+ end
57
+
58
+ it 'returns already_carrier on second call' do
59
+ client.infect_agent(meme_id: meme_id, agent_id: 'alice')
60
+ result = client.infect_agent(meme_id: meme_id, agent_id: 'alice')
61
+ expect(result[:result]).to eq(:already_carrier)
62
+ end
63
+
64
+ it 'returns meme_not_found for unknown meme' do
65
+ result = client.infect_agent(meme_id: 'bad-id', agent_id: 'alice')
66
+ expect(result[:infected]).to be false
67
+ expect(result[:reason]).to eq(:meme_not_found)
68
+ end
69
+ end
70
+
71
+ describe '#attempt_transmission' do
72
+ before { client.infect_agent(meme_id: meme_id, agent_id: 'alice') }
73
+
74
+ it 'returns a result with transmitted key' do
75
+ result = client.attempt_transmission(meme_id: meme_id, source_agent_id: 'alice',
76
+ target_agent_id: 'bob')
77
+ expect(result).to have_key(:transmitted)
78
+ end
79
+
80
+ it 'returns meme_not_found for unknown meme' do
81
+ result = client.attempt_transmission(meme_id: 'bad', source_agent_id: 'alice',
82
+ target_agent_id: 'bob')
83
+ expect(result[:reason]).to eq(:meme_not_found)
84
+ end
85
+ end
86
+
87
+ describe '#recover_agent' do
88
+ before { client.infect_agent(meme_id: meme_id, agent_id: 'alice') }
89
+
90
+ it 'returns recovered true' do
91
+ result = client.recover_agent(meme_id: meme_id, agent_id: 'alice')
92
+ expect(result[:recovered]).to be true
93
+ end
94
+
95
+ it 'handles unknown meme gracefully' do
96
+ result = client.recover_agent(meme_id: 'bad', agent_id: 'alice')
97
+ expect(result[:recovered]).to be false
98
+ end
99
+ end
100
+
101
+ describe '#immunize_agent' do
102
+ it 'returns immunized true' do
103
+ result = client.immunize_agent(meme_id: meme_id, agent_id: 'alice')
104
+ expect(result[:immunized]).to be true
105
+ end
106
+ end
107
+
108
+ describe '#spread_step' do
109
+ before { client.infect_agent(meme_id: meme_id, agent_id: 'alice') }
110
+
111
+ it 'returns a hash with transmissions' do
112
+ result = client.spread_step(meme_id: meme_id)
113
+ expect(result).to have_key(:transmissions)
114
+ expect(result[:transmissions]).to be_a(Integer)
115
+ end
116
+
117
+ it 'returns meme_not_found reason for bad meme' do
118
+ result = client.spread_step(meme_id: 'bad')
119
+ expect(result[:reason]).to eq(:meme_not_found)
120
+ end
121
+ end
122
+
123
+ describe '#epidemic_report' do
124
+ before { client.infect_agent(meme_id: meme_id, agent_id: 'alice') }
125
+
126
+ it 'returns SIR statistics' do
127
+ report = client.epidemic_report(meme_id: meme_id)
128
+ expect(report[:infected]).to eq(1)
129
+ expect(report[:susceptible]).to be >= 0
130
+ expect(report[:recovered]).to eq(0)
131
+ end
132
+
133
+ it 'includes virulence_label' do
134
+ report = client.epidemic_report(meme_id: meme_id)
135
+ expect(report).to have_key(:virulence_label)
136
+ end
137
+ end
138
+
139
+ describe '#most_viral' do
140
+ before do
141
+ client.create_meme(label: 'high', virulence: 0.95)
142
+ client.create_meme(label: 'low', virulence: 0.05)
143
+ end
144
+
145
+ it 'returns a hash with memes array' do
146
+ result = client.most_viral
147
+ expect(result[:memes]).to be_an(Array)
148
+ end
149
+
150
+ it 'returns count of memes' do
151
+ result = client.most_viral(limit: 2)
152
+ expect(result[:count]).to be <= 2
153
+ end
154
+
155
+ it 'sorts by virulence descending' do
156
+ result = client.most_viral
157
+ virulences = result[:memes].map { |m| m[:virulence] }
158
+ expect(virulences).to eq(virulences.sort.reverse)
159
+ end
160
+ end
161
+
162
+ describe '#agent_status' do
163
+ it 'returns susceptible for uninfected registered agent' do
164
+ result = client.agent_status(agent_id: 'bob', meme_id: meme_id)
165
+ expect(result[:status]).to eq(:susceptible)
166
+ end
167
+
168
+ it 'returns infected after infection' do
169
+ client.infect_agent(meme_id: meme_id, agent_id: 'bob')
170
+ result = client.agent_status(agent_id: 'bob', meme_id: meme_id)
171
+ expect(result[:status]).to eq(:infected)
172
+ end
173
+
174
+ it 'returns immune after immunization' do
175
+ client.immunize_agent(meme_id: meme_id, agent_id: 'carol')
176
+ result = client.agent_status(agent_id: 'carol', meme_id: meme_id)
177
+ expect(result[:status]).to eq(:immune)
178
+ end
179
+ end
180
+
181
+ describe '#susceptible_agents' do
182
+ it 'returns agents and count' do
183
+ result = client.susceptible_agents(meme_id: meme_id)
184
+ expect(result[:agents]).to be_an(Array)
185
+ expect(result[:count]).to eq(result[:agents].size)
186
+ end
187
+
188
+ it 'returns meme_id in result' do
189
+ result = client.susceptible_agents(meme_id: meme_id)
190
+ expect(result[:meme_id]).to eq(meme_id)
191
+ end
192
+ end
193
+
194
+ describe '#contagion_status' do
195
+ it 'returns meme_count and agent_count' do
196
+ result = client.contagion_status
197
+ expect(result[:meme_count]).to be_a(Integer)
198
+ expect(result[:agent_count]).to be_a(Integer)
199
+ end
200
+
201
+ it 'returns contagion_types list' do
202
+ expect(client.contagion_status[:contagion_types]).to eq(%i[emotional belief behavioral cognitive])
203
+ end
204
+
205
+ it 'returns max_agents and max_memes' do
206
+ result = client.contagion_status
207
+ expect(result[:max_agents]).to eq(200)
208
+ expect(result[:max_memes]).to eq(500)
209
+ end
210
+ end
211
+ end