1010RELAY_ROOT = Path (__file__ ).parent .parent .parent
1111
1212
13+ TEST_CONFIG = {
14+ "outcomes" : {
15+ "emit_outcomes" : True ,
16+ "batch_size" : 1 ,
17+ "batch_interval" : 1 ,
18+ "aggregator" : {
19+ "bucket_interval" : 1 ,
20+ "flush_interval" : 1 ,
21+ },
22+ },
23+ "aggregator" : {
24+ "bucket_interval" : 1 ,
25+ "initial_delay" : 0 ,
26+ },
27+ }
28+
29+
1330@pytest .mark .parametrize ("num_intermediate_relays" , [0 , 1 , 2 ])
1431def test_profile_chunk_outcomes (
1532 mini_sentry ,
@@ -35,29 +52,13 @@ def test_profile_chunk_outcomes(
3552 project_config .setdefault ("features" , []).append (
3653 "organizations:continuous-profiling"
3754 )
38- config = {
39- "outcomes" : {
40- "emit_outcomes" : True ,
41- "batch_size" : 1 ,
42- "batch_interval" : 1 ,
43- "aggregator" : {
44- "bucket_interval" : 1 ,
45- "flush_interval" : 1 ,
46- },
47- "source" : "processing-relay" ,
48- },
49- "aggregator" : {
50- "bucket_interval" : 1 ,
51- "initial_delay" : 0 ,
52- },
53- }
5455
5556 # The innermost Relay needs to be in processing mode
56- upstream = relay_with_processing (config )
57+ upstream = relay_with_processing (TEST_CONFIG )
5758
5859 # build a chain of relays
5960 for i in range (num_intermediate_relays ):
60- config = deepcopy (config )
61+ config = deepcopy (TEST_CONFIG )
6162 if i == 0 :
6263 # Emulate a PoP Relay
6364 config ["outcomes" ]["source" ] = "pop-relay"
@@ -103,24 +104,7 @@ def test_profile_chunk_outcomes_invalid(
103104 "organizations:continuous-profiling"
104105 )
105106
106- config = {
107- "outcomes" : {
108- "emit_outcomes" : True ,
109- "batch_size" : 1 ,
110- "batch_interval" : 1 ,
111- "aggregator" : {
112- "bucket_interval" : 1 ,
113- "flush_interval" : 1 ,
114- },
115- "source" : "pop-relay" ,
116- },
117- "aggregator" : {
118- "bucket_interval" : 1 ,
119- "initial_delay" : 0 ,
120- },
121- }
122-
123- upstream = relay_with_processing (config )
107+ upstream = relay_with_processing (TEST_CONFIG )
124108
125109 envelope = Envelope ()
126110 payload = {
@@ -144,18 +128,19 @@ def test_profile_chunk_outcomes_invalid(
144128 "project_id" : 42 ,
145129 "quantity" : 1 ,
146130 "reason" : "profiling_platform_not_supported" ,
147- "source" : "pop-relay" ,
148131 },
149132 ]
150133
151134 profiles_consumer .assert_empty ()
152135
153136
137+ @pytest .mark .parametrize ("item_header_platform" , [None , "cocoa" ])
154138def test_profile_chunk_outcomes_rate_limited (
155139 mini_sentry ,
156140 relay_with_processing ,
157141 outcomes_consumer ,
158142 profiles_consumer ,
143+ item_header_platform ,
159144):
160145 """
161146 Tests that Relay reports correct outcomes when profile chunks are rate limited.
@@ -186,23 +171,7 @@ def test_profile_chunk_outcomes_rate_limited(
186171 }
187172 ]
188173
189- config = {
190- "outcomes" : {
191- "emit_outcomes" : True ,
192- "batch_size" : 1 ,
193- "batch_interval" : 1 ,
194- "aggregator" : {
195- "bucket_interval" : 1 ,
196- "flush_interval" : 0 ,
197- },
198- },
199- "aggregator" : {
200- "bucket_interval" : 1 ,
201- "initial_delay" : 0 ,
202- },
203- }
204-
205- upstream = relay_with_processing (config )
174+ upstream = relay_with_processing (TEST_CONFIG )
206175
207176 # Load a valid profile chunk from test fixtures
208177 with open (
@@ -213,7 +182,13 @@ def test_profile_chunk_outcomes_rate_limited(
213182
214183 # Create and send envelope containing the profile chunk
215184 envelope = Envelope ()
216- envelope .add_item (Item (payload = PayloadRef (bytes = profile ), type = "profile_chunk" ))
185+ envelope .add_item (
186+ Item (
187+ payload = PayloadRef (bytes = profile ),
188+ type = "profile_chunk" ,
189+ headers = {"platform" : item_header_platform },
190+ )
191+ )
217192 upstream .send_envelope (project_id , envelope )
218193
219194 # Verify the rate limited outcome was emitted with correct properties
@@ -235,3 +210,70 @@ def test_profile_chunk_outcomes_rate_limited(
235210
236211 # Verify no profiles were forwarded to the consumer
237212 profiles_consumer .assert_empty ()
213+
214+
215+ @pytest .mark .parametrize (
216+ "platform, category" ,
217+ [
218+ ("cocoa" , "profile_chunk_ui" ),
219+ ("node" , "profile_chunk" ),
220+ (None , "profile_chunk" ), # Special case, currently this will forward
221+ ],
222+ )
223+ def test_profile_chunk_outcomes_rate_limited_fast (
224+ mini_sentry ,
225+ relay ,
226+ platform ,
227+ category ,
228+ ):
229+ """
230+ Tests that Relay reports correct outcomes when profile chunks are rate limited already in the
231+ fast-path, using the item header.
232+
233+ The test is parameterized to also *not* send the necessary item header, in which case this currently
234+ asserts the chunk is let through. Once Relay's behaviour is changed to reject or profile chunks
235+ without the necessary headers or the profile type is defaulted this test needs to be adjusted accordingly.
236+ """
237+ project_id = 42
238+ project_config = mini_sentry .add_full_project_config (project_id )["config" ]
239+
240+ project_config .setdefault ("features" , []).append (
241+ "organizations:continuous-profiling"
242+ )
243+
244+ project_config ["quotas" ] = [
245+ {
246+ "id" : f"test_rate_limiting_{ uuid .uuid4 ().hex } " ,
247+ "categories" : [category ],
248+ "limit" : 0 ,
249+ "reasonCode" : "profile_chunks_exceeded" ,
250+ }
251+ ]
252+
253+ upstream = relay (mini_sentry )
254+
255+ with open (
256+ RELAY_ROOT / "relay-profiling/tests/fixtures/sample/v2/valid.json" ,
257+ "rb" ,
258+ ) as f :
259+ profile = f .read ()
260+
261+ envelope = Envelope ()
262+ envelope .add_item (
263+ Item (
264+ payload = PayloadRef (bytes = profile ),
265+ type = "profile_chunk" ,
266+ headers = {"platform" : platform },
267+ )
268+ )
269+ upstream .send_envelope (project_id , envelope )
270+
271+ if platform is None :
272+ envelope = mini_sentry .captured_events .get (timeout = 1 )
273+ assert [item .type for item in envelope .items ] == ["profile_chunk" ]
274+ else :
275+ outcome = mini_sentry .get_client_report ()
276+ assert outcome ["rate_limited_events" ] == [
277+ {"category" : category , "quantity" : 1 , "reason" : "profile_chunks_exceeded" }
278+ ]
279+ assert mini_sentry .captured_events .empty ()
0 commit comments