diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ad299767af..1d75f0e2de7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ - Enable OTLP endpoints by default. ([#5951](https://github.com/getsentry/relay/pull/5951)) +**Bug Fixes**: + +- Emit more precise outcome discard reasons for the Playstation, Minidump, and Attachments endpoints. ([#5950](https://github.com/getsentry/relay/pull/5950)) + **Internal**: - Bump `sentry-conventions` to 0.6.0-4. ([#5944](https://github.com/getsentry/relay/pull/5944)) diff --git a/relay-server/src/endpoints/attachments.rs b/relay-server/src/endpoints/attachments.rs index 3c160f892d5..25ef10e1e66 100644 --- a/relay-server/src/endpoints/attachments.rs +++ b/relay-server/src/endpoints/attachments.rs @@ -5,6 +5,7 @@ use axum::routing::{MethodRouter, post}; use multer::{Field, Multipart}; use relay_config::Config; use relay_event_schema::protocol::EventId; +use relay_quotas::DataCategory; use serde::Deserialize; use tower_http::limit::RequestBodyLimitLayer; @@ -52,8 +53,12 @@ async fn multipart_to_envelope( ) .await?; - let envelope = - common::managed_items_to_envelope(items, meta, state.outcome_aggregator(), path.event_id); + let envelope = items.map(|items, records| { + if items.iter().any(|i| i.creates_event()) { + records.modify_by(DataCategory::Error, 1); + } + Box::new(Envelope::from_request(Some(path.event_id), meta).with_items(items)) + }); Ok(envelope) } diff --git a/relay-server/src/endpoints/common.rs b/relay-server/src/endpoints/common.rs index d608a9da2a1..9b93b9a1e49 100644 --- a/relay-server/src/endpoints/common.rs +++ b/relay-server/src/endpoints/common.rs @@ -17,13 +17,12 @@ use serde::Deserialize; use crate::envelope::{ AttachmentPlaceholder, AttachmentType, ContentType, Envelope, EnvelopeError, Item, ItemType, - ManagedItems, + Items, }; -use crate::extractors::RequestMeta; use crate::managed::{Managed, Rejected}; use crate::service::ServiceState; use crate::services::buffer::{ProjectKeyPair, PushError}; -use crate::services::outcome::{DiscardItemType, DiscardReason, Outcome, TrackOutcome}; +use crate::services::outcome::{DiscardItemType, DiscardReason, Outcome}; use crate::services::processor::{BucketSource, MetricData, ProcessMetrics}; use crate::services::upload::{Create, Stream, Upload}; use crate::statsd::{RelayCounters, RelayDistributions}; @@ -125,6 +124,29 @@ pub enum BadStoreRequest { ObjectstoreUploadFailed, } +impl BadStoreRequest { + pub fn to_outcome(&self) -> Option { + let discard_reason = match self { + Self::InvalidCompressionContainer(_) => DiscardReason::InvalidCompression, + Self::InvalidMinidump => DiscardReason::InvalidMinidump, + #[cfg(sentry)] + Self::InvalidProsperodump => DiscardReason::InvalidProsperodump, + Self::MissingMinidump => DiscardReason::MissingMinidumpUpload, + #[cfg(sentry)] + Self::MissingProsperodump => DiscardReason::MissingProsperodumpUpload, + Self::Overflow(item_type) => DiscardReason::TooLarge(*item_type), + _ => DiscardReason::Internal, + }; + Some(Outcome::Invalid(discard_reason)) + } +} + +impl From> for BadStoreRequest { + fn from(rejected: Rejected) -> Self { + rejected.into_inner() + } +} + impl From for BadStoreRequest { fn from(value: BytesRejection) -> Self { match value { @@ -292,7 +314,7 @@ pub fn event_id_from_formdata(data: &[u8]) -> Result, BadStoreRe /// /// Extracting the event id from chunked formdata fields on the Minidump endpoint (`sentry__1`, /// `sentry__2`, ...) is not supported. In this case, `None` is returned. -pub fn event_id_from_items(items: &ManagedItems) -> Result, BadStoreRequest> { +pub fn event_id_from_items(items: &Items) -> Result, BadStoreRequest> { if let Some(item) = items.iter().find(|item| item.ty() == &ItemType::Event) && let Some(event_id) = event_id_from_json(&item.payload())? { @@ -575,27 +597,6 @@ where Some(item) } -pub fn managed_items_to_envelope( - items: ManagedItems, - meta: RequestMeta, - outcome_aggregator: &Addr, - event_id: EventId, -) -> Managed> { - let envelope = Envelope::from_request(Some(event_id), meta); - let mut envelope = Managed::from_envelope(envelope, outcome_aggregator.clone()); - let mut has_event = false; - for item in items { - envelope.merge_with(item, |envelope, item, records| { - if !has_event && item.creates_event() { - records.modify_by(DataCategory::Error, 1); - has_event = true; - } - envelope.add_item(item); - }); - } - envelope -} - #[derive(Debug)] pub struct TextResponse(pub Option); diff --git a/relay-server/src/endpoints/minidump.rs b/relay-server/src/endpoints/minidump.rs index b4d0efa6026..50771a419f5 100644 --- a/relay-server/src/endpoints/minidump.rs +++ b/relay-server/src/endpoints/minidump.rs @@ -24,7 +24,7 @@ use crate::endpoints::common::{self, BadStoreRequest, TextResponse, upload_to_ob use crate::envelope::ContentType::Minidump; use crate::envelope::{AttachmentType, Envelope, Item, ItemType}; use crate::extractors::{RawContentType, RequestMeta}; -use crate::managed::Managed; +use crate::managed::{Managed, ManagedResult}; use crate::middlewares; use crate::service::ServiceState; use crate::services::outcome::{DiscardAttachmentType, DiscardItemType, DiscardReason, Outcome}; @@ -123,9 +123,8 @@ fn decode_minidump(minidump_data: Bytes, max_size: usize) -> Result { if decoded.len() > max_size { - return Err(BadStoreRequest::Overflow(DiscardItemType::Attachment( - DiscardAttachmentType::Minidump, - ))); + let item_type = DiscardItemType::Attachment(DiscardAttachmentType::Minidump); + return Err(BadStoreRequest::Overflow(item_type)); } Ok(Bytes::from(decoded)) } @@ -284,36 +283,37 @@ async fn multipart_to_envelope( ) .await?; - let minidump_item = items - .iter_mut() - .find(|item| item.attachment_type() == Some(AttachmentType::Minidump)) - .ok_or(BadStoreRequest::MissingMinidump)?; + let minidump_idx = items + .iter() + .position(|item| item.attachment_type() == Some(AttachmentType::Minidump)) + .ok_or(BadStoreRequest::MissingMinidump) + .reject(&items)?; // Doing these operations does not make sense if we already streamed the minidump to objectstore. - if !minidump_item.is_attachment_ref() { - let payload = minidump_item.payload(); + if !items[minidump_idx].is_attachment_ref() { + let payload = items[minidump_idx].payload(); let payload = extract_embedded_minidump(payload.clone()) .await? .unwrap_or(payload); - let payload = decode_minidump(payload, config.max_attachment_size())?; + let payload = decode_minidump(payload, config.max_attachment_size()).reject(&items)?; - minidump_item.modify(|inner, records| { - inner.set_payload(Minidump, payload); - records.lenient(DataCategory::Attachment); - }); - - validate_minidump(&minidump_item.payload())?; - - minidump_item.modify(|inner, _| { - if let Some(minidump_filename) = inner.filename() { - inner.set_filename(remove_container_extension(minidump_filename).to_owned()) + items.try_modify(|items, records| -> Result<(), BadStoreRequest> { + let minidump_item = &mut items[minidump_idx]; + minidump_item.set_payload(Minidump, payload); + records.lenient(DataCategory::Attachment); // decoding the minidump changes its size + if let Some(minidump_filename) = minidump_item.filename() { + minidump_item.set_filename(remove_container_extension(minidump_filename).to_owned()) } - }); + validate_minidump(&minidump_item.payload())?; + Ok(()) + })?; } let event_id = common::event_id_from_items(&items)?.unwrap_or_else(EventId::new); - let envelope = - common::managed_items_to_envelope(items, meta, state.outcome_aggregator(), event_id); + let envelope = items.map(|items, records| { + records.modify_by(DataCategory::Error, 1); + Box::new(Envelope::from_request(Some(event_id), meta).with_items(items)) + }); Ok(envelope) } @@ -400,7 +400,6 @@ async fn raw_minidump_to_envelope( item.set_filename(MINIDUMP_FILE_NAME); item.set_attachment_type(AttachmentType::Minidump); let mut item = Managed::with_meta_from_request_meta(&meta, state.outcome_aggregator(), item); - if let Some(upload_context) = upload_context && matches!(upload_context.upload_minidumps, UploadDecision::Upload) { @@ -416,23 +415,20 @@ async fn raw_minidump_to_envelope( .await .ok_or(BadStoreRequest::ObjectstoreUploadFailed)?; } else { - let decoded_payload = decode_minidump( - request.extract().await?, - state.config().max_attachment_size(), - )?; - item.modify(|inner, records| { - inner.set_payload(Minidump, decoded_payload); + let minidump_data = request.extract().await?; + item.try_modify(|inner, records| -> Result<(), BadStoreRequest> { + let payload = decode_minidump(minidump_data, state.config().max_attachment_size())?; + inner.set_payload(Minidump, payload); records.lenient(DataCategory::Attachment); // decoding the minidump changes its size - }); - validate_minidump(&item.payload())?; + validate_minidump(&inner.payload())?; + Ok(()) + })?; }; // Create an envelope with a random event id. - let envelope = Envelope::from_request(Some(EventId::new()), meta); - let mut envelope = Managed::from_envelope(envelope, state.outcome_aggregator().clone()); - envelope.merge_with(item, |envelope, item, records| { + let envelope = item.map(|item, records| { records.modify_by(DataCategory::Error, 1); - envelope.add_item(item); + Box::new(Envelope::from_request(Some(EventId::new()), meta).with_items(vec![item])) }); Ok(envelope) } diff --git a/relay-server/src/endpoints/playstation.rs b/relay-server/src/endpoints/playstation.rs index 567c7366b9f..7863360abd7 100644 --- a/relay-server/src/endpoints/playstation.rs +++ b/relay-server/src/endpoints/playstation.rs @@ -189,16 +189,22 @@ async fn multipart_to_envelope( ) .await?; - let prosperodump_item = items - .iter_mut() - .find(|item| item.attachment_type() == Some(AttachmentType::Prosperodump)) - .ok_or(BadStoreRequest::MissingProsperodump)?; - prosperodump_item.modify(|inner, _| inner.set_payload(OctetStream, inner.payload())); - validate_prosperodump(&prosperodump_item.payload())?; + items.try_modify(|inner, _| -> Result<(), BadStoreRequest> { + let prosperodump = inner + .iter_mut() + .find(|item| item.attachment_type() == Some(AttachmentType::Prosperodump)) + .ok_or(BadStoreRequest::MissingProsperodump)?; + let payload = prosperodump.payload(); + validate_prosperodump(&payload)?; + prosperodump.set_payload(OctetStream, payload); + Ok(()) + })?; let event_id = common::event_id_from_items(&items)?.unwrap_or_else(EventId::new); - let envelope = - common::managed_items_to_envelope(items, meta, state.outcome_aggregator(), event_id); + let envelope = items.map(|items, records| { + records.modify_by(DataCategory::Error, 1); + Box::new(Envelope::from_request(Some(event_id), meta).with_items(items)) + }); Ok(envelope) } diff --git a/relay-server/src/envelope/item.rs b/relay-server/src/envelope/item.rs index d6467b8d57b..a7512e33a83 100644 --- a/relay-server/src/envelope/item.rs +++ b/relay-server/src/envelope/item.rs @@ -12,7 +12,6 @@ use smallvec::{SmallVec, smallvec}; use crate::envelope::{AttachmentType, ContentType, EnvelopeError}; use crate::integrations::{Integration, LogsIntegration, SpansIntegration}; -use crate::managed::Managed; use crate::statsd::RelayTimers; #[derive(Clone, Debug)] @@ -770,7 +769,6 @@ impl Item { } } -pub type ManagedItems = SmallVec<[Managed; 3]>; pub type Items = SmallVec<[Item; 3]>; pub type ItemIter<'a> = std::slice::Iter<'a, Item>; pub type ItemIterMut<'a> = std::slice::IterMut<'a, Item>; diff --git a/relay-server/src/envelope/mod.rs b/relay-server/src/envelope/mod.rs index 5b67f481b8c..2fc78868e55 100644 --- a/relay-server/src/envelope/mod.rs +++ b/relay-server/src/envelope/mod.rs @@ -551,6 +551,14 @@ impl Envelope { self.items.push(item) } + /// Add new items and return `Self`. + pub fn with_items(mut self, items: impl IntoIterator) -> Self { + for item in items { + self.items.push(item) + } + self + } + /// Splits off the items from the envelope using provided predicates. /// /// First predicate is the additional condition on the count of found items by second diff --git a/relay-server/src/managed/managed.rs b/relay-server/src/managed/managed.rs index ad3c691411d..ccfc3539dcf 100644 --- a/relay-server/src/managed/managed.rs +++ b/relay-server/src/managed/managed.rs @@ -16,6 +16,7 @@ use relay_system::Addr; use smallvec::SmallVec; use crate::Envelope; +use crate::endpoints::common::BadStoreRequest; use crate::extractors::RequestMeta; use crate::managed::{Counted, ManagedEnvelope, Quantities}; use crate::services::outcome::{DiscardReason, Outcome, TrackOutcome}; @@ -88,6 +89,14 @@ impl OutcomeError for Infallible { } } +impl OutcomeError for BadStoreRequest { + type Error = Self; + + fn consume(self) -> (Option, Self) { + (self.to_outcome(), self) + } +} + /// A wrapper type which ensures outcomes have been emitted for an error. /// /// [`Managed`] wraps an error in [`Rejected`] once outcomes for have been emitted for the managed diff --git a/relay-server/src/services/outcome.rs b/relay-server/src/services/outcome.rs index 2611b50e156..ee3a7a45040 100644 --- a/relay-server/src/services/outcome.rs +++ b/relay-server/src/services/outcome.rs @@ -377,6 +377,12 @@ pub enum DiscardReason { /// since it resolves the project first, and then checks for the valid project key. MultiProjectId, + /// (Relay) A request without a prosperodump was made to the playstation endpoint. + MissingProsperodumpUpload, + + /// (Relay) The prosperodump submitted to the playstation endpoint was invalid. + InvalidProsperodump, + /// (Relay) A minidump file was missing for the minidump endpoint. MissingMinidumpUpload, @@ -509,6 +515,8 @@ impl DiscardReason { DiscardReason::DisallowedMethod => "disallowed_method", DiscardReason::ContentType => "content_type", DiscardReason::MultiProjectId => "multi_project_id", + DiscardReason::MissingProsperodumpUpload => "missing_prosperodump_upload", + DiscardReason::InvalidProsperodump => "invalid_prosperodump", DiscardReason::MissingMinidumpUpload => "missing_minidump_upload", DiscardReason::InvalidMinidump => "invalid_minidump", DiscardReason::SecurityReportType => "security_report_type", diff --git a/relay-server/src/utils/multipart.rs b/relay-server/src/utils/multipart.rs index 9a81cee7ff7..215819052af 100644 --- a/relay-server/src/utils/multipart.rs +++ b/relay-server/src/utils/multipart.rs @@ -13,10 +13,12 @@ use tokio::io::AsyncReadExt; use tokio_util::io::StreamReader; use crate::endpoints::common::BadStoreRequest; -use crate::envelope::{AttachmentType, ContentType, Item, ItemType, ManagedItems}; +use crate::envelope::{AttachmentType, ContentType, Item, ItemType, Items}; use crate::extractors::RequestMeta; use crate::managed::Managed; -use crate::services::outcome::TrackOutcome; +use crate::services::outcome::{ + DiscardAttachmentType, DiscardItemType, DiscardReason, Outcome, TrackOutcome, +}; /// Type used for encoding string lengths. type Len = u32; @@ -212,6 +214,9 @@ pub async fn read_attachment_bytes_into_item( records.lenient(DataCategory::Attachment); }); if n_bytes > limit { + let attachment_type = item.attachment_type().unwrap_or(AttachmentType::Attachment); + let item_type = DiscardItemType::Attachment(DiscardAttachmentType::from(attachment_type)); + let _ = item.reject_err(Outcome::Invalid(DiscardReason::TooLarge(item_type))); return match ignore_size_exceeded { true => Ok(None), false => Err(multer::Error::FieldSizeExceeded { @@ -229,11 +234,11 @@ pub async fn multipart_items( attachment_strategy: impl AttachmentStrategy, request_meta: &RequestMeta, outcome_aggregator: &Addr, -) -> Result { - let mut items = ManagedItems::new(); +) -> Result, multer::Error> { + let mut items = + Managed::with_meta_from_request_meta(request_meta, outcome_aggregator, Items::new()); let mut form_data = FormDataWriter::new(); let mut attachments_size = 0; - let meta_provider = Managed::with_meta_from_request_meta(request_meta, outcome_aggregator, ()); while let Some(field) = multipart.next_field().await? { if let Some(file_name) = field.file_name() { @@ -241,19 +246,31 @@ pub async fn multipart_items( let attachment_type = attachment_strategy.infer_type(&field); item.set_attachment_type(attachment_type); item.set_filename(file_name); - let item = meta_provider.wrap(item); - let item = attachment_strategy.add_to_item(field, item, config).await?; + let item = items.wrap(item); + let item = attachment_strategy + .add_to_item(field, item, config) + .await + .inspect_err(|e| { + if let multer::Error::FieldSizeExceeded { .. } = e { + let attachment_type = DiscardAttachmentType::from(attachment_type); + let item_type = DiscardItemType::Attachment(attachment_type); + let discard_reason = DiscardReason::TooLarge(item_type); + let _ = items.reject_err(Outcome::Invalid(discard_reason)); + } + })?; if let Some(item) = item { // This increases the attachments byte count even if the item is an attachment ref. // This is by design as the total number of bytes read into memory should be // constrained. attachments_size += item.len(); + items.merge_with(item, |items, item, _| items.push(item)); if attachments_size > config.max_attachments_size() { + let item_type = DiscardItemType::Attachment(DiscardAttachmentType::Attachment); + let _ = items.reject_err(Outcome::Invalid(DiscardReason::TooLarge(item_type))); return Err(multer::Error::StreamSizeExceeded { limit: config.max_attachments_size() as u64, }); } - items.push(item); } } else if let Some(field_name) = field.name().map(str::to_owned) { // Ensure to decode this SAFELY to match Django's POST data behavior. This allows us to @@ -271,7 +288,7 @@ pub async fn multipart_items( // Content type is `Text` (since it is not a json object but multiple // json arrays serialized one after the other). item.set_payload(ContentType::Text, form_data); - items.push(meta_provider.wrap(item)); + items.merge_with(items.wrap(item), |items, item, _| items.push(item)); } Ok(items) diff --git a/tests/integration/test_minidump.py b/tests/integration/test_minidump.py index ce63c9d1bbb..4f0e20a17fe 100644 --- a/tests/integration/test_minidump.py +++ b/tests/integration/test_minidump.py @@ -13,6 +13,7 @@ from urllib3.filepost import encode_multipart_formdata from sentry_relay.consts import DataCategory +from .asserts import time_within_delta from .test_attachment_ref import upload_and_make_ref from .consts import DUMMY_UPLOAD_LOCATION @@ -280,31 +281,104 @@ def test_minidump_invalid_json(mini_sentry, relay): assert_only_minidump(envelope) -def test_minidump_invalid_magic(mini_sentry, relay): +def test_minidump_invalid_magic(mini_sentry, relay_with_processing, outcomes_consumer): project_id = 42 - relay = relay(mini_sentry) mini_sentry.add_full_project_config(project_id) + outcomes_consumer = outcomes_consumer() + relay = relay_with_processing() - attachments = [ - (MINIDUMP_ATTACHMENT_NAME, "minidump.dmp", "content without MDMP magic"), - ] - - with pytest.raises(HTTPError): + content = b"content without MDMP magic" + attachments = [(MINIDUMP_ATTACHMENT_NAME, "minidump.dmp", content)] + with pytest.raises(HTTPError) as exc_info: relay.send_minidump(project_id=project_id, files=attachments) + assert exc_info.value.response.status_code == 400 + assert outcomes_consumer.get_outcomes() == [ + { + "timestamp": time_within_delta(), + "project_id": 42, + "outcome": 3, + "reason": "invalid_minidump", + "category": DataCategory.ATTACHMENT.value, + "quantity": len(content), + }, + { + "timestamp": time_within_delta(), + "project_id": 42, + "outcome": 3, + "reason": "invalid_minidump", + "category": DataCategory.ATTACHMENT_ITEM.value, + "quantity": 1, + }, + ] -def test_minidump_invalid_field(mini_sentry, relay): + +def test_minidump_invalid_field(mini_sentry, relay_with_processing, outcomes_consumer): project_id = 42 - relay = relay(mini_sentry) mini_sentry.add_full_project_config(project_id) + outcomes_consumer = outcomes_consumer() + relay = relay_with_processing() - attachments = [ - ("unknown_field_name", "minidump.dmp", "MDMP content"), + content = b"MDMP content" + attachments = [("unknown_field_name", "minidump.dmp", content)] + with pytest.raises(HTTPError) as exc_info: + relay.send_minidump(project_id=project_id, files=attachments) + + assert exc_info.value.response.status_code == 400 + assert outcomes_consumer.get_outcomes() == [ + { + "timestamp": time_within_delta(), + "project_id": 42, + "outcome": 3, + "reason": "missing_minidump_upload", + "category": DataCategory.ATTACHMENT.value, + "quantity": len(content), + }, + { + "timestamp": time_within_delta(), + "project_id": 42, + "outcome": 3, + "reason": "missing_minidump_upload", + "category": DataCategory.ATTACHMENT_ITEM.value, + "quantity": 1, + }, ] - with pytest.raises(HTTPError): + +def test_minidump_invalid_compression_outcome( + mini_sentry, relay_with_processing, outcomes_consumer +): + project_id = 42 + mini_sentry.add_full_project_config(project_id) + outcomes_consumer = outcomes_consumer() + relay = relay_with_processing() + content = b"\x1f\x8b" + b"not a valid gzip stream" + attachments = [(MINIDUMP_ATTACHMENT_NAME, "minidump.dmp.gz", content)] + + with pytest.raises(HTTPError) as exc_info: relay.send_minidump(project_id=project_id, files=attachments) + assert exc_info.value.response.status_code == 400 + outcomes = outcomes_consumer.get_outcomes() + assert outcomes == [ + { + "timestamp": time_within_delta(), + "project_id": 42, + "outcome": 3, + "reason": "invalid_compression", + "category": DataCategory.ATTACHMENT.value, + "quantity": len(content), + }, + { + "timestamp": time_within_delta(), + "project_id": 42, + "outcome": 3, + "reason": "invalid_compression", + "category": DataCategory.ATTACHMENT_ITEM.value, + "quantity": 1, + }, + ] + @pytest.mark.parametrize( "content_type", ("application/octet-stream", "application/x-dmp") @@ -1076,3 +1150,68 @@ def test_minidump_objectstore_uploads_rate_limits( assert mini_sentry.get_aggregated_outcomes() == sorted( expected_outcomes, key=lambda o: sorted(o.items()) ) + + +def test_minidump_max_attachment_size_exceeded( + mini_sentry, relay_with_processing, outcomes_consumer +): + project_id = 42 + dmp_path = os.path.join(os.path.dirname(__file__), "fixtures/native/minidump.dmp") + with open(dmp_path, "rb") as f: + minidump_content = f.read() + attachment_content = b"yo" + + mini_sentry.add_full_project_config(project_id) + outcomes_consumer = outcomes_consumer() + relay = relay_with_processing( + { + "limits": { + "max_attachment_size": len(minidump_content) - 1, + "max_attachments_size": 1000 * 1024 * 1024, + } + } + ) + + attachments = [ + ("attachment1", "attach1.txt", attachment_content), + (MINIDUMP_ATTACHMENT_NAME, "minidump.dmp", minidump_content), + ] + with pytest.raises(HTTPError) as exc_info: + relay.send_minidump(project_id=project_id, files=attachments) + + assert exc_info.value.response.status_code == 400 + outcomes = outcomes_consumer.get_outcomes() + assert outcomes == [ + { + "timestamp": time_within_delta(), + "project_id": 42, + "outcome": 3, + "reason": "too_large:attachment:minidump", + "category": DataCategory.ATTACHMENT.value, + "quantity": len(minidump_content), + }, + { + "timestamp": time_within_delta(), + "project_id": 42, + "outcome": 3, + "reason": "too_large:attachment:minidump", + "category": DataCategory.ATTACHMENT_ITEM.value, + "quantity": 1, + }, + { + "timestamp": time_within_delta(), + "project_id": 42, + "outcome": 3, + "reason": "too_large:attachment:minidump", + "category": DataCategory.ATTACHMENT.value, + "quantity": len(attachment_content), + }, + { + "timestamp": time_within_delta(), + "project_id": 42, + "outcome": 3, + "reason": "too_large:attachment:minidump", + "category": DataCategory.ATTACHMENT_ITEM.value, + "quantity": 1, + }, + ] diff --git a/tests/integration/test_playstation.py b/tests/integration/test_playstation.py index 288547bf538..fb1668e43b5 100644 --- a/tests/integration/test_playstation.py +++ b/tests/integration/test_playstation.py @@ -288,7 +288,7 @@ def test_playstation_invalid_prosperodump( "timestamp": time_within_delta(), "project_id": 42, "outcome": 3, - "reason": "internal", + "reason": "invalid_prosperodump", "category": 4, "quantity": len(playstation_dump), }, @@ -296,7 +296,7 @@ def test_playstation_invalid_prosperodump( "timestamp": time_within_delta(), "project_id": 42, "outcome": 3, - "reason": "internal", + "reason": "invalid_prosperodump", "category": 22, "quantity": 1, }, @@ -325,7 +325,7 @@ def test_playstation_missing_prosperodump( "timestamp": time_within_delta(), "project_id": 42, "outcome": 3, - "reason": "internal", + "reason": "missing_prosperodump_upload", "category": 4, "quantity": len(video_content), }, @@ -333,7 +333,7 @@ def test_playstation_missing_prosperodump( "timestamp": time_within_delta(), "project_id": 42, "outcome": 3, - "reason": "internal", + "reason": "missing_prosperodump_upload", "category": 22, "quantity": 1, }, @@ -367,7 +367,7 @@ def test_playstation_max_attachments_size_exceeded( "timestamp": time_within_delta(), "project_id": 42, "outcome": 3, - "reason": "internal", + "reason": "too_large:attachment:attachment", "category": 4, "quantity": len(playstation_dump), }, @@ -375,7 +375,7 @@ def test_playstation_max_attachments_size_exceeded( "timestamp": time_within_delta(), "project_id": 42, "outcome": 3, - "reason": "internal", + "reason": "too_large:attachment:attachment", "category": 22, "quantity": 1, }, @@ -409,7 +409,7 @@ def test_playstation_max_attachment_size_exceeded( "timestamp": time_within_delta(), "project_id": 42, "outcome": 3, - "reason": "internal", + "reason": "too_large:attachment:prosperodump", "category": 4, "quantity": len(playstation_dump), }, @@ -417,7 +417,7 @@ def test_playstation_max_attachment_size_exceeded( "timestamp": time_within_delta(), "project_id": 42, "outcome": 3, - "reason": "internal", + "reason": "too_large:attachment:prosperodump", "category": 22, "quantity": 1, },