bitwarden_exporters/cxf/
note.rs

1use credential_exchange_format::NoteCredential;
2
3/// Extract note content from a CXF Note credential
4/// The way notes are handled (in import.rs) depends on their context:
5/// - If part of an item, use parent type and map content to Cipher::notes
6/// - If standalone, map to SecureNote
7///
8/// That's why we only have this small utility function and tests here.
9pub(super) fn extract_note_content(note: &NoteCredential) -> String {
10    note.content.value.0.clone()
11}
12
13#[cfg(test)]
14mod tests {
15    use super::*;
16
17    #[test]
18    fn test_extract_note_content_with_content() {
19        let note = NoteCredential {
20            content: "This is a test note with important information."
21                .to_owned()
22                .into(),
23        };
24
25        let content = extract_note_content(&note);
26        assert_eq!(
27            content,
28            "This is a test note with important information.".to_string()
29        );
30    }
31
32    #[test]
33    fn test_extract_note_content_empty_string() {
34        let note = NoteCredential {
35            content: "".to_owned().into(),
36        };
37
38        let content = extract_note_content(&note);
39        assert_eq!(content, "".to_string());
40    }
41
42    #[test]
43    fn test_extract_note_content_multiline() {
44        let note = NoteCredential {
45            content: "Line 1\nLine 2\nLine 3".to_owned().into(),
46        };
47
48        let content = extract_note_content(&note);
49        assert_eq!(content, "Line 1\nLine 2\nLine 3".to_string());
50    }
51
52    #[test]
53    fn test_extract_note_content_special_characters() {
54        let note = NoteCredential {
55            content: "Note with emojis 🔐 and special chars: @#$%^&*()"
56                .to_owned()
57                .into(),
58        };
59
60        let content = extract_note_content(&note);
61        assert_eq!(
62            content,
63            "Note with emojis 🔐 and special chars: @#$%^&*()".to_string()
64        );
65    }
66
67    #[test]
68    fn test_extract_note_content_very_long() {
69        let long_content = "A".repeat(10000);
70        let note = NoteCredential {
71            content: long_content.clone().into(),
72        };
73
74        let content = extract_note_content(&note);
75        assert_eq!(content, long_content);
76    }
77
78    #[test]
79    fn test_standalone_note_credential() {
80        use credential_exchange_format::{Credential, Item};
81
82        use crate::{cxf::import::parse_item, CipherType, ImportingCipher};
83
84        let item = Item {
85            id: [0, 1, 2, 3, 4, 5, 6].as_ref().into(),
86            creation_at: Some(1706613834),
87            modified_at: Some(1706623773),
88            title: "My Important Note".to_string(),
89            subtitle: None,
90            favorite: None,
91            credentials: vec![Credential::Note(Box::new(NoteCredential {
92                content:
93                    "This is a standalone secure note with important information.\nLine 2\nLine 3"
94                        .to_string()
95                        .into(),
96            }))],
97            tags: None,
98            extensions: None,
99            scope: None,
100        };
101
102        let ciphers: Vec<ImportingCipher> = parse_item(item);
103        assert_eq!(ciphers.len(), 1);
104        let cipher = ciphers.first().unwrap();
105
106        assert_eq!(cipher.folder_id, None);
107        assert_eq!(cipher.name, "My Important Note");
108        assert_eq!(
109            cipher.notes,
110            Some(
111                "This is a standalone secure note with important information.\nLine 2\nLine 3"
112                    .to_string()
113            )
114        );
115
116        match &cipher.r#type {
117            CipherType::SecureNote(_) => (), // Successfully created a SecureNote
118            _ => panic!("Expected SecureNote"),
119        };
120
121        assert_eq!(cipher.fields.len(), 0); // Notes don't have custom fields
122    }
123}