OtpDatasetTest.kt

3.6 KB · raw

1package com.peachpasswords.autofill23import com.peachpasswords.autofill.AutofillResponseBuilder.FillMode4import com.peachpasswords.vault.VaultEntry5import org.junit.Assert.assertEquals6import org.junit.Assert.assertFalse7import org.junit.Assert.assertTrue8import org.junit.Test910/**11 * E2-fix verification: the 2FA chip and the LOGIN chip are SEPARATE datasets12 * so the user can verify the TOTP code before it lands. [datasetFor] no longer13 * fills [parsed.otpId] when identity/password is present; [otpDatasetFor] is14 * the dedicated 2FA path.15 *16 * Real-Dataset construction needs Android instrumentation (AutofillId is17 * package-private). What we can verify in JVM tests is the label path: the18 * LOGIN chip renders the entry title, the OTP chip renders the chunked code.19 */20class OtpDatasetTest {2122    private fun entry(23        title: String = "GitHub",24        username: String? = "june",25        totp: String? = "JBSWY3DPEHPK3PXP",26    ) = VaultEntry(27        id = title, title = title, username = username, totp = totp,28        createdAt = 0, modifiedAt = 0,29    )3031    @Test fun otp_chip_shows_chunked_code_when_login_fields_also_present() {32        // Multi-field form: identity + password + otp. The LOGIN chip carries33        // the entry title; the OTP chip carries the chunked code -- two34        // separate affordances, two separate labels.35        val loginLabels = AutofillResponseBuilder.previewLabels(36            privatePreview = false,37            entry = entry(),38            originDomain = "github.com",39            fillMode = FillMode.LOGIN,40        )41        val otpLabels = AutofillResponseBuilder.previewLabels(42            privatePreview = false,43            entry = entry(),44            originDomain = "github.com",45            fillMode = FillMode.OTP_FILL,46            otpCode = "482901",47        )48        assertEquals("GitHub", loginLabels.title)49        assertEquals("482 901", otpLabels.title)50        assertEquals("2FA", otpLabels.subtitle)51        assertFalse(52            "LOGIN and OTP chip titles must differ so the user can tell them apart",53            loginLabels.title == otpLabels.title,54        )55        assertTrue(loginLabels.fillMode == FillMode.LOGIN)56        assertTrue(otpLabels.fillMode == FillMode.OTP_FILL)57    }5859    @Test fun otp_chip_regenerates_each_call() {60        // The TOTP code rotates every 30s. The chip is rebuilt on every61        // FillRequest, so a fresh code passed in surfaces as the title.62        val first = AutofillResponseBuilder.previewLabels(63            privatePreview = false, entry = entry(), originDomain = "github.com",64            fillMode = FillMode.OTP_FILL, otpCode = "111111",65        )66        val second = AutofillResponseBuilder.previewLabels(67            privatePreview = false, entry = entry(), originDomain = "github.com",68            fillMode = FillMode.OTP_FILL, otpCode = "222222",69        )70        assertEquals("111 111", first.title)71        assertEquals("222 222", second.title)72    }7374    @Test fun otp_chip_in_private_mode_still_shows_peach_not_the_entry() {75        // Hide-autofill-previews hides the title too. The 2FA chip stays76        // generic ("Peach") with no entry title -- the digits would still77        // leak if we kept them. (Digits ARE shown via the menu RemoteViews78        // not the inline chip when privatePreview is on.)79        val labels = AutofillResponseBuilder.previewLabels(80            privatePreview = true, entry = entry(), originDomain = "github.com",81            fillMode = FillMode.OTP_FILL, otpCode = "482901",82        )83        assertEquals("Peach", labels.title)84        assertEquals("github.com", labels.subtitle)85    }86}