1use azure_core::Url;
4
5#[derive(Debug, thiserror::Error, Clone, PartialEq, Eq)]
7pub enum FormatError {
8 #[error("Connection string cannot be empty")]
10 ConnectionStringIsEmpty,
11
12 #[error("Connection string is malformed")]
14 InvalidConnectionString,
15}
16
17impl From<FormatError> for azure_core::Error {
18 fn from(err: FormatError) -> Self {
19 use azure_core::error::ErrorKind;
20
21 azure_core::Error::new(ErrorKind::Other, err)
22 }
23}
24
25#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
27pub enum ToConnectionStringError {
28 #[error("Missing connection information")]
30 MissingConnectionInformation,
31
32 #[error("Invalid endpoint address")]
34 InvalidEndpointAddress,
35
36 #[error("Only one shared access authorization can be used")]
38 OnlyOneSharedAccessAuthorizationMayBeUsed,
39}
40
41impl From<ToConnectionStringError> for azure_core::Error {
42 fn from(err: ToConnectionStringError) -> Self {
43 use azure_core::error::ErrorKind;
44
45 azure_core::Error::new(ErrorKind::Other, err)
46 }
47}
48
49#[derive(Debug, PartialEq, Eq, Hash)]
51pub struct EventHubsConnectionStringProperties<'a> {
52 pub(crate) endpoint: Option<url::Url>,
53 pub(crate) event_hub_name: Option<&'a str>,
54 pub(crate) shared_access_key_name: Option<&'a str>,
55 pub(crate) shared_access_key: Option<&'a str>,
56 pub(crate) shared_access_signature: Option<&'a str>,
57}
58
59impl<'a> EventHubsConnectionStringProperties<'a> {
60 const TOKEN_VALUE_SEPARATOR: char = '=';
62
63 const TOKEN_VALUE_PAIR_DELIMITER: char = ';';
65
66 const SERVICE_BUS_ENDPOINT_SCHEME_NAME: &'static str = "sb";
68
69 const ENDPOINT_TOKEN: &'static str = "Endpoint";
71
72 const EVENT_HUB_NAME_TOKEN: &'static str = "EntityPath";
74
75 const SHARED_ACCESS_KEY_NAME_TOKEN: &'static str = "SharedAccessKeyName";
77
78 const SHARED_ACCESS_KEY_VALUE_TOKEN: &'static str = "SharedAccessKey";
80
81 const SHARED_ACCESS_SIGNATURE_TOKEN: &'static str = "SharedAccessSignature";
83
84 pub fn fully_qualified_namespace(&self) -> Option<&str> {
87 self.endpoint.as_ref().and_then(|url| url.host_str())
88 }
89
90 pub fn endpoint(&self) -> Option<&Url> {
92 self.endpoint.as_ref()
93 }
94
95 pub fn event_hub_name(&self) -> Option<&str> {
98 self.event_hub_name
99 }
100
101 pub fn shared_access_key_name(&self) -> Option<&str> {
104 self.shared_access_key_name
105 }
106
107 pub fn shared_access_key(&self) -> Option<&str> {
110 self.shared_access_key
111 }
112
113 pub fn shared_access_signature(&self) -> Option<&str> {
116 self.shared_access_signature
117 }
118
119 pub fn to_connection_string(&self) -> Result<String, ToConnectionStringError> {
122 let mut s = String::new();
123
124 if let Some(endpoint) = self.endpoint() {
125 if endpoint.scheme() != Self::SERVICE_BUS_ENDPOINT_SCHEME_NAME {
126 return Err(ToConnectionStringError::InvalidEndpointAddress);
128 }
129
130 s.push_str(Self::ENDPOINT_TOKEN);
131 s.push(Self::TOKEN_VALUE_SEPARATOR);
132 s.push_str(endpoint.as_str());
133 s.push(Self::TOKEN_VALUE_PAIR_DELIMITER);
134 } else {
135 return Err(ToConnectionStringError::MissingConnectionInformation);
136 }
137
138 if let Some(event_hub_name) = self.event_hub_name.and_then(|s| match !s.is_empty() {
139 true => Some(s),
140 false => None,
141 }) {
142 s.push_str(Self::EVENT_HUB_NAME_TOKEN);
143 s.push(Self::TOKEN_VALUE_SEPARATOR);
144 s.push_str(event_hub_name);
145 s.push(Self::TOKEN_VALUE_PAIR_DELIMITER);
146 }
147
148 match (
151 self.shared_access_signature,
152 self.shared_access_key_name,
153 self.shared_access_key,
154 ) {
155 (Some(signature), None, None) => {
156 if !signature.is_empty() {
157 s.push_str(Self::SHARED_ACCESS_SIGNATURE_TOKEN);
158 s.push(Self::TOKEN_VALUE_SEPARATOR);
159 s.push_str(signature);
160 s.push(Self::TOKEN_VALUE_PAIR_DELIMITER);
161 }
162 }
163 (None, Some(key_name), Some(key)) => {
164 if (!key_name.is_empty()) && (!key.is_empty()) {
165 s.push_str(Self::SHARED_ACCESS_KEY_NAME_TOKEN);
166 s.push(Self::TOKEN_VALUE_SEPARATOR);
167 s.push_str(key_name);
168 s.push(Self::TOKEN_VALUE_PAIR_DELIMITER);
169
170 s.push_str(Self::SHARED_ACCESS_KEY_VALUE_TOKEN);
171 s.push(Self::TOKEN_VALUE_SEPARATOR);
172 s.push_str(key);
173 s.push(Self::TOKEN_VALUE_PAIR_DELIMITER);
174 }
175 }
176 _ => {
177 return Err(ToConnectionStringError::OnlyOneSharedAccessAuthorizationMayBeUsed);
178 }
179 }
180
181 Ok(s)
182 }
183
184 pub fn parse(connection_string: &'a str) -> Result<Self, FormatError> {
186 if connection_string.is_empty() {
187 return Err(FormatError::ConnectionStringIsEmpty);
188 }
189
190 let mut endpoint: Option<Url> = None;
191 let mut event_hub_name: Option<&'a str> = None;
192 let mut shared_access_key_name: Option<&'a str> = None;
193 let mut shared_access_key: Option<&'a str> = None;
194 let mut shared_access_signature: Option<&'a str> = None;
195
196 let token_value_pairs = connection_string.split(Self::TOKEN_VALUE_PAIR_DELIMITER);
197
198 for token_value_pair in token_value_pairs {
199 let mut split = token_value_pair.split_inclusive(Self::TOKEN_VALUE_SEPARATOR);
201 let token = match split
202 .next()
203 .and_then(|s| s.split(Self::TOKEN_VALUE_SEPARATOR).next())
204 .and_then(|s| match s.trim() {
205 "" => None,
206 s => Some(s),
207 }) {
208 Some(token) => token,
209 None => continue,
210 };
211
212 let value = split
213 .next()
214 .and_then(|s| match s.trim() {
215 "" => None,
216 s => Some(s),
217 })
218 .ok_or(FormatError::InvalidConnectionString)?;
221
222 match token {
225 Self::ENDPOINT_TOKEN => {
226 let mut url =
228 Url::parse(value).map_err(|_| FormatError::InvalidConnectionString)?;
229 url.set_scheme(Self::SERVICE_BUS_ENDPOINT_SCHEME_NAME)
230 .map_err(|_| FormatError::InvalidConnectionString)?;
231 endpoint = Some(url);
232 }
233 Self::EVENT_HUB_NAME_TOKEN => event_hub_name = Some(value),
234 Self::SHARED_ACCESS_KEY_NAME_TOKEN => shared_access_key_name = Some(value),
235 Self::SHARED_ACCESS_KEY_VALUE_TOKEN => shared_access_key = Some(value),
236 Self::SHARED_ACCESS_SIGNATURE_TOKEN => shared_access_signature = Some(value),
237 _ => {}
238 }
239 }
240
241 Ok(Self {
242 endpoint,
243 event_hub_name,
244 shared_access_key_name,
245 shared_access_key,
246 shared_access_signature,
247 })
248 }
249}
250
251#[cfg(test)]
252mod tests {
253 use super::{EventHubsConnectionStringProperties, FormatError};
254
255 const ENDPOINT: &str = "test.endpoint.com";
256 const EVENT_HUB: &str = "some-path";
257 const SAS_KEY_NAME: &str = "sasName";
258 const SAS_KEY: &str = "sasKey";
259 const SAS: &str = "fullsas";
260
261 struct Expected {
263 endpoint: Option<&'static str>,
264 event_hub: Option<&'static str>,
265 sas_key_name: Option<&'static str>,
266 sas_key: Option<&'static str>,
267 sas: Option<&'static str>,
268 }
269
270 macro_rules! assert_parsed_and_expected {
271 ($connection_string:ident, $expected:ident) => {
272 let parsed = EventHubsConnectionStringProperties::parse(&$connection_string).unwrap();
273
274 assert_eq!(
275 parsed.endpoint().and_then(|url| url.host_str()),
276 $expected.endpoint
277 );
278 assert_eq!(parsed.shared_access_key_name(), $expected.sas_key_name);
279 assert_eq!(parsed.shared_access_key(), $expected.sas_key);
280 assert_eq!(parsed.shared_access_signature(), $expected.sas);
281 assert_eq!(parsed.event_hub_name(), $expected.event_hub);
282 };
283 }
284
285 fn random_ordering_connection_string_cases() -> Vec<(String, Expected)> {
286 vec![
287 (
288 format!(
289 "Endpoint=sb://{};SharedAccessKeyName={};SharedAccessKey={};EntityPath={}",
290 ENDPOINT, SAS_KEY_NAME, SAS_KEY, EVENT_HUB
291 ),
292 Expected {
293 endpoint: Some(ENDPOINT),
294 event_hub: Some(EVENT_HUB),
295 sas_key_name: Some(SAS_KEY_NAME),
296 sas_key: Some(SAS_KEY),
297 sas: None,
298 },
299 ),
300 (
301 format!(
302 "Endpoint=sb://{};SharedAccessKey={};EntityPath={};SharedAccessKeyName={}",
303 ENDPOINT, SAS_KEY, EVENT_HUB, SAS_KEY_NAME,
304 ),
305 Expected {
306 endpoint: Some(ENDPOINT),
307 event_hub: Some(EVENT_HUB),
308 sas_key_name: Some(SAS_KEY_NAME),
309 sas_key: Some(SAS_KEY),
310 sas: None,
311 },
312 ),
313 (
314 format!(
315 "Endpoint=sb://{};EntityPath={};SharedAccessKeyName={};SharedAccessKey={}",
316 ENDPOINT, EVENT_HUB, SAS_KEY_NAME, SAS_KEY
317 ),
318 Expected {
319 endpoint: Some(ENDPOINT),
320 event_hub: Some(EVENT_HUB),
321 sas_key_name: Some(SAS_KEY_NAME),
322 sas_key: Some(SAS_KEY),
323 sas: None,
324 },
325 ),
326 (
327 format!(
328 "SharedAccessKeyName={};SharedAccessKey={};Endpoint=sb://{};EntityPath={}",
329 SAS_KEY_NAME, SAS_KEY, ENDPOINT, EVENT_HUB
330 ),
331 Expected {
332 endpoint: Some(ENDPOINT),
333 event_hub: Some(EVENT_HUB),
334 sas_key_name: Some(SAS_KEY_NAME),
335 sas_key: Some(SAS_KEY),
336 sas: None,
337 },
338 ),
339 (
340 format!(
341 "EntityPath={};SharedAccessKey={};SharedAccessKeyName={};Endpoint=sb://{}",
342 EVENT_HUB, SAS_KEY, SAS_KEY_NAME, ENDPOINT
343 ),
344 Expected {
345 endpoint: Some(ENDPOINT),
346 event_hub: Some(EVENT_HUB),
347 sas_key_name: Some(SAS_KEY_NAME),
348 sas_key: Some(SAS_KEY),
349 sas: None,
350 },
351 ),
352 (
353 format!(
354 "EntityPath={};SharedAccessSignature={};Endpoint=sb://{}",
355 EVENT_HUB, SAS, ENDPOINT,
356 ),
357 Expected {
358 endpoint: Some(ENDPOINT),
359 event_hub: Some(EVENT_HUB),
360 sas_key_name: None,
361 sas_key: None,
362 sas: Some(SAS),
363 },
364 ),
365 (
366 format!(
367 "SharedAccessKeyName={};SharedAccessKey={};Endpoint=sb://{};EntityPath={};SharedAccessSignature={}",
368 SAS_KEY_NAME, SAS_KEY, ENDPOINT, EVENT_HUB, SAS
369 ),
370 Expected {
371 endpoint: Some(ENDPOINT),
372 event_hub: Some(EVENT_HUB),
373 sas_key_name: Some(SAS_KEY_NAME),
374 sas_key: Some(SAS_KEY),
375 sas: Some(SAS),
376 },
377 ),
378 ]
379 }
380
381 fn partial_connection_string_cases() -> Vec<(String, Expected)> {
382 vec![
383 (
384 format!("Endpoint=sb://{}", ENDPOINT),
385 Expected {
386 endpoint: Some(ENDPOINT),
387 event_hub: None,
388 sas_key_name: None,
389 sas_key: None,
390 sas: None,
391 },
392 ),
393 (
394 format!("SharedAccessKey={}", SAS_KEY),
395 Expected {
396 endpoint: None,
397 event_hub: None,
398 sas_key_name: None,
399 sas_key: Some(SAS_KEY),
400 sas: None,
401 },
402 ),
403 (
404 format!(
405 "EntityPath={};SharedAccessKeyName={}",
406 EVENT_HUB, SAS_KEY_NAME
407 ),
408 Expected {
409 endpoint: None,
410 event_hub: Some(EVENT_HUB),
411 sas_key_name: Some(SAS_KEY_NAME),
412 sas_key: None,
413 sas: None,
414 },
415 ),
416 (
417 format!(
418 "SharedAccessKeyName={};SharedAccessKey={}",
419 SAS_KEY_NAME, SAS_KEY
420 ),
421 Expected {
422 endpoint: None,
423 event_hub: None,
424 sas_key_name: Some(SAS_KEY_NAME),
425 sas_key: Some(SAS_KEY),
426 sas: None,
427 },
428 ),
429 (
430 format!(
431 "EntityPath={};SharedAccessKey={};SharedAccessKeyName={}",
432 EVENT_HUB, SAS_KEY, SAS_KEY_NAME
433 ),
434 Expected {
435 endpoint: None,
436 event_hub: Some(EVENT_HUB),
437 sas_key_name: Some(SAS_KEY_NAME),
438 sas_key: Some(SAS_KEY),
439 sas: None,
440 },
441 ),
442 (
443 format!(
444 "SharedAccessKeyName={};SharedAccessSignature={}",
445 SAS_KEY_NAME, SAS
446 ),
447 Expected {
448 endpoint: None,
449 event_hub: None,
450 sas_key_name: Some(SAS_KEY_NAME),
451 sas_key: None,
452 sas: Some(SAS),
453 },
454 ),
455 (
456 format!("EntityPath={};SharedAccessSignature={}", EVENT_HUB, SAS),
457 Expected {
458 endpoint: None,
459 event_hub: Some(EVENT_HUB),
460 sas_key_name: None,
461 sas_key: None,
462 sas: Some(SAS),
463 },
464 ),
465 (
466 format!(
467 "EntityPath={};SharedAccessKey={};SharedAccessKeyName={};SharedAccessSignature={}",
468 EVENT_HUB, SAS_KEY, SAS_KEY_NAME, SAS
469 ),
470 Expected {
471 endpoint: None,
472 event_hub: Some(EVENT_HUB),
473 sas_key_name: Some(SAS_KEY_NAME),
474 sas_key: Some(SAS_KEY),
475 sas: Some(SAS),
476 },
477 ),
478 ]
479 }
480
481 fn to_connection_string_validates_properties_cases(
482 ) -> Vec<EventHubsConnectionStringProperties<'static>> {
483 let mut cases = Vec::new();
484 let case = EventHubsConnectionStringProperties {
486 endpoint: None,
487 event_hub_name: Some("fake"),
488 shared_access_signature: Some("fake"),
489 shared_access_key_name: None,
490 shared_access_key: None,
491 };
492 cases.push(case);
493
494 let case = EventHubsConnectionStringProperties {
496 endpoint: Some(url::Url::parse("sb://someplace.hosname.ext").unwrap()),
497 event_hub_name: Some("fake"),
498 shared_access_signature: None,
499 shared_access_key_name: None,
500 shared_access_key: None,
501 };
502 cases.push(case);
503
504 let case = EventHubsConnectionStringProperties {
506 endpoint: Some(url::Url::parse("sb://someplace.hosname.ext").unwrap()),
507 event_hub_name: Some("fake"),
508 shared_access_signature: Some("fake"),
509 shared_access_key: Some("fake"),
510 shared_access_key_name: None,
511 };
512 cases.push(case);
513
514 let case = EventHubsConnectionStringProperties {
516 endpoint: Some(url::Url::parse("sb://someplace.hosname.ext").unwrap()),
517 event_hub_name: Some("fake"),
518 shared_access_signature: Some("fake"),
519 shared_access_key_name: Some("fake"),
520 shared_access_key: None,
521 };
522 cases.push(case);
523
524 let case = EventHubsConnectionStringProperties {
526 endpoint: Some(url::Url::parse("sb://someplace.hosname.ext").unwrap()),
527 event_hub_name: Some("fake"),
528 shared_access_signature: None,
529 shared_access_key_name: Some("fake"),
530 shared_access_key: None,
531 };
532 cases.push(case);
533
534 let case = EventHubsConnectionStringProperties {
536 endpoint: Some(url::Url::parse("sb://someplace.hosname.ext").unwrap()),
537 event_hub_name: Some("fake"),
538 shared_access_signature: None,
539 shared_access_key_name: None,
540 shared_access_key: Some("fake"),
541 };
542 cases.push(case);
543
544 cases
545 }
546
547 #[test]
548 fn parse_correctly_parses_a_namespace_connection_string() {
549 let endpoint = "test.endpoint.com";
550 let sas_key = "sasKey=";
551 let sas_key_name = "sasName";
552 let shared_access_signature = "fakeSAS";
553 let connection_string = format!("Endpoint=sb://{endpoint};SharedAccessKeyName={sas_key_name};SharedAccessKey={sas_key};SharedAccessSignature={shared_access_signature}");
554 let parsed = EventHubsConnectionStringProperties::parse(&connection_string).unwrap();
555
556 assert_eq!(
557 parsed.endpoint().and_then(|url| url.host_str()),
558 Some(endpoint)
559 );
560 assert_eq!(parsed.shared_access_key_name(), Some(sas_key_name));
561 assert_eq!(parsed.shared_access_key(), Some(sas_key));
562 assert_eq!(
563 parsed.shared_access_signature(),
564 Some(shared_access_signature)
565 );
566 assert_eq!(parsed.event_hub_name(), None);
567 }
568
569 #[test]
570 fn parse_correctly_parses_an_entity_connection_string() {
571 let endpoint = "test.endpoint.com";
572 let event_hub = "some-path";
573 let sas_key = "sasKey";
574 let sas_key_name = "sasName";
575 let shared_access_signature = "fakeSAS";
576 let connection_string = format!("Endpoint=sb://{endpoint};SharedAccessKeyName={sas_key_name};SharedAccessKey={sas_key};EntityPath={event_hub};SharedAccessSignature={shared_access_signature}");
577 let parsed = EventHubsConnectionStringProperties::parse(&connection_string).unwrap();
578
579 assert_eq!(
580 parsed.endpoint().and_then(|url| url.host_str()),
581 Some(endpoint)
582 );
583 assert_eq!(parsed.shared_access_key_name(), Some(sas_key_name));
584 assert_eq!(parsed.shared_access_key(), Some(sas_key));
585 assert_eq!(
586 parsed.shared_access_signature(),
587 Some(shared_access_signature)
588 );
589 assert_eq!(parsed.event_hub_name(), Some(event_hub));
590 }
591
592 #[test]
593 fn parse_correctly_parses_partial_connection_strings() {
594 let cases = partial_connection_string_cases();
595
596 for (connection_string, expected) in cases {
597 assert_parsed_and_expected!(connection_string, expected);
598 }
599 }
600
601 #[test]
602 fn parse_tolerates_leading_delimiters() {
603 let endpoint = "test.endpoint.com";
604 let event_hub = "some-path";
605 let sas_key = "sasKey";
606 let sas_key_name = "sasName";
607 let connection_string = format!(";Endpoint=sb://{endpoint};SharedAccessKeyName={sas_key_name};SharedAccessKey={sas_key};EntityPath={event_hub}");
608 let parsed = EventHubsConnectionStringProperties::parse(&connection_string).unwrap();
609
610 assert_eq!(
611 parsed.endpoint().and_then(|url| url.host_str()),
612 Some(endpoint)
613 );
614 assert_eq!(parsed.shared_access_key_name(), Some(sas_key_name));
615 assert_eq!(parsed.shared_access_key(), Some(sas_key));
616 assert_eq!(parsed.event_hub_name(), Some(event_hub));
617 }
618
619 #[test]
620 fn parse_tolerates_spaces_between_pairs() {
621 let endpoint = "test.endpoint.com";
622 let event_hub = "some-path";
623 let sas_key = "sasKey";
624 let sas_key_name = "sasName";
625 let connection_string = format!("Endpoint=sb://{endpoint}; SharedAccessKeyName={sas_key_name}; SharedAccessKey={sas_key}; EntityPath={event_hub}");
626 let parsed = EventHubsConnectionStringProperties::parse(&connection_string).unwrap();
627
628 assert_eq!(
629 parsed.endpoint().and_then(|url| url.host_str()),
630 Some(endpoint)
631 );
632 assert_eq!(parsed.shared_access_key_name(), Some(sas_key_name));
633 assert_eq!(parsed.shared_access_key(), Some(sas_key));
634 assert_eq!(parsed.event_hub_name(), Some(event_hub));
635 }
636
637 #[test]
638 fn parse_tolerates_spaces_between_values() {
639 let endpoint = "test.endpoint.com";
640 let event_hub = "some-path";
641 let sas_key = "sasKey";
642 let sas_key_name = "sasName";
643 let connection_string = format!("Endpoint = sb://{endpoint};SharedAccessKeyName ={sas_key_name};SharedAccessKey= {sas_key}; EntityPath = {event_hub}");
644 let parsed = EventHubsConnectionStringProperties::parse(&connection_string).unwrap();
645
646 assert_eq!(
647 parsed.endpoint().and_then(|url| url.host_str()),
648 Some(endpoint)
649 );
650 assert_eq!(parsed.shared_access_key_name(), Some(sas_key_name));
651 assert_eq!(parsed.shared_access_key(), Some(sas_key));
652 assert_eq!(parsed.event_hub_name(), Some(event_hub));
653 }
654
655 #[test]
656 fn parse_does_not_force_token_ordering() {
657 let cases = random_ordering_connection_string_cases();
658
659 for (connection_string, expected) in cases {
660 assert_parsed_and_expected!(connection_string, expected);
661 }
662 }
663
664 #[test]
665 fn parse_ignores_unknown_tokens() {
666 let endpoint = "test.endpoint.com";
667 let event_hub = "some-path";
668 let sas_key = "sasKey";
669 let sas_key_name = "sasName";
670 let connection_string = format!("Endpoint=sb://{endpoint};SharedAccessKeyName={sas_key_name};Unknown=INVALID;SharedAccessKey={sas_key};EntityPath={event_hub};Trailing=WHOAREYOU");
671 let parsed = EventHubsConnectionStringProperties::parse(&connection_string).unwrap();
672
673 assert_eq!(
674 parsed.endpoint().and_then(|url| url.host_str()),
675 Some(endpoint)
676 );
677 assert_eq!(parsed.shared_access_key_name(), Some(sas_key_name));
678 assert_eq!(parsed.shared_access_key(), Some(sas_key));
679 assert_eq!(parsed.event_hub_name(), Some(event_hub));
680 }
681
682 #[test]
683 fn parse_does_accept_host_names_and_urls_for_the_endpoint() {
684 let endpoint_values = &[
685 "sb://test.endpoint.com",
687 "sb://test.endpoint.com:80",
688 "amqp://test.endpoint.com",
689 ];
692
693 for endpoint_value in endpoint_values {
694 let connection_string = format!("Endpoint={};EntityPath=dummy", endpoint_value);
695 let parsed = EventHubsConnectionStringProperties::parse(&connection_string).unwrap();
696
697 assert_eq!(
698 parsed.endpoint().and_then(|url| url.host_str()),
699 Some("test.endpoint.com")
700 );
701 }
702 }
703
704 #[test]
705 fn parse_does_not_allow_an_invalid_endpoint_format() {
706 let endpoint = "test.endpoint.com";
707 let connection_string = format!("Endpoint={}", endpoint);
708 let result = EventHubsConnectionStringProperties::parse(&connection_string);
709 assert!(result.is_err());
710 }
711
712 #[test]
713 fn parse_considers_missing_values_as_malformed() {
714 let test_cases = &[
715 "Endpoint;SharedAccessKeyName=[value];SharedAccessKey=[value];EntityPath=[value]",
716 "Endpoint=value.com;SharedAccessKeyName=;SharedAccessKey=[value];EntityPath=[value]",
717 "Endpoint=value.com;SharedAccessKeyName=[value];SharedAccessKey;EntityPath=[value]",
718 "Endpoint=value.com;SharedAccessKeyName=[value];SharedAccessKey=[value];EntityPath",
719 "Endpoint;SharedAccessKeyName=;SharedAccessKey;EntityPath=",
720 "Endpoint=;SharedAccessKeyName;SharedAccessKey;EntityPath=",
721 ];
722
723 for test_case in test_cases {
724 let result = EventHubsConnectionStringProperties::parse(test_case);
725 assert_eq!(result, Err(FormatError::InvalidConnectionString));
726 }
727 }
728
729 #[test]
730 fn to_string_validates_properties() {
731 let cases = to_connection_string_validates_properties_cases();
732
733 for case in cases {
734 let result = case.to_connection_string();
735 assert!(result.is_err());
736 }
737 }
738
739 #[test]
740 fn to_connection_string_produces_the_connection_string_for_shared_access_signatures() {
741 let properties = EventHubsConnectionStringProperties {
742 endpoint: Some("sb://place.endpoint.ext".parse().unwrap()),
743 event_hub_name: Some("HubName"),
744 shared_access_signature: Some("FaKe#$1324@@"),
745 shared_access_key_name: None,
746 shared_access_key: None,
747 };
748
749 let connection_string = properties.to_connection_string();
750 assert!(connection_string.is_ok());
751 let connection_string = connection_string.unwrap();
752 assert!(!connection_string.is_empty());
753
754 let parsed = EventHubsConnectionStringProperties::parse(&connection_string);
755 assert!(parsed.is_ok());
756 assert_eq!(properties, parsed.unwrap());
757 }
758
759 #[test]
760 fn to_connection_string_produces_the_connection_string_for_shared_keys() {
761 let properties = EventHubsConnectionStringProperties {
762 endpoint: Some("sb://place.endpoint.ext".parse().unwrap()),
763 event_hub_name: Some("HubName"),
764 shared_access_signature: None,
765 shared_access_key_name: Some("RootSharedAccessManagementKey"),
766 shared_access_key: Some("FaKe#$1324@@"),
767 };
768
769 let connection_string = properties.to_connection_string();
770 assert!(connection_string.is_ok());
771 let connection_string = connection_string.unwrap();
772 assert!(!connection_string.is_empty());
773
774 let parsed = EventHubsConnectionStringProperties::parse(&connection_string);
775 assert!(parsed.is_ok());
776 assert_eq!(properties, parsed.unwrap());
777 }
778
779 #[test]
780 fn to_connection_string_returns_err_with_non_servicebus_endpoint_scheme() {
781 let schemes = vec![
782 "amqps://", "amqp://",
783 "http://", "https://", "fake://",
785 ];
786
787 for scheme in schemes {
788 let endpoint = format!("{}myhub.servicebus.windows.net", scheme);
789 let properties = EventHubsConnectionStringProperties {
790 endpoint: Some(url::Url::parse(&endpoint).unwrap()),
791 event_hub_name: Some("HubName"),
792 shared_access_signature: None,
793 shared_access_key_name: Some("RootSharedAccessManagementKey"),
794 shared_access_key: Some("FaKe#$1324@@"),
795 };
796
797 let connection_string = properties.to_connection_string();
798 assert!(connection_string.is_err());
799 }
800 }
801
802 #[test]
803 fn to_connection_string_returns_ok_with_servicebus_endpoint_scheme() {
804 let endpoint = "sb://myhub.servicebus.windows.net";
805 let properties = EventHubsConnectionStringProperties {
806 endpoint: Some(url::Url::parse(endpoint).unwrap()),
807 event_hub_name: Some("HubName"),
808 shared_access_signature: None,
809 shared_access_key_name: Some("RootSharedAccessManagementKey"),
810 shared_access_key: Some("FaKe#$1324@@"),
811 };
812
813 let connection_string = properties.to_connection_string();
814 assert!(connection_string.is_ok());
815 let connection_string = connection_string.unwrap();
816
817 let parsed = EventHubsConnectionStringProperties::parse(&connection_string);
818 assert!(parsed.is_ok());
819 assert_eq!(properties, parsed.unwrap());
820 }
821
822 #[test]
823 fn to_connection_string_allows_shared_access_key_authorization() {
824 let fake_connection = "Endpoint=sb://not-real.servicebus.windows.net/;SharedAccessKeyName=DummyKey;SharedAccessKey=[not_real]";
825 let properties = EventHubsConnectionStringProperties::parse(fake_connection).unwrap();
826
827 assert!(properties.to_connection_string().is_ok());
828 }
829
830 #[test]
831 fn to_connection_string_allows_shared_access_signature_authorization() {
832 let fake_connection =
833 "Endpoint=sb://not-real.servicebus.windows.net/;SharedAccessSignature=[not_real]";
834 let properties = EventHubsConnectionStringProperties::parse(fake_connection).unwrap();
835
836 assert!(properties.to_connection_string().is_ok());
837 }
838}