mirror of
https://github.com/bulwarkmail/native.git
synced 2026-10-31 08:06:36 +00:00
fix: add no extra account named after its id when Android refuses one #34
Turning device sync on retried with the app account's registry id as the Android account name whenever adding the account failed. That fallback is for one case only: the login name turned out to belong to another app account's Android account, which the native module reports with the `conflict` code. A passing AccountManager failure or a permission error instead created a second account named after the opaque id, which then showed in the Contacts and Calendar apps. Only a `conflict` rejection takes the fallback now; any other failure fails the turn-on as before.
This commit is contained in:
@@ -43,7 +43,8 @@ vi.mock('../../native', () => ({
|
||||
ensureAndroidAccount: vi.fn(async (name: string, registryId: string) => {
|
||||
h.state.log.push(`ensure ${name}`);
|
||||
const existing = h.find(name);
|
||||
if (existing && existing.registryId !== registryId) throw new Error('conflict');
|
||||
// As the native module rejects (DeviceSyncAccounts.ensure → AccountConflictException).
|
||||
if (existing && existing.registryId !== registryId) throw Object.assign(new Error(`${name} belongs to another app account`), { code: 'conflict' });
|
||||
if (existing) return false;
|
||||
h.state.accounts.push({ name, registryId, sync: {} });
|
||||
return true;
|
||||
@@ -212,6 +213,18 @@ describe('enable', () => {
|
||||
expect(outcome).toEqual({ kind: 'enabled', accountName: ALICE });
|
||||
});
|
||||
|
||||
it('adds no second account named after the registry id when adding the account fails for another reason', async () => {
|
||||
for (const code of ['failed', 'permission']) {
|
||||
vi.mocked(native.ensureAndroidAccount).mockClear();
|
||||
vi.mocked(native.ensureAndroidAccount).mockRejectedValueOnce(Object.assign(new Error(`AccountManager said no (${code})`), { code }));
|
||||
const outcome = await enableDeviceSync(ALICE, CONTACTS_AUTHORITY);
|
||||
expect(outcome).toEqual({ kind: 'failed', message: `AccountManager said no (${code})` });
|
||||
expect(native.ensureAndroidAccount).toHaveBeenCalledTimes(1);
|
||||
expect(h.state.accounts).toEqual([]);
|
||||
expect(accountDeviceSync(ALICE).androidAccountName).toBeUndefined();
|
||||
}
|
||||
});
|
||||
|
||||
it('leaves no account behind when turning sync on fails', async () => {
|
||||
h.state.failSetSyncEnabled = true;
|
||||
const outcome = await enableDeviceSync(ALICE, CONTACTS_AUTHORITY);
|
||||
|
||||
@@ -122,8 +122,9 @@ async function ensureAccountFor(registryId: string): Promise<string> {
|
||||
await ensureAndroidAccount(name, registryId);
|
||||
return name;
|
||||
} catch (err) {
|
||||
// The name belongs to another app account after all (the list was stale).
|
||||
if (name === registryId) throw err;
|
||||
// The name belongs to another app account after all (the list was stale). Any
|
||||
// other failure fails the turn-on: a second account would show in the apps.
|
||||
if (name === registryId || (err as { code?: unknown } | null)?.code !== 'conflict') throw err;
|
||||
await ensureAndroidAccount(registryId, registryId);
|
||||
return registryId;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user