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

This commit is contained in:
TanTan committed 2026-11-13 09:02:00 +08:00
commit fa9c73363d
1367 files changed
+355735

No files matched your search

+123
View File
@@ -0,0 +1,123 @@
import { describe, expect, it, vi } from 'vitest';
import { createApiClient } from '../src/lib/api/client';
import { createAppResources } from '../src/lib/app-resources';
import type { QQInfo } from '../src/types';
function setup() {
const api = createApiClient({ tokenStore: { load: () => null, save: () => undefined } });
vi.spyOn(api, 'qqList').mockResolvedValue([]);
vi.spyOn(api.processes, 'list').mockResolvedValue([]);
vi.spyOn(api, 'connections').mockResolvedValue([]);
return { api, store: createAppResources(api) };
}
const accounts: QQInfo[] = [{ uin: '12345', nickname: '当前账号' }];
describe('app resources', () => {
it('publishes data and load status together, distinguishing an empty result from an initial load', async () => {
const { store } = setup();
const initial = store.getSnapshot();
expect(initial.qqList).toEqual({ data: [], ready: false, error: null });
const listener = vi.fn(() => {
expect(store.getSnapshot().qqList).toEqual({ data: [], ready: true, error: null });
});
const unsubscribe = store.subscribe(listener);
await store.refreshQqList();
expect(listener).toHaveBeenCalledOnce();
expect(initial.qqList.ready).toBe(false);
unsubscribe();
await store.refreshQqList();
expect(listener).toHaveBeenCalledOnce();
});
it('keeps the latest request when an older request completes later', async () => {
const { api, store } = setup();
const old = Promise.withResolvers<QQInfo[]>();
vi.mocked(api.qqList).mockReturnValueOnce(old.promise).mockResolvedValueOnce(accounts);
const first = store.refreshQqList();
await store.refreshQqList();
old.resolve([]);
await first;
expect(store.getSnapshot().qqList.data).toEqual(accounts);
});
it.each(['success', 'failure'])(
'keeps a pushed snapshot after a stale REST %s',
async (outcome) => {
const { api, store } = setup();
const request = Promise.withResolvers<QQInfo[]>();
vi.mocked(api.qqList).mockReturnValueOnce(request.promise);
const pending = store.refreshQqList();
store.onEvent({ resource: 'qq-list', data: accounts });
const snapshot = store.getSnapshot();
if (outcome === 'success') {
request.resolve([]);
} else {
request.reject(new Error('过期请求失败'));
}
await pending;
expect(store.getSnapshot()).toBe(snapshot);
expect(snapshot.qqList).toEqual({ data: accounts, ready: true, error: null });
},
);
it('does not invalidate a different resource when receiving a push', async () => {
const { api, store } = setup();
vi.mocked(api.qqList).mockResolvedValueOnce(accounts);
const pending = store.refreshQqList();
store.onEvent({ resource: 'connections', data: [] });
await pending;
expect(store.getSnapshot().qqList.data).toEqual(accounts);
expect(store.getSnapshot().connections.ready).toBe(true);
});
it('retains the last data on failure and clears the error on recovery', async () => {
const { api, store } = setup();
store.onEvent({ resource: 'qq-list', data: accounts });
vi.mocked(api.qqList).mockRejectedValueOnce(new Error('连接中断'));
await store.refreshQqList();
expect(store.getSnapshot().qqList).toEqual({
data: accounts,
ready: true,
error: '连接中断',
});
await store.refreshQqList();
expect(store.getSnapshot().qqList).toEqual({ data: [], ready: true, error: null });
});
it('invalidates pending work on cleanup and allows a new subscription cycle', async () => {
const { api, store } = setup();
const request = Promise.withResolvers<QQInfo[]>();
vi.mocked(api.qqList).mockReturnValueOnce(request.promise);
const pending = store.refreshQqList();
const initial = store.getSnapshot();
store.invalidate();
request.resolve(accounts);
await pending;
expect(store.getSnapshot()).toBe(initial);
await store.refreshQqList();
expect(store.getSnapshot().qqList.ready).toBe(true);
});
it('reconciles all stream resources when frames are dropped', async () => {
const { api, store } = setup();
store.onEvent({ kind: 'ready' });
expect(api.qqList).not.toHaveBeenCalled();
store.onEvent({ kind: 'dropped', count: 3 });
await vi.waitFor(() => expect(store.getSnapshot().connections.ready).toBe(true));
expect(api.qqList).toHaveBeenCalledOnce();
expect(api.processes.list).toHaveBeenCalledOnce();
expect(api.connections).toHaveBeenCalledOnce();
});
it('reports background update failures but rejects an explicit forced check', async () => {
const { api, store } = setup();
vi.spyOn(api.update, 'check').mockRejectedValue(new Error('检查失败'));
await expect(store.refreshUpdate()).resolves.toBeNull();
expect(store.getSnapshot().updateInfo.error).toBe('检查失败');
await expect(store.refreshUpdate(true)).rejects.toThrow('检查失败');
expect(api.update.check).toHaveBeenLastCalledWith(true);
});
});