diff --git a/src/main/browser/browser-session-persisted-profile-validation.ts b/src/main/browser/browser-session-persisted-profile-validation.ts index f2845d28379..18a1ff62fb1 100644 --- a/src/main/browser/browser-session-persisted-profile-validation.ts +++ b/src/main/browser/browser-session-persisted-profile-validation.ts @@ -30,17 +30,20 @@ export function inspectRetiredBrowserSessionProfileUserAgentModes( let noticePending = false let degraded = false for (const profile of profiles) { - if (!isValidPersistedBrowserSessionProfile(profile, activeOrcaProfileId)) { - noticePending = true - degraded = true - continue - } - if (!Object.hasOwn(profile, 'userAgentMode')) { + // Refusing to hydrate an entry is not the same as finding a retired choice: hydrateFromPersisted + // already skips it silently, and a notice here would claim an old choice could not be inspected + // for a profile that never carried one. + if (!profile || typeof profile !== 'object' || !Object.hasOwn(profile, 'userAgentMode')) { continue } noticePending = true const mode = Reflect.get(profile, 'userAgentMode') - if (mode !== 'clean' && mode !== 'native') { + // Degraded covers both ways the choice is uninspectable: an unreadable mode, and a mode sitting + // on an entry we refuse to hydrate, where we cannot say which profile it belonged to. + if ( + (mode !== 'clean' && mode !== 'native') || + !isValidPersistedBrowserSessionProfile(profile, activeOrcaProfileId) + ) { degraded = true } } diff --git a/src/main/browser/browser-session-user-agent-migration-inspection.test.ts b/src/main/browser/browser-session-user-agent-migration-inspection.test.ts index 499b341e265..73fc76c446e 100644 --- a/src/main/browser/browser-session-user-agent-migration-inspection.test.ts +++ b/src/main/browser/browser-session-user-agent-migration-inspection.test.ts @@ -3,38 +3,73 @@ import { getOrcaProfileBrowserSessionPartition } from '../../shared/orca-profile import { inspectRetiredBrowserSessionProfileUserAgentModes } from './browser-session-persisted-profile-validation' const ORCA_PROFILE_ID = 'local-default' +const PROFILE_ID = '11111111-1111-4111-8111-111111111111' function profileWithMode(mode: unknown): Record { - const id = '11111111-1111-4111-8111-111111111111' return { - id, + id: PROFILE_ID, scope: 'isolated', - partition: getOrcaProfileBrowserSessionPartition(ORCA_PROFILE_ID, id), + partition: getOrcaProfileBrowserSessionPartition(ORCA_PROFILE_ID, PROFILE_ID), label: 'Existing', source: null, userAgentMode: mode } } +/** Fails `isValidPersistedBrowserSessionProfile` on its id, for reasons unrelated to identity. */ +function unhydratableProfile(extra: Record = {}): Record { + return { + id: 'not-a-uuid', + scope: 'isolated', + partition: 'persist:orca-browser-session-not-a-uuid', + label: 'Unhydratable', + source: null, + ...extra + } +} + describe('retired browser profile identity inspection', () => { it('detects an inspectable old choice without removing its bytes', () => { const profile = profileWithMode('native') - expect( - inspectRetiredBrowserSessionProfileUserAgentModes([profile], ORCA_PROFILE_ID) - ).toEqual({ noticePending: true, degraded: false }) + expect(inspectRetiredBrowserSessionProfileUserAgentModes([profile], ORCA_PROFILE_ID)).toEqual({ + noticePending: true, + degraded: false + }) expect(profile.userAgentMode).toBe('native') }) - it.each([[null], [42], ['broken'], [profileWithMode('unexpected')]])( - 'turns malformed metadata into a degraded notice without throwing', - (entry) => { - expect(() => - inspectRetiredBrowserSessionProfileUserAgentModes([entry], ORCA_PROFILE_ID) - ).not.toThrow() - expect( - inspectRetiredBrowserSessionProfileUserAgentModes([entry], ORCA_PROFILE_ID) - ).toEqual({ noticePending: true, degraded: true }) + // "I refuse to hydrate this" is not "a retired identity choice was found". hydrateFromPersisted + // skips these entries silently, and the notice text claims an old choice could not be inspected — + // which would be a lie about a profile that never carried one, repeated on every launch. + it.each([ + { scenario: 'null', entry: null }, + { scenario: 'a number', entry: 42 }, + { scenario: 'a string', entry: 'broken' }, + { + scenario: 'a profile that fails validation for an unrelated reason', + entry: unhydratableProfile() } - ) + ])('stays silent about $scenario, which carries no identity choice', ({ entry }) => { + expect(inspectRetiredBrowserSessionProfileUserAgentModes([entry], ORCA_PROFILE_ID)).toEqual({ + noticePending: false, + degraded: false + }) + }) + + it.each([ + { scenario: 'an unreadable mode', entry: profileWithMode('unexpected') }, + { + scenario: 'a mode on an entry that cannot be hydrated', + entry: unhydratableProfile({ userAgentMode: 'native' }) + } + ])('turns $scenario into a degraded notice without throwing', ({ entry }) => { + expect(() => + inspectRetiredBrowserSessionProfileUserAgentModes([entry], ORCA_PROFILE_ID) + ).not.toThrow() + expect(inspectRetiredBrowserSessionProfileUserAgentModes([entry], ORCA_PROFILE_ID)).toEqual({ + noticePending: true, + degraded: true + }) + }) })