mirror of
https://github.com/bulwarkmail/native.git
synced 2026-11-02 08:07:11 +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,
|
filterHasSnippetTerms, snippetFilterFor, type SearchSnippetResult, type SnippetBatch,
|
||||||
} from '../lib/search-snippet';
|
} from '../lib/search-snippet';
|
||||||
import { LIST_AUTH_RESULTS_PROPERTY } from '../lib/email-headers';
|
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 /
|
// Aggregated views across accounts ("All inboxes", "All Sent", All mail /
|
||||||
// Unread / Starred). Because the JMAP client is a single-account singleton
|
// 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 username = jmapClient.username;
|
||||||
const serverUrl = jmapClient.serverUrl;
|
const serverUrl = jmapClient.serverUrl;
|
||||||
if (!username || !serverUrl) return false;
|
if (!username || !serverUrl) return false;
|
||||||
// Same rule as generateAccountId(username, serverUrl) in lib/account-utils.
|
// With the port, and the registry's host-only id where the account kept
|
||||||
let host = serverUrl;
|
// one: a client on :8016 is not an old host-only account on :8120.
|
||||||
try { host = new URL(serverUrl).hostname; } catch { host = serverUrl.replace(/^https?:\/\//, '').replace(/\/.*$/, ''); }
|
return generateAccountId(username, serverUrl) === accountId;
|
||||||
return `${username}@${host}` === accountId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function entryFor(accountId: string): Promise<AccountEntry> {
|
async function entryFor(accountId: string): Promise<AccountEntry> {
|
||||||
|
|||||||
Reference in New Issue
Block a user