1use crate::scenario::Scenario;
9
10#[derive(Debug, Clone, Default)]
12pub struct Spec {
13 pub auth_b64: Option<String>,
15 pub partial_first_put: Option<usize>,
17 pub cut_first_get: Option<(usize, bool)>,
19 pub drip: Option<super::server::Drip>,
21 pub sizeless: bool,
23 pub rate_first: Option<(usize, u64)>,
25 pub flaky_first: Option<u32>,
27 pub pause_first_503: Option<u64>,
29 pub ignore_range: bool,
31 pub markerless: bool,
33 pub foreign_marker: bool,
35 pub read_only: bool,
37 pub redirect: Option<String>,
39 pub drop_connection: bool,
41 pub freeze_upload: bool,
43}
44
45impl Spec {
46 pub fn atomic() -> Self {
48 Self::default()
49 }
50
51 pub fn with_auth(mut self, user: &str, pass: &str) -> Self {
53 self.auth_b64 = Some(crate::base64::encode(format!("{user}:{pass}").as_bytes()));
54 self
55 }
56
57 pub fn with_partial_put(mut self, bytes: usize) -> Self {
59 self.partial_first_put = Some(bytes);
60 self
61 }
62
63 pub fn with_cut_body(mut self, bytes: usize, fake_length: bool) -> Self {
65 self.cut_first_get = Some((bytes, fake_length));
66 self
67 }
68
69 pub fn with_drip(mut self, delay_ms: u64, chunk_size: usize) -> Self {
71 self.drip = Some(super::server::Drip {
72 delay_ms,
73 chunk_size,
74 });
75 self
76 }
77
78 pub fn with_sizeless(mut self) -> Self {
80 self.sizeless = true;
81 self
82 }
83
84 pub fn with_rate_limit(mut self, n: usize, retry_after_secs: u64) -> Self {
86 self.rate_first = Some((n, retry_after_secs));
87 self
88 }
89
90 pub fn with_flaky(mut self, n: u32) -> Self {
92 self.flaky_first = Some(n);
93 self
94 }
95
96 pub fn with_retry_after_503(mut self, retry_after_secs: u64) -> Self {
98 self.pause_first_503 = Some(retry_after_secs);
99 self
100 }
101
102 pub fn with_ignore_range(mut self) -> Self {
104 self.ignore_range = true;
105 self
106 }
107
108 pub fn with_markerless(mut self) -> Self {
110 self.markerless = true;
111 self
112 }
113
114 pub fn with_foreign_marker(mut self) -> Self {
116 self.foreign_marker = true;
117 self
118 }
119
120 pub fn with_read_only(mut self) -> Self {
122 self.read_only = true;
123 self
124 }
125
126 pub fn with_redirect(mut self, location_path: &str) -> Self {
128 self.redirect = Some(location_path.to_owned());
129 self
130 }
131
132 pub fn with_drop_connection(mut self) -> Self {
134 self.drop_connection = true;
135 self
136 }
137
138 pub fn with_freeze_upload(mut self) -> Self {
140 self.freeze_upload = true;
141 self
142 }
143}
144
145impl From<&Scenario> for Spec {
146 fn from(scenario: &Scenario) -> Self {
147 let mut spec = Self::default();
148 match scenario {
149 Scenario::PartialPut {
150 first_attempt_bytes,
151 } => spec.partial_first_put = Some(*first_attempt_bytes),
152 Scenario::CutBody {
153 after_bytes,
154 fake_length,
155 } => spec.cut_first_get = Some((*after_bytes, *fake_length)),
156 Scenario::DropConnection => spec.drop_connection = true,
157 Scenario::FreezeUpload => spec.freeze_upload = true,
158 Scenario::Sizeless => spec.sizeless = true,
159 Scenario::Slow {
160 chunk_delay_ms,
161 chunk_size,
162 } => {
163 spec.drip = Some(super::server::Drip {
164 delay_ms: *chunk_delay_ms,
165 chunk_size: *chunk_size,
166 })
167 }
168 Scenario::ForeignMarker => spec.foreign_marker = true,
169 Scenario::Markerless => spec.markerless = true,
170 Scenario::Auth401 { user, pass } => {
171 spec.auth_b64 = Some(crate::base64::encode(format!("{user}:{pass}").as_bytes()))
172 }
173 Scenario::Auth403 { user, pass } => {
174 spec.auth_b64 = Some(crate::base64::encode(format!("{user}:{pass}").as_bytes()))
175 }
176 Scenario::Flaky { first_failures } => spec.flaky_first = Some(*first_failures),
177 Scenario::RetryAfter503 { retry_after_secs } => {
178 spec.pause_first_503 = Some(*retry_after_secs)
179 }
180 Scenario::RateLimit {
181 first_429s,
182 retry_after_secs,
183 } => spec.rate_first = Some((*first_429s, *retry_after_secs)),
184 Scenario::Redirect { location_path } => spec.redirect = Some(location_path.clone()),
185 Scenario::ReadOnly => spec.read_only = true,
186 Scenario::DocDrift => {}
187 Scenario::NoService => {}
188 Scenario::Search400 => {}
189 Scenario::ServiceStatusEmpty => {}
190 Scenario::ServiceStatusVersion => {}
191 Scenario::ServiceStatusDown => {}
192 Scenario::RepoCollectionTrimmed => {}
193 Scenario::RepoCollectionScoped { .. } => {}
194 Scenario::RepoDetailAdmin { .. } => {}
195 Scenario::RepoPrefixMatch => {}
196 Scenario::AssetsPagination => {}
197 Scenario::AssetsAbsent => {}
198 Scenario::AssetsPrefixQ => {}
199 Scenario::EulaGate => {}
200 Scenario::EulaAbsent => {}
201 Scenario::SearchLag { .. } => {}
202 Scenario::DetailInHint => {}
203 Scenario::IgnoreRange => spec.ignore_range = true,
204 Scenario::AuditLitter => {}
205 Scenario::Atomic => {}
206 }
207 spec
208 }
209}