package com.peachpasswords.ui.vault import com.peachpasswords.vault.VaultEntry import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertNull import org.junit.Assert.assertTrue import org.junit.Test class ImportParsersTest { @Test fun detect_blank_is_null() { assertNull(CsvImport.detectProvider("")) assertNull(CsvImport.detectProvider(" \n ")) } @Test fun chrome_csv_detects_and_parses() { val csv = "name,url,username,password\nGitHub,https://github.com,june,hunter2\n" assertEquals(CsvImport.CHROME_CSV, CsvImport.detectProvider(csv)) val rows = CsvImport.parse(csv, CsvImport.CHROME_CSV) assertEquals(1, rows.size) assertEquals("GitHub", rows[0].title) assertEquals("https://github.com", rows[0].url) assertEquals("june", rows[0].username) assertEquals("hunter2", rows[0].password) } @Test fun csv_quoted_fields_with_commas_are_preserved() { val rows = CsvImport.parseCsv("name,url,username,password\n\"Bank, NA\",https://b.com,j,\"p,w\"\n") // Header + 1 data row. assertEquals(2, rows.size) assertEquals(listOf("Bank, NA", "https://b.com", "j", "p,w"), rows[1]) } @Test fun csv_doubled_quotes_unescape() { assertEquals(listOf("a\"b", "c"), CsvImport.splitCsvLine("\"a\"\"b\",c")) } @Test fun detect_and_parse_returns_success_for_chrome() { val csv = "name,url,username,password\nA,https://a.com,u,p\nB,https://b.com,u2,p2\n" val r = CsvImport.detectAndParse(csv) assertTrue(r is ImportParseResult.Success) r as ImportParseResult.Success assertEquals(2, r.entries.size) } @Test fun proton_pass_json_detects_and_parses_login_items() { val json = """ { "vaults": { "main": { "name": "Personal", "items": [ { "data": { "type": "login", "metadata": { "name": "Proton Site", "note": "saved note" }, "content": { "username": "june", "password": "hunter2", "urls": ["https://example.com"], "totpUri": "otpauth://totp/Example:june?secret=JBSWY3DPEHPK3PXP" } } } ] }, "trash": { "name": "Recycle Bin", "items": [ { "data": { "type": "login", "metadata": { "name": "Deleted" }, "content": { "username": "x", "password": "y" } } } ] } } } """.trimIndent() assertEquals(CsvImport.PROTONPASS_JSON, CsvImport.detectProvider(json)) val rows = CsvImport.parse(json, CsvImport.PROTONPASS_JSON) assertEquals(1, rows.size) assertEquals("Proton Site", rows[0].title) assertEquals("june", rows[0].username) assertEquals("hunter2", rows[0].password) assertEquals("https://example.com", rows[0].url) assertEquals("saved note", rows[0].notes) assertEquals("otpauth://totp/Example:june?secret=JBSWY3DPEHPK3PXP", rows[0].totp) } @Test fun peachscript_export_is_recognised() { val out = PeachScriptExport.buildEncrypted( listOf( VaultEntry( id = "11111111-2222-3333-4444-555555555555", type = "login", title = "GH", username = "u", password = "p", createdAt = 0, modifiedAt = 0, ), ), "backup-pass-123", ) assertTrue(PeachScriptImport.looksLikePeachScript(out)) assertTrue(PeachScriptImport.isEncrypted(out)) } @Test fun non_peachscript_text_is_rejected() { assertFalse(PeachScriptImport.looksLikePeachScript("just text")) // The old JSON marker is not PeachScript anymore (PUA-encoded binary only). assertFalse(PeachScriptImport.looksLikePeachScript("""{"peachscript":1,"encrypted":true}""")) } @Test fun file_picker_routes_peachscript_files_to_peachscript_restore() { val out = PeachScriptExport.buildEncrypted( listOf( VaultEntry( id = "11111111-2222-3333-4444-555555555555", type = "login", title = "GH", username = "u", password = "p", createdAt = 0, modifiedAt = 0, ), ), "backup-pass-123", ) assertTrue(shouldRouteFileToPeachScript("backup.txt", out)) assertTrue(shouldRouteFileToPeachScript("backup.peach", "not yet decoded")) assertFalse(shouldRouteFileToPeachScript("export.csv", "name,url,username,password\nA,https://a.com,u,p\n")) } @Test fun import_errors_match_browser_recovery_copy() { assertEquals( "Wrong backup password. This is the password you chose when you created the backup.", ImportErrors.WRONG_PASSWORD, ) assertEquals( "This Peach backup file looks corrupted or incomplete.", ImportErrors.CORRUPTED, ) assertEquals( "No importable password entries were found in that file.", ImportErrors.NO_ENTRIES, ) } }