Coverage for test/test_agent.py: 99%
405 statements
« prev ^ index » next coverage.py v7.2.3, created at 2023-05-04 13:14 +0700
« prev ^ index » next coverage.py v7.2.3, created at 2023-05-04 13:14 +0700
1import copy
2import datetime
3import pytest
4from ..agent_model.agents import BaseAgent
5from ..agent_model.util import get_default_currency_data
7@pytest.fixture
8def kwargs():
9 return {
10 'model': object(),
11 'agent_id': 'test_agent',
12 'amount': 10,
13 'description': 'test_description',
14 'agent_class': 'test_agent_class',
15 'properties': {'test_property': {'value': 1}},
16 'capacity': {'test_currency': 2},
17 'thresholds': {'test_currency': {
18 'path': 'test_currency',
19 'limit': '<',
20 'value': 0.5,
21 }},
22 'flows': {
23 'in': {
24 'test_currency': {
25 'value': 1,
26 'connections': ['test_agent_2']
27 }
28 },
29 'out': {
30 'test_currency': {
31 'value': 1,
32 'connections': ['test_agent_2']
33 }
34 }
35 },
36 'cause_of_death': 'test_death',
37 'active': 5,
38 'storage': {'test_currency': 1},
39 'attributes': {'test_attribute': 1},
40 }
42class TestAgentInit:
43 def test_agent_init_empty(self):
44 """Confirm that all attributes are set correctly when no kwargs are passed"""
45 model = object()
46 test_agent = BaseAgent(model, 'test_agent')
47 assert test_agent.agent_id == 'test_agent'
48 assert test_agent.amount == 1
49 assert test_agent.model == model
50 assert test_agent.registered == False
51 assert test_agent.cause_of_death == None
52 assert str(test_agent.flows) == str({'in': {}, 'out': {}})
53 empty_strings = {'description', 'agent_class'}
54 empty_dicts = {'properties', 'capacity', 'thresholds', 'storage', 'attributes', 'records'}
55 for k in empty_strings:
56 assert getattr(test_agent, k) == ''
57 for k in empty_dicts:
58 assert str(getattr(test_agent, k)) == str({})
60 def test_agent_init_full(self, kwargs):
61 """Confirm that all kwargs are set correctly"""
62 test_agent = BaseAgent(**kwargs)
63 for k, v in kwargs.items():
64 assert str(getattr(test_agent, k)) == str(v)
66 def test_agent_init_kwargs_immutable(self, kwargs):
67 """Test that the kwargs passed to Agent.__init__() are immutable.
69 We pass a set of kwargs to the Agent class and modify them outside of
70 the class. Then, we confirm that the Agent's internal attributes are
71 not modified by the external changes to the kwargs object. This test
72 ensures that the Agent's initialization process correctly creates
73 a copy of the kwargs object to ensure immutability."""
74 test_agent = BaseAgent(**kwargs)
75 # Confirm that class is not
76 def recursively_modify_kwargs(obj):
77 if isinstance(obj, dict):
78 for k, v in obj.items():
79 obj[k] = recursively_modify_kwargs(v)
80 elif isinstance(obj, object):
81 return object()
82 elif isinstance(obj, list):
83 return [recursively_modify_kwargs(i) for i in obj]
84 elif isinstance(obj, (int, float)):
85 return obj + 1
86 else:
87 return f'{obj}_modified'
88 recursively_modify_kwargs(kwargs)
89 for k, v in kwargs.items():
90 assert str(getattr(test_agent, k)) != str(v)
92@pytest.fixture
93def mock_model():
94 class MockModel:
95 floating_point_precision = 6
96 agents = {}
97 time = datetime.datetime(2020, 1, 1)
98 currencies = {'test_currency': {'currency_type': 'currency'}}
99 return MockModel()
101@pytest.fixture
102def basic_model(mock_model, kwargs):
103 test_agent = BaseAgent(**{**kwargs, 'model': mock_model})
104 test_agent_2 = BaseAgent(mock_model, 'test_agent_2', capacity={'test_currency': 2})
105 mock_model.agents = {
106 'test_agent': test_agent,
107 'test_agent_2': test_agent_2
108 }
109 return mock_model
111class TestAgentRegister:
112 def test_agent_register_empty(self):
113 """Confirm that all attributes set correctly when no kwargs passed"""
114 test_agent = BaseAgent(object(), 'test_agent')
115 assert test_agent.registered == False
116 test_agent.register()
117 assert test_agent.registered == True
118 assert test_agent.attributes == {'age': 0}
119 assert test_agent.records['active'] == []
120 assert test_agent.records['cause_of_death'] == None
121 assert test_agent.records['storage'] == {}
122 assert test_agent.records['attributes'] == {'age': []}
123 assert test_agent.records['flows'] == {'in': {}, 'out': {}}
125 def test_agent_register_full_missing_connection(self, basic_model):
126 """Confirm that an error is raised if connection agent not in model"""
127 test_agent = basic_model.agents['test_agent']
128 basic_model.agents.pop('test_agent_2')
129 with pytest.raises(ValueError):
130 test_agent.register()
131 assert test_agent.registered == False
133 def test_agent_register_full_missing_currency(self, basic_model):
134 """Confirm that an error is raised if capacity missing for storage"""
135 test_agent = basic_model.agents['test_agent']
136 basic_model.agents['test_agent_2'].capacity.pop('test_currency')
137 with pytest.raises(ValueError):
138 test_agent.register()
139 assert test_agent.registered == False
141 def test_agent_register_full_missing_capacity(self, basic_model):
142 """Confirm that an error is raised if initial storage greater than capacity"""
143 test_agent = basic_model.agents['test_agent']
144 test_agent.capacity['test_currency'] = 0
145 with pytest.raises(ValueError):
146 test_agent.register()
147 assert test_agent.registered == False
149 def test_agent_register_full(self, basic_model):
150 """Confirm that all fields set correctly when kwargs passed"""
151 test_agent = basic_model.agents['test_agent']
152 test_agent.register()
153 assert test_agent.registered == True
154 assert test_agent.attributes == {'age': 0, 'test_attribute': 1}
155 assert test_agent.records['active'] == []
156 assert test_agent.records['storage'] == {'test_currency': []}
157 assert test_agent.records['attributes'] == {'age': [], 'test_attribute': []}
158 assert test_agent.records['flows'] == {
159 'in': {'test_currency': {'test_agent_2': []}},
160 'out': {'test_currency': {'test_agent_2': []}}
161 }
163 def test_agent_register_record_initial_state(self, basic_model):
164 """Confirm that initial state is recorded when requested"""
165 test_agent = basic_model.agents['test_agent']
166 test_agent.register(record_initial_state=True)
167 assert test_agent.records['active'] == [5]
168 assert test_agent.records['storage'] == {'test_currency': [1]}
169 assert test_agent.records['attributes'] == {'age': [0], 'test_attribute': [1]}
170 assert test_agent.records['flows'] == {
171 'in': {'test_currency': {'test_agent_2': [0]}},
172 'out': {'test_currency': {'test_agent_2': [0]}}
173 }
175@pytest.fixture
176def flow():
177 return {
178 'value': 1,
179 'criteria': [{
180 'buffer': 1,
181 }],
182 'deprive': {
183 'value': 2,
184 },
185 'growth': {
186 'daily': {
187 'type': 'clipped',
188 },
189 'lifetime': {
190 'type': 'sigmoid'
191 }
192 },
193 'connections': ['test_agent_2']
194 }
196class TestAgentRegisterFlow:
197 def test_agent_register_flow(self, basic_model, flow):
198 """Confirm that all flow attributes are set correctly"""
199 test_agent = basic_model.agents['test_agent']
200 test_agent.properties['lifetime'] = {'value': 100}
201 test_agent.flows['in']['test_currency'] = flow
202 test_agent.register(record_initial_state=True)
204 for attr in [
205 'in_test_currency_criteria_0_buffer',
206 'in_test_currency_deprive',
207 'in_test_currency_daily_growth_factor',
208 'in_test_currency_lifetime_growth_factor',
209 ]:
210 assert attr in test_agent.attributes
211 assert len(test_agent.records['attributes'][attr]) == 1
213@pytest.fixture
214def mock_model_with_currencies(mock_model):
215 mock_model.currencies = {
216 'test_currency_1': {
217 'currency_type': 'currency',
218 'class': 'test_currency_class'},
219 'test_currency_2': {
220 'currency_type': 'currency',
221 'class': 'test_currency_class'},
222 'test_currency_class': {
223 'currency_type': 'class',
224 'currencies': ['test_currency_1', 'test_currency_2']},
225 }
226 return mock_model
228class TestAgentView:
229 def test_agent_view_empty(self, mock_model_with_currencies):
230 """Confirm that view returns empty dict if no storage or capacity"""
231 test_agent = BaseAgent(mock_model_with_currencies, 'test_agent')
232 test_agent.register()
233 assert test_agent.view('test_currency_1') == {'test_currency_1': 0}
234 assert test_agent.view('test_currency_2') == {'test_currency_2': 0}
235 assert test_agent.view('test_currency_class') == {}
237 def test_agent_view_full(self, mock_model_with_currencies):
238 """Confirm view returns correct values for currency or currency class"""
239 test_agent = BaseAgent(
240 mock_model_with_currencies,
241 'test_agent',
242 storage={'test_currency_1': 1, 'test_currency_2': 2},
243 capacity={'test_currency_1': 1, 'test_currency_2': 2},
244 )
245 assert test_agent.view('test_currency_1') == {'test_currency_1': 1}
246 assert test_agent.view('test_currency_2') == {'test_currency_2': 2}
247 assert test_agent.view('test_currency_class') == {'test_currency_1': 1, 'test_currency_2': 2}
249 def test_agent_view_error(self, mock_model_with_currencies):
250 """Confirm that error is raised if view currency not in model"""
251 test_agent = BaseAgent(mock_model_with_currencies, 'test_agent')
252 with pytest.raises(KeyError):
253 test_agent.view('test_currency_3')
255class TestAgentSerialize:
256 def test_agent_serialize(self, basic_model, kwargs):
257 """Confirm that all fields are serialized correctly"""
258 test_agent = basic_model.agents['test_agent']
259 test_agent.register()
260 serialized = test_agent.serialize()
261 serializable = {'agent_id', 'amount', 'description', 'agent_class',
262 'properties', 'capacity', 'thresholds', 'flows',
263 'cause_of_death', 'active', 'storage', 'attributes'}
264 assert set(serialized.keys()) == serializable
265 for key in serializable:
266 if key == 'attributes':
267 assert serialized[key] == {'age': 0, 'test_attribute': 1}
268 else:
269 assert serialized[key] == kwargs[key]
271class TestAgentGetRecords:
272 def test_agent_get_records_basic(self, basic_model):
273 """Confirm that all fields are recorded correctly"""
274 test_agent = basic_model.agents['test_agent']
275 test_agent.register(record_initial_state=True)
276 records = test_agent.get_records()
278 assert records['active'] == [5]
279 assert records['cause_of_death'] == 'test_death'
280 assert records['storage'] == {'test_currency': [1]}
281 assert records['attributes'] == {'age': [0], 'test_attribute': [1]}
282 assert records['flows'] == {
283 'in': {'test_currency': {'test_agent_2': [0]}},
284 'out': {'test_currency': {'test_agent_2': [0]}}
285 }
287 def test_agent_get_records_static(self, basic_model, kwargs):
288 """Confirm that static fields are recorded correctly"""
289 test_agent = basic_model.agents['test_agent']
290 test_agent.register(record_initial_state=True)
291 records = test_agent.get_records(static=True)
292 static_keys = {'agent_id', 'amount', 'agent_class', 'description',
293 'properties', 'capacity', 'thresholds', 'flows'}
294 assert set(records['static'].keys()) == static_keys
295 for key in static_keys:
296 assert records['static'][key] == kwargs[key]
298 def test_agent_get_records_clear_cache(self, basic_model):
299 """Confirm that get_records clears cache when requested"""
300 test_agent = basic_model.agents['test_agent']
301 test_agent.register(record_initial_state=True)
302 test_agent.get_records(clear_cache=True)
303 def recursively_check_empty(dictionary):
304 for key in dictionary:
305 if isinstance(dictionary[key], dict):
306 recursively_check_empty(dictionary[key])
307 elif isinstance(dictionary[key], list):
308 assert dictionary[key] == []
309 recursively_check_empty(test_agent.records)
311class TestAgentSave:
312 def test_agent_save(self, basic_model, kwargs):
313 """Test that save returns a dictionary matching initialization"""
314 test_agent = basic_model.agents['test_agent']
315 test_agent.register(record_initial_state=True)
316 expected = copy.deepcopy(kwargs)
317 del expected['model']
318 expected['attributes'] = {'age': 0, 'test_attribute': 1}
320 saved = test_agent.save()
321 assert saved == expected
322 assert 'records' not in saved
324 def test_agent_save_with_records(self, basic_model, kwargs):
325 """Test that records are included in save if requested"""
326 test_agent = basic_model.agents['test_agent']
327 test_agent.register(record_initial_state=True)
328 expected = copy.deepcopy(kwargs)
329 del expected['model']
330 expected['attributes'] = {'age': 0, 'test_attribute': 1}
331 expected['records'] = test_agent.get_records()
333 saved = test_agent.save(records=True)
334 assert saved == expected
336class TestAgentIncrement:
337 def test_agent_increment_positive(self, mock_model_with_currencies):
338 """Test that increment correctly increments currencies"""
339 test_agent = BaseAgent(
340 mock_model_with_currencies,
341 'test_agent',
342 storage={'test_currency_1': 1},
343 capacity={'test_currency_1': 2},
344 )
345 # Test incrementing a single currency
346 receipt = test_agent.increment('test_currency_1', 1)
347 assert receipt == {'test_currency_1': 1}
348 assert test_agent.storage['test_currency_1'] == 2
349 # Test incrementing a single currency beyond capacity
350 receipt = test_agent.increment('test_currency_1', 1)
351 assert receipt == {'test_currency_1': 0}
352 assert test_agent.storage['test_currency_1'] == 2
353 # Test incrementing a currency without capacity
354 with pytest.raises(ValueError):
355 test_agent.increment('test_currency_2', 1)
356 # Test incrementing a currency class
357 with pytest.raises(ValueError):
358 test_agent.increment('test_currency_class', 1)
360 def test_agent_increment_negative(self, mock_model_with_currencies):
361 """Test that increment correctly decrements currencies"""
362 test_agent = BaseAgent(
363 mock_model_with_currencies,
364 'test_agent',
365 storage={'test_currency_1': 2, 'test_currency_2': 1},
366 capacity={'test_currency_1': 2, 'test_currency_2': 2},
367 )
368 # Test decrementing a single currency
369 receipt = test_agent.increment('test_currency_1', -1)
370 assert receipt == {'test_currency_1': -1}
371 assert test_agent.storage['test_currency_1'] == 1
372 # Test decrementing a currency class
373 receipt = test_agent.increment('test_currency_class', -1)
374 assert receipt == {'test_currency_1': -0.5, 'test_currency_2': -0.5}
375 assert test_agent.storage['test_currency_1'] == 0.5
376 assert test_agent.storage['test_currency_2'] == 0.5
377 # Test decrementing a currency class beyond stored
378 receipt = test_agent.increment('test_currency_class', -2)
379 assert receipt == {'test_currency_1': -0.5, 'test_currency_2': -0.5}
380 assert test_agent.storage['test_currency_1'] == 0
381 assert test_agent.storage['test_currency_2'] == 0
382 # TODO: Test with amount>1, confirm capacity scales with amount
384@pytest.fixture
385def get_flow_value_kwargs(kwargs):
386 return {
387 'dT': 1,
388 'direction': 'in',
389 'currency': 'test_currency',
390 'flow': kwargs['flows']['in']['test_currency'],
391 'influx': {},
392 }
394class TestAgentGetFlowValue:
395 def test_agent_get_flow_value_basic(self, basic_model, get_flow_value_kwargs):
396 """Test that get_flow_value returns the correct value"""
397 test_agent = basic_model.agents['test_agent']
398 flow_value = test_agent.get_flow_value(**get_flow_value_kwargs)
399 assert flow_value == 1
400 get_flow_value_kwargs['dT'] = 0.33
401 flow_value = test_agent.get_flow_value(**get_flow_value_kwargs)
402 assert flow_value == 0.33
404 def test_agent_get_flow_value_requires(self, basic_model, get_flow_value_kwargs):
405 """Test that get_flow_value handles requires correctly"""
406 # Single Currency
407 test_agent = basic_model.agents['test_agent']
408 get_flow_value_kwargs['flow']['requires'] = ['test_currency_2']
409 flow_value = test_agent.get_flow_value(**get_flow_value_kwargs)
410 assert flow_value == 0
411 get_flow_value_kwargs['influx'] = {'test_currency_2': 0.5}
412 flow_value = test_agent.get_flow_value(**get_flow_value_kwargs)
413 assert flow_value == 0.5
414 get_flow_value_kwargs['influx'] = {'test_currency_2': 1}
415 flow_value = test_agent.get_flow_value(**get_flow_value_kwargs)
416 assert flow_value == 1
418 # Multiple Currencies
419 get_flow_value_kwargs['flow']['requires'] = ['test_currency_2', 'test_currency_3']
420 get_flow_value_kwargs['influx'] = {'test_currency_2': 0.5, 'test_currency_3': 0.5}
421 flow_value = test_agent.get_flow_value(**get_flow_value_kwargs)
422 assert flow_value == 0.25
424 def test_agent_get_flow_value_criteria_basic(self, basic_model, get_flow_value_kwargs):
425 # TODO: Move some of this to the test for evaluate_criteria.
426 test_agent = basic_model.agents['test_agent']
427 # Equality | Attribute
428 test_agent.flows['in']['test_currency']['criteria'] = [{
429 'path': 'test_attribute',
430 'limit': '=',
431 'value': 1,
432 }]
433 get_flow_value_kwargs['flow'] = test_agent.flows['in']['test_currency']
434 flow_value = test_agent.get_flow_value(**get_flow_value_kwargs)
435 assert flow_value == 1
436 test_agent.flows['in']['test_currency']['criteria'][0]['value'] = 2
437 flow_value = test_agent.get_flow_value(**get_flow_value_kwargs)
438 assert flow_value == 0
440 def test_agent_get_flow_value_growth(self, basic_model, get_flow_value_kwargs):
441 test_agent = basic_model.agents['test_agent']
442 get_flow_value_kwargs['flow']['growth'] = {'lifetime': {'type': 'sigmoid'}}
443 test_agent.properties['lifetime'] = {'value': 10}
444 test_agent.attributes['age'] = 0
445 flow_value = test_agent.get_flow_value(**get_flow_value_kwargs)
446 assert 0 < flow_value < 0.0001
447 test_agent.attributes['age'] = 5
448 flow_value = test_agent.get_flow_value(**get_flow_value_kwargs)
449 assert flow_value == 0.5
450 test_agent.attributes['age'] = 10
451 flow_value = test_agent.get_flow_value(**get_flow_value_kwargs)
452 assert 0.9999 < flow_value < 1
454 def test_agent_get_flow_value_weighted(self, basic_model, get_flow_value_kwargs):
455 test_agent = basic_model.agents['test_agent']
456 test_agent.properties['test_property']['value'] = 0.5
457 test_agent.attributes['test_attribute'] = 0.5
458 test_agent.storage['test_currency'] = 10 # divided by active=5
459 get_flow_value_kwargs['flow']['weighted'] = [
460 'test_property', 'test_attribute', 'test_currency']
461 flow_value = test_agent.get_flow_value(**get_flow_value_kwargs)
462 assert flow_value == 1 * 0.5 * 0.5 * 10 / 5
463 get_flow_value_kwargs['flow']['weighted'] = ['missing_weight']
464 with pytest.raises(ValueError):
465 test_agent.get_flow_value(**get_flow_value_kwargs)
467class TestAgentProcessFlow:
468 def test_process_flow_influx(self, mock_model_with_currencies):
469 test_agent = BaseAgent(
470 mock_model_with_currencies,
471 'test_agent',
472 flows={
473 'in': {
474 'test_currency_1': {
475 'value': 1,
476 'connections': ['test_agent_2'],
477 },
478 },
479 'out': {
480 'test_currency_2': {
481 'value': 1,
482 'connections': ['test_agent_2'],
483 },
484 }
485 }
486 )
487 test_agent_2 = BaseAgent(
488 mock_model_with_currencies,
489 'test_agent_2',
490 capacity={'test_currency_1': 2, 'test_currency_2': 2},
491 )
492 mock_model_with_currencies.agents = {
493 'test_agent': test_agent,
494 'test_agent_2': test_agent_2,
495 }
496 test_agent.register()
497 test_agent_2.register()
498 influx = {}
499 for direction in ('in', 'out'):
500 currency = next(iter(test_agent.flows[direction]))
501 kwargs = dict(dT=1, direction=direction, currency=currency,
502 flow=test_agent.flows[direction][currency], influx=influx,
503 target=1, actual=1)
504 test_agent.process_flow(**kwargs)
505 assert influx == {'test_currency_1': 1}
507 def test_process_flow_deprive(self, mock_model_with_currencies):
508 test_agent = BaseAgent(
509 mock_model_with_currencies,
510 'test_agent',
511 amount=5,
512 flows={
513 'in': {
514 'test_currency_1': {
515 'value': 1,
516 'connections': ['test_agent_2'],
517 'deprive': {'value': 2}
518 },
519 },
520 }
521 )
522 test_agent_2 = BaseAgent(
523 mock_model_with_currencies,
524 'test_agent_2',
525 capacity={'test_currency_1': 2},
526 )
527 mock_model_with_currencies.agents = {
528 'test_agent': test_agent,
529 'test_agent_2': test_agent_2,
530 }
531 test_agent.register()
532 test_agent_2.register()
533 # Start with deprive buffer full
534 assert test_agent.attributes['in_test_currency_1_deprive'] == 2
535 # Actual is equal to half of target; half of the active are deprived
536 process_kwargs = dict(dT=1, direction='in', currency='test_currency_1',
537 flow=test_agent.flows['in']['test_currency_1'], influx={},
538 target=10, actual=5)
539 test_agent.process_flow(**process_kwargs)
540 assert test_agent.attributes['in_test_currency_1_deprive'] == 1.5
541 test_agent.process_flow(**process_kwargs)
542 assert test_agent.attributes['in_test_currency_1_deprive'] == 1
543 test_agent.process_flow(**process_kwargs)
544 assert test_agent.attributes['in_test_currency_1_deprive'] == 0.5
545 test_agent.process_flow(**process_kwargs)
546 assert test_agent.attributes['in_test_currency_1_deprive'] == 0
547 # Deprive buffer is empty, so half of the active die
548 test_agent.process_flow(**process_kwargs)
549 assert test_agent.active == 2
550 # Actual is equal to target again, buffer resets
551 process_kwargs['actual'] = 10
552 test_agent.process_flow(**process_kwargs)
553 assert test_agent.attributes['in_test_currency_1_deprive'] == 2
554 # Actual equal to zero; all of active are deprived
555 process_kwargs['actual'] = 0
556 test_agent.process_flow(**process_kwargs)
557 assert test_agent.active == 2
558 assert test_agent.attributes['in_test_currency_1_deprive'] == 1
559 # Buffer responds to DT
560 process_kwargs['dT'] = 0.5
561 test_agent.process_flow(**process_kwargs)
562 assert test_agent.active == 2
563 assert test_agent.attributes['in_test_currency_1_deprive'] == 0.5
564 test_agent.process_flow(**process_kwargs)
565 assert test_agent.active == 2
566 assert test_agent.attributes['in_test_currency_1_deprive'] == 0
567 # When deprive buffer is empty, all active die (per dT)
568 process_kwargs['dT'] = 1
569 test_agent.process_flow(**process_kwargs)
570 assert test_agent.active == 0
571 assert test_agent.cause_of_death == 'test_agent deprived of test_currency_1'
573class TestAgentStep:
574 def test_agent_step_empty(self, basic_model):
575 test_agent = BaseAgent(basic_model, 'test_agent')
576 assert not test_agent.registered
577 test_agent.step()
578 assert test_agent.registered
579 assert test_agent.records == {
580 'active': [1],
581 'cause_of_death': None,
582 'storage': {},
583 'attributes': {'age': [1]},
584 'flows': {'in': {}, 'out': {}}
585 }
586 test_agent.step()
587 assert test_agent.attributes['age'] == 2
588 assert test_agent.records['attributes']['age'] == [1, 2]
589 assert test_agent.records['active'] == [1, 1]
591 def test_agent_step_threshold(self, mock_model_with_currencies):
592 test_agent = BaseAgent(
593 mock_model_with_currencies,
594 'test_agent',
595 thresholds={'test_currency_1': {
596 'path': 'in_test_currency_1_ratio',
597 'limit': '<',
598 'value': 0.5,
599 }},
600 flows={'in': {'test_currency_1': {'connections': ['test_structure']}}},
601 )
602 test_structure = BaseAgent(
603 mock_model_with_currencies,
604 'test_structure',
605 capacity={'test_currency_1': 10, 'test_currency_2': 10},
606 storage={'test_currency_1': 5, 'test_currency_2': 5},
607 )
608 mock_model_with_currencies.agents = {
609 'test_agent': test_agent,
610 'test_structure': test_structure,
611 }
612 test_agent.register()
613 test_structure.register()
614 # Threshold not met
615 test_agent.step()
616 assert test_agent.active
617 assert test_agent.cause_of_death == None
618 assert test_agent.records['flows']['in']['test_currency_1']['test_structure'] == [0]
619 assert test_agent.attributes['age'] == 1
620 # Threshold met
621 test_structure.storage['test_currency_1'] = 4.9
622 test_agent.step()
623 assert test_agent.active == 0
624 assert test_agent.cause_of_death == 'test_agent passed test_currency_1 threshold'
625 assert test_agent.records['flows']['in']['test_currency_1']['test_structure'] == [0, 0]
626 assert test_agent.attributes['age'] == 2
627 # Records continue to accumulate even after agent is dead, but age stays same
628 test_agent.step()
629 assert test_agent.records['flows']['in']['test_currency_1']['test_structure'] == [0, 0, 0]
630 assert test_agent.attributes['age'] == 2
632 def test_agent_step_flows(self, mock_model_with_currencies):
633 test_agent = BaseAgent(
634 mock_model_with_currencies,
635 'test_agent',
636 flows={
637 'in': {
638 'test_currency_1': {
639 'value': 1,
640 'connections': ['test_structure'],
641 }
642 },
643 'out': {
644 'test_currency_2': {
645 'value': 1,
646 'connections': ['test_structure'],
647 }
648 }
649 }
650 )
651 test_structure = BaseAgent(
652 mock_model_with_currencies,
653 'test_structure',
654 capacity={'test_currency_1': 10, 'test_currency_2': 10},
655 storage={'test_currency_1': 5, 'test_currency_2': 5},
656 )
657 mock_model_with_currencies.agents = {
658 'test_agent': test_agent,
659 'test_structure': test_structure,
660 }
661 test_agent.register()
662 test_structure.register()
663 test_agent.step()
664 assert test_agent.records['flows']['in']['test_currency_1']['test_structure'] == [1]
665 assert test_agent.records['flows']['out']['test_currency_2']['test_structure'] == [1]
666 assert test_structure.storage['test_currency_1'] == 4
667 assert test_structure.storage['test_currency_2'] == 6
668 test_agent.step(dT=0.5)
669 assert test_agent.records['flows']['in']['test_currency_1']['test_structure'] == [1, 0.5]
670 assert test_agent.records['flows']['out']['test_currency_2']['test_structure'] == [1, 0.5]
671 assert test_structure.storage['test_currency_1'] == 3.5
672 assert test_structure.storage['test_currency_2'] == 6.5
674 def test_agent_step_multi_connections(self, mock_model_with_currencies):
675 test_agent = BaseAgent(
676 mock_model_with_currencies,
677 'test_agent',
678 flows={
679 'in': {
680 'test_currency_1': {
681 'value': 2,
682 'connections': ['test_structure_1', 'test_structure_2'],
683 }
684 },
685 }
686 )
687 test_structure_1 = BaseAgent(
688 mock_model_with_currencies,
689 'test_structure_1',
690 capacity={'test_currency_1': 10},
691 storage={'test_currency_1': 5},
692 )
693 test_structure_2 = BaseAgent(
694 mock_model_with_currencies,
695 'test_structure_2',
696 capacity={'test_currency_1': 10},
697 storage={'test_currency_1': 5},
698 )
699 mock_model_with_currencies.agents = {
700 'test_agent': test_agent,
701 'test_structure_1': test_structure_1,
702 'test_structure_2': test_structure_2,
703 }
704 test_agent.register()
705 test_structure_1.register()
706 test_structure_2.register()
708 # If available, use first connection
709 test_agent.step()
710 assert test_agent.records['flows']['in']['test_currency_1'] == {
711 'test_structure_1': [2],
712 'test_structure_2': [0],
713 }
714 assert test_structure_1.storage['test_currency_1'] == 3
715 assert test_structure_2.storage['test_currency_1'] == 5
717 # If partially available, split between first and second connections
718 test_structure_1.storage['test_currency_1'] = 1
719 test_agent.step()
720 test_agent.step()
721 assert test_agent.records['flows']['in']['test_currency_1'] == {
722 'test_structure_1': [2, 1, 0],
723 'test_structure_2': [0, 1, 2],
724 }
725 assert test_structure_1.storage['test_currency_1'] == 0
726 assert test_structure_2.storage['test_currency_1'] == 2
728class TestAgentKill:
729 def test_agent_kill(self, basic_model):
730 test_agent = BaseAgent(basic_model, 'test_agent', amount=2)
731 basic_model.agents = {'test_agent': test_agent}
732 test_agent.register()
733 test_agent.kill('test_reason', 1)
734 assert test_agent.active == 1
735 assert test_agent.cause_of_death == None
736 test_agent.kill('test_reason', 1)
737 assert test_agent.active == 0
738 assert test_agent.cause_of_death == 'test_reason'