SnowLuma v2.4.2 source snapshot
Dev Build / cleanup (push) Waiting to run
Dev Build / typecheck (push) Waiting to run
Publish MCP to npm / Build, test, and publish MCP (push) Waiting to run
Publish SDK to npm / Build, test, and publish SDK (push) Waiting to run
Sync action catalog to docs / sync (push) Waiting to run
Publish UI to npm / Build and publish UI (push) Waiting to run
Build and Release / preflight (push) Waiting to run
Dev Build / cleanup (push) Waiting to run
Dev Build / typecheck (push) Waiting to run
Publish MCP to npm / Build, test, and publish MCP (push) Waiting to run
Publish SDK to npm / Build, test, and publish SDK (push) Waiting to run
Sync action catalog to docs / sync (push) Waiting to run
Publish UI to npm / Build and publish UI (push) Waiting to run
Build and Release / preflight (push) Waiting to run
This commit is contained in:
commit
fa9c73363d
1367 files changed
+355735
No files matched your search
@@ -0,0 +1,119 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { createContactList } from '../src/lib/debug-contacts';
|
||||
import type { DebugInvokeResult } from '../src/types';
|
||||
|
||||
function deferred() {
|
||||
let resolve!: (result: DebugInvokeResult) => void;
|
||||
const promise = new Promise<DebugInvokeResult>((done) => {
|
||||
resolve = done;
|
||||
});
|
||||
return { promise, resolve };
|
||||
}
|
||||
|
||||
const success = (nickname: string): DebugInvokeResult => ({
|
||||
status: 'ok',
|
||||
data: [{ user_id: 123, nickname }],
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
vi.stubGlobal('localStorage', { getItem: () => null, setItem: vi.fn() });
|
||||
});
|
||||
afterEach(() => vi.unstubAllGlobals());
|
||||
|
||||
describe('debug contact lists', () => {
|
||||
it('keeps account state separate and ignores a response after leaving its list', async () => {
|
||||
const previous = deferred();
|
||||
const invoke = vi
|
||||
.fn()
|
||||
.mockReturnValueOnce(previous.promise)
|
||||
.mockResolvedValue(success('current'));
|
||||
const oldList = createContactList(invoke, 'friends', 'old-account');
|
||||
const pending = oldList.refresh();
|
||||
oldList.invalidate();
|
||||
const current = createContactList(invoke, 'friends', 'new-account');
|
||||
await current.refresh();
|
||||
previous.resolve(success('stale'));
|
||||
await pending;
|
||||
expect(current.getSnapshot().items[0].label).toBe('current');
|
||||
expect(oldList.getSnapshot().items).toEqual([]);
|
||||
expect(createContactList(invoke, 'friends', 'old-account').getSnapshot().items).toEqual([]);
|
||||
});
|
||||
|
||||
it('supersedes an in-flight refresh when using a fresh cached list', async () => {
|
||||
const pending = deferred();
|
||||
const invoke = vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce(success('cached'))
|
||||
.mockReturnValueOnce(pending.promise);
|
||||
const list = createContactList(invoke, 'friends', 'cache-account');
|
||||
await list.refresh();
|
||||
const refresh = list.refresh();
|
||||
await list.refresh(false);
|
||||
expect(list.getSnapshot().loading).toBe(false);
|
||||
pending.resolve(success('superseded'));
|
||||
await refresh;
|
||||
expect(list.getSnapshot().items[0].label).toBe('cached');
|
||||
});
|
||||
|
||||
it('ignores an earlier refresh that completes after its replacement', async () => {
|
||||
const first = deferred();
|
||||
const invoke = vi
|
||||
.fn()
|
||||
.mockReturnValueOnce(first.promise)
|
||||
.mockResolvedValueOnce(success('latest'));
|
||||
const list = createContactList(invoke, 'friends', 'race-account');
|
||||
const pending = list.refresh();
|
||||
await list.refresh();
|
||||
first.resolve(success('old'));
|
||||
await pending;
|
||||
expect(list.getSnapshot().items[0].label).toBe('latest');
|
||||
});
|
||||
|
||||
it('keeps group-member caches scoped to the account and group', async () => {
|
||||
const invoke = vi.fn().mockResolvedValue(success('member'));
|
||||
const first = createContactList(invoke, 'members', 'member-account', '111');
|
||||
await first.refresh();
|
||||
expect(invoke).toHaveBeenCalledWith('member-account', 'get_group_member_list', {
|
||||
group_id: '111',
|
||||
});
|
||||
expect(
|
||||
createContactList(invoke, 'members', 'member-account', '111').getSnapshot().items,
|
||||
).toHaveLength(1);
|
||||
expect(
|
||||
createContactList(invoke, 'members', 'member-account', '222').getSnapshot().items,
|
||||
).toEqual([]);
|
||||
expect(
|
||||
createContactList(invoke, 'members', 'other-account', '111').getSnapshot().items,
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
it('reports malformed successful responses instead of caching an empty list', async () => {
|
||||
const invoke = vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce(success('retained'))
|
||||
.mockResolvedValueOnce({ status: 'ok', data: {} });
|
||||
const list = createContactList(invoke, 'friends', 'invalid-account');
|
||||
await list.refresh();
|
||||
await list.refresh();
|
||||
expect(list.getSnapshot()).toMatchObject({ loading: false, error: '联系人列表格式无效' });
|
||||
expect(list.getSnapshot().items[0].label).toBe('retained');
|
||||
await list.refresh(false);
|
||||
expect(list.getSnapshot().error).toBeNull();
|
||||
expect(invoke).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('does not request contacts without an account or group selection', async () => {
|
||||
const invoke = vi.fn();
|
||||
const lists = [
|
||||
createContactList(invoke, 'friends', ''),
|
||||
createContactList(invoke, 'members', 'account'),
|
||||
];
|
||||
|
||||
for (const list of lists) {
|
||||
await list.refresh();
|
||||
expect(list.getSnapshot()).toEqual({ items: [], loading: false, error: null });
|
||||
}
|
||||
|
||||
expect(invoke).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user