feat(core): import_lastpass — SecureNote rows
Rows with url == "http://sn" map to SecureNoteCore with extra copied verbatim into the body. LastPass-packed structured data (credit cards, addresses) flows through unparsed — users can re-categorize manually post-import. SecureNote rows skip the password-required check that applies to Logins. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -155,3 +155,55 @@ fn login_with_lowercase_base32_totp_is_accepted() {
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn url_http_sn_maps_to_secure_note() {
|
||||
let csv = format!(
|
||||
"{HEADER}\n\
|
||||
http://sn,,,,The body of the note,My Note,,",
|
||||
);
|
||||
let (items, warnings) = parse_lastpass_csv(csv.as_bytes()).unwrap();
|
||||
assert!(warnings.is_empty());
|
||||
assert_eq!(items.len(), 1);
|
||||
assert_eq!(items[0].title, "My Note");
|
||||
match &items[0].core {
|
||||
ItemCore::SecureNote(sn) => assert_eq!(sn.body.as_str(), "The body of the note"),
|
||||
other => panic!("expected SecureNote, got {:?}", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secure_note_does_not_require_password() {
|
||||
// SecureNote rows have empty password; that must not trigger the
|
||||
// `missing password` skip path (which is Login-only).
|
||||
let csv = format!("{HEADER}\nhttp://sn,,,,note text,Title,,");
|
||||
let (items, warnings) = parse_lastpass_csv(csv.as_bytes()).unwrap();
|
||||
assert!(warnings.is_empty(), "{:?}", warnings);
|
||||
assert_eq!(items.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secure_note_passes_through_grouping_and_favorite() {
|
||||
let csv = format!("{HEADER}\nhttp://sn,,,,body,Title,Personal,1");
|
||||
let (items, _) = parse_lastpass_csv(csv.as_bytes()).unwrap();
|
||||
assert_eq!(items[0].group.as_deref(), Some("Personal"));
|
||||
assert!(items[0].favorite);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secure_note_preserves_structured_extra_verbatim() {
|
||||
// LastPass packs structured note data (e.g. credit cards) into `extra`
|
||||
// using their own key:value format. We do NOT auto-parse it — verbatim
|
||||
// pass-through, per spec D10.
|
||||
let csv_body = "NoteType:Credit Card\nNumber:4111111111111111\nCVV:123";
|
||||
let csv = format!(
|
||||
"{HEADER}\n\
|
||||
http://sn,,,,\"{csv_body}\",Visa,,",
|
||||
csv_body = csv_body,
|
||||
);
|
||||
let (items, _) = parse_lastpass_csv(csv.as_bytes()).unwrap();
|
||||
match &items[0].core {
|
||||
ItemCore::SecureNote(sn) => assert_eq!(sn.body.as_str(), csv_body),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user