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,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);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user