mirror of
https://github.com/bulwarkmail/native.git
synced 2026-10-31 16:06:32 +00:00
fix: keep the unified views from reading an old account on another port through the live one
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { createFakeJmap } from '../../stores/__tests__/fake-jmap-server';
|
||||
|
||||
// The unified views use the connected client for the live account and stored
|
||||
// credentials for the others. Which account is live followed the old
|
||||
// host-only id rule, so with a client signed in to :8016 an account from the
|
||||
// days before ports (me@10.10.10.62, on :8120) was taken for the live one and
|
||||
// asked the :8016 server for its mail. The live account's id is the one
|
||||
// generateAccountId gives, registry included.
|
||||
|
||||
const server = vi.hoisted(() => ({ current: null as null | ReturnType<typeof import('../../stores/__tests__/fake-jmap-server').createFakeJmap> }));
|
||||
const client = vi.hoisted(() => ({
|
||||
request: null as unknown as ReturnType<typeof import('vitest').vi.fn>,
|
||||
getStoredCredentials: null as unknown as ReturnType<typeof import('vitest').vi.fn>,
|
||||
}));
|
||||
|
||||
vi.mock('../jmap-client', () => {
|
||||
client.request = vi.fn(async (calls: Array<[string, Record<string, unknown>, string]>) => {
|
||||
if (calls[0][0] === 'Mailbox/get') {
|
||||
const accountId = calls[0][1].accountId as string;
|
||||
return { methodResponses: [['Mailbox/get', { accountId, list: [{ id: `${accountId}-in`, role: 'inbox', name: 'Inbox' }] }, calls[0][2]]] };
|
||||
}
|
||||
return server.current!.request(calls as never);
|
||||
});
|
||||
client.getStoredCredentials = vi.fn(async () => null);
|
||||
return {
|
||||
jmapClient: {
|
||||
isConnected: true,
|
||||
accountId: 'c',
|
||||
username: 'me',
|
||||
serverUrl: 'http://10.10.10.62:8016',
|
||||
currentSession: { apiUrl: 'http://10.10.10.62:8016/jmap/', accounts: {} },
|
||||
getSharedMailAccounts: () => [],
|
||||
request: client.request,
|
||||
getStoredCredentials: client.getStoredCredentials,
|
||||
},
|
||||
rewriteSessionUrl: (u: string) => u,
|
||||
extractOrigin: (u: string) => u,
|
||||
parseRetryAfter: () => 0,
|
||||
REQUEST_TIMEOUT_MS: 30000,
|
||||
};
|
||||
});
|
||||
|
||||
import { fetchUnifiedInbox, resetUnifiedCache } from '../unified-inbox';
|
||||
import { setKnownAccounts } from '../../lib/account-utils';
|
||||
|
||||
const OLD_8120 = 'me@10.10.10.62';
|
||||
const LIVE_8016 = 'me@10.10.10.62:8016';
|
||||
|
||||
beforeEach(() => {
|
||||
resetUnifiedCache();
|
||||
client.request.mockClear();
|
||||
client.getStoredCredentials.mockClear();
|
||||
server.current = createFakeJmap({
|
||||
c: [{
|
||||
id: 'a', threadId: 't-a', size: 1, hasAttachment: false, mailboxIds: { 'c-in': true }, keywords: {},
|
||||
receivedAt: '2026-10-20T10:00:00Z', subject: 'On 8016', preview: '',
|
||||
}],
|
||||
});
|
||||
setKnownAccounts([
|
||||
{ id: OLD_8120, username: 'me', serverUrl: 'http://10.10.10.62:8120' },
|
||||
{ id: LIVE_8016, username: 'me', serverUrl: 'http://10.10.10.62:8016' },
|
||||
]);
|
||||
});
|
||||
|
||||
describe('the live account in the unified views', () => {
|
||||
it('is not an older host-only account on another port of the same host', async () => {
|
||||
await fetchUnifiedInbox([OLD_8120], 25, {}).catch(() => undefined);
|
||||
expect(client.request).not.toHaveBeenCalled();
|
||||
expect(client.getStoredCredentials).toHaveBeenCalledWith(OLD_8120);
|
||||
});
|
||||
|
||||
it('is the account with the port the client is signed in to', async () => {
|
||||
const result = await fetchUnifiedInbox([LIVE_8016], 25, {});
|
||||
expect(client.getStoredCredentials).not.toHaveBeenCalled();
|
||||
expect(result.emails.map((e) => e.subject)).toEqual(['On 8016']);
|
||||
});
|
||||
});
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
filterHasSnippetTerms, snippetFilterFor, type SearchSnippetResult, type SnippetBatch,
|
||||
} from '../lib/search-snippet';
|
||||
import { LIST_AUTH_RESULTS_PROPERTY } from '../lib/email-headers';
|
||||
import { generateAccountId } from '../lib/account-utils';
|
||||
|
||||
// Aggregated views across accounts ("All inboxes", "All Sent", All mail /
|
||||
// Unread / Starred). Because the JMAP client is a single-account singleton
|
||||
@@ -208,10 +209,9 @@ function isLiveAccount(accountId: string): boolean {
|
||||
const username = jmapClient.username;
|
||||
const serverUrl = jmapClient.serverUrl;
|
||||
if (!username || !serverUrl) return false;
|
||||
// Same rule as generateAccountId(username, serverUrl) in lib/account-utils.
|
||||
let host = serverUrl;
|
||||
try { host = new URL(serverUrl).hostname; } catch { host = serverUrl.replace(/^https?:\/\//, '').replace(/\/.*$/, ''); }
|
||||
return `${username}@${host}` === accountId;
|
||||
// With the port, and the registry's host-only id where the account kept
|
||||
// one: a client on :8016 is not an old host-only account on :8120.
|
||||
return generateAccountId(username, serverUrl) === accountId;
|
||||
}
|
||||
|
||||
async function entryFor(accountId: string): Promise<AccountEntry> {
|
||||
|
||||
Reference in New Issue
Block a user