11from unittest .mock import MagicMock
22
33import requests
4- from pygitguardian .models import Detail
4+ from pygitguardian .models import Detail , UserInfo
55
66from ggshield .verticals .ai .agent_activity .models import AgentActivityEvent
77from ggshield .verticals .ai .agent_activity .orchestrator import (
1212)
1313
1414
15+ _USER = UserInfo (hostname = "dev-laptop" , username = "dev" , machine_id = "machine-001" )
16+
17+
1518def test_send_agent_activity_batch_serialises_and_calls_client () -> None :
1619 client = MagicMock ()
1720 client .send_agent_activity .return_value = MagicMock (
@@ -33,7 +36,7 @@ def test_send_agent_activity_batch_serialises_and_calls_client() -> None:
3336 content = '{"key": "bubbleId:abc:xyz", "value": "{"y": 2}"}' ,
3437 ),
3538 ]
36- result = send_agent_activity_batch (client , events )
39+ result = send_agent_activity_batch (client , events , _USER )
3740 client .send_agent_activity .assert_called_once_with (
3841 [
3942 {
@@ -51,13 +54,14 @@ def test_send_agent_activity_batch_serialises_and_calls_client() -> None:
5154 "content" : '{"key": "bubbleId:abc:xyz", "value": "{"y": 2}"}' ,
5255 },
5356 ],
57+ user = _USER .to_dict (),
5458 )
5559 assert result .ingested == 2
5660
5761
5862def test_send_agent_activity_batch_empty_list_short_circuits () -> None :
5963 client = MagicMock ()
60- result = send_agent_activity_batch (client , [])
64+ result = send_agent_activity_batch (client , [], _USER )
6165 client .send_agent_activity .assert_not_called ()
6266 assert result .ingested == 0
6367
@@ -66,7 +70,7 @@ def test_send_agent_activity_batch_treats_detail_as_failure() -> None:
6670 """An API error (Detail) is reported as a failed batch, not an ingest."""
6771 client = MagicMock ()
6872 client .send_agent_activity .return_value = Detail ("boom" , status_code = 500 )
69- result = send_agent_activity_batch (client , [_event ("a" , 1 )])
73+ result = send_agent_activity_batch (client , [_event ("a" , 1 )], _USER )
7074 assert result .success is False
7175 assert result .ingested == 0
7276 assert result .duplicates == 0
@@ -75,7 +79,7 @@ def test_send_agent_activity_batch_treats_detail_as_failure() -> None:
7579def test_collect_agent_activity_counts_detail_response_as_failed_batch () -> None :
7680 client = MagicMock ()
7781 client .send_agent_activity .return_value = Detail ("boom" , status_code = 500 )
78- report = collect_agent_activity (client , [_make_agent ("a" , [_event ("a" , 1 )])])
82+ report = collect_agent_activity (client , _USER , [_make_agent ("a" , [_event ("a" , 1 )])])
7983 assert report .parsed == 1
8084 assert report .ingested == 0
8185 assert report .failed_batches == 1
@@ -98,6 +102,18 @@ def _event(agent_name: str, i: int) -> AgentActivityEvent:
98102 )
99103
100104
105+ def test_collect_agent_activity_attaches_user_to_each_batch () -> None :
106+ client = MagicMock ()
107+ client .send_agent_activity .return_value = MagicMock (
108+ success = True , ingested = 1 , duplicates = 0
109+ )
110+ collect_agent_activity (client , _USER , [_make_agent ("a" , [_event ("a" , 1 )])])
111+
112+ _ , kwargs = client .send_agent_activity .call_args
113+ assert kwargs ["user" ] == _USER .to_dict ()
114+ assert kwargs ["user" ]["machine_id" ] == "machine-001"
115+
116+
101117def test_collect_agent_activity_batches_in_chunks_of_batch_size () -> None :
102118 """An agent yielding more than BATCH_SIZE events triggers multiple sends."""
103119 events = [_event ("a" , i ) for i in range (BATCH_SIZE + 3 )]
@@ -107,7 +123,7 @@ def test_collect_agent_activity_batches_in_chunks_of_batch_size() -> None:
107123 success = True , ingested = BATCH_SIZE , duplicates = 0
108124 )
109125
110- report = collect_agent_activity (client , [agent ])
126+ report = collect_agent_activity (client , _USER , [agent ])
111127
112128 assert client .send_agent_activity .call_count == 2
113129 assert report .parsed == BATCH_SIZE + 3
@@ -122,7 +138,7 @@ def test_collect_agent_activity_aggregates_per_agent_counts() -> None:
122138 )
123139
124140 report = collect_agent_activity (
125- client , [_make_agent ("a" , events_a ), _make_agent ("b" , events_b )]
141+ client , _USER , [_make_agent ("a" , events_a ), _make_agent ("b" , events_b )]
126142 )
127143
128144 assert report .parsed == 2
@@ -131,7 +147,7 @@ def test_collect_agent_activity_aggregates_per_agent_counts() -> None:
131147
132148def test_collect_agent_activity_handles_empty_agents () -> None :
133149 client = MagicMock ()
134- report = collect_agent_activity (client , [_make_agent ("a" , [])])
150+ report = collect_agent_activity (client , _USER , [_make_agent ("a" , [])])
135151 client .send_agent_activity .assert_not_called ()
136152 assert report .parsed == 0
137153
@@ -152,7 +168,7 @@ def test_collect_agent_activity_flushes_on_byte_threshold() -> None:
152168 client = MagicMock ()
153169 client .send_agent_activity .return_value = MagicMock (ingested = 1 , duplicates = 0 )
154170
155- report = collect_agent_activity (client , [_make_agent ("a" , events )])
171+ report = collect_agent_activity (client , _USER , [_make_agent ("a" , events )])
156172
157173 # 3 oversized events => 3 separate sends, none waiting for the 500 count.
158174 assert client .send_agent_activity .call_count == 3
@@ -163,7 +179,7 @@ def test_collect_agent_activity_counts_network_error_as_failed_batch() -> None:
163179 """A network error while sending a batch is counted as a failed batch."""
164180 client = MagicMock ()
165181 client .send_agent_activity .side_effect = requests .exceptions .ConnectionError ("down" )
166- report = collect_agent_activity (client , [_make_agent ("a" , [_event ("a" , 1 )])])
182+ report = collect_agent_activity (client , _USER , [_make_agent ("a" , [_event ("a" , 1 )])])
167183
168184 assert report .parsed == 1
169185 assert report .ingested == 0
0 commit comments