mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-22 00:03:51 +02:00
refactor queryRequest
This commit is contained in:
@@ -2,7 +2,6 @@ import { beforeEach, describe, expect, test, vi } from 'vitest';
|
||||
|
||||
const mockRequest = vi.fn();
|
||||
const mockPatchAndRefetchActiveQuery = vi.fn(() => Promise.resolve());
|
||||
const mockFetchWithEntityPolicy = vi.fn();
|
||||
|
||||
const mockApplyCurrentUser = vi.fn((json) => ({ id: json.id || 'usr_me', ...json }));
|
||||
const mockApplyUser = vi.fn((json) => ({ ...json }));
|
||||
@@ -24,22 +23,12 @@ vi.mock('../../stores', () => ({
|
||||
}));
|
||||
|
||||
vi.mock('../../queries', () => ({
|
||||
entityQueryPolicies: {
|
||||
user: { staleTime: 20000, gcTime: 90000, retry: 1, refetchOnWindowFocus: false },
|
||||
avatar: { staleTime: 60000, gcTime: 300000, retry: 1, refetchOnWindowFocus: false },
|
||||
world: { staleTime: 60000, gcTime: 300000, retry: 1, refetchOnWindowFocus: false },
|
||||
worldCollection: { staleTime: 60000, gcTime: 300000, retry: 1, refetchOnWindowFocus: false },
|
||||
instance: { staleTime: 0, gcTime: 10000, retry: 0, refetchOnWindowFocus: false }
|
||||
},
|
||||
fetchWithEntityPolicy: (...args) => mockFetchWithEntityPolicy(...args),
|
||||
patchAndRefetchActiveQuery: (...args) =>
|
||||
mockPatchAndRefetchActiveQuery(...args),
|
||||
queryKeys: {
|
||||
user: (userId) => ['user', userId],
|
||||
avatar: (avatarId) => ['avatar', avatarId],
|
||||
world: (worldId) => ['world', worldId],
|
||||
worldsByUser: (params) => ['worlds', 'user', params.userId, params],
|
||||
instance: (worldId, instanceId) => ['instance', worldId, instanceId]
|
||||
world: (worldId) => ['world', worldId]
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -88,26 +77,4 @@ describe('entity mutation query sync', () => {
|
||||
);
|
||||
});
|
||||
|
||||
test('getCachedWorlds uses policy wrapper for world list data', async () => {
|
||||
mockFetchWithEntityPolicy.mockResolvedValue({
|
||||
data: {
|
||||
json: [{ id: 'wrld_1' }],
|
||||
params: { userId: 'usr_me', n: 50, offset: 0 }
|
||||
},
|
||||
cache: true
|
||||
});
|
||||
|
||||
const args = await worldRequest.getCachedWorlds({
|
||||
userId: 'usr_me',
|
||||
n: 50,
|
||||
offset: 0,
|
||||
sort: 'updated',
|
||||
order: 'descending',
|
||||
user: 'me',
|
||||
releaseStatus: 'all'
|
||||
});
|
||||
|
||||
expect(mockFetchWithEntityPolicy).toHaveBeenCalled();
|
||||
expect(args.cache).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { beforeEach, describe, expect, test, vi } from 'vitest';
|
||||
|
||||
const mockRequest = vi.fn();
|
||||
const mockFetchWithEntityPolicy = vi.fn();
|
||||
const mockInvalidateQueries = vi.fn().mockResolvedValue();
|
||||
const mockHandleFavoriteAdd = vi.fn();
|
||||
const mockHandleFavoriteDelete = vi.fn();
|
||||
@@ -24,24 +23,8 @@ vi.mock('../../stores', () => ({
|
||||
}));
|
||||
|
||||
vi.mock('../../queries', () => ({
|
||||
entityQueryPolicies: {
|
||||
favoriteCollection: {
|
||||
staleTime: 60000,
|
||||
gcTime: 300000,
|
||||
retry: 1,
|
||||
refetchOnWindowFocus: false
|
||||
}
|
||||
},
|
||||
fetchWithEntityPolicy: (...args) => mockFetchWithEntityPolicy(...args),
|
||||
queryClient: {
|
||||
invalidateQueries: (...args) => mockInvalidateQueries(...args)
|
||||
},
|
||||
queryKeys: {
|
||||
favoriteLimits: () => ['favorite', 'limits'],
|
||||
favorites: (params) => ['favorite', 'items', params],
|
||||
favoriteGroups: (params) => ['favorite', 'groups', params],
|
||||
favoriteWorlds: (params) => ['favorite', 'worlds', params],
|
||||
favoriteAvatars: (params) => ['favorite', 'avatars', params]
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -52,21 +35,6 @@ describe('favorite query sync', () => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
test('cached favorite reads go through fetchWithEntityPolicy', async () => {
|
||||
mockFetchWithEntityPolicy.mockResolvedValue({
|
||||
data: { json: [], params: { n: 300, offset: 0 } },
|
||||
cache: true
|
||||
});
|
||||
|
||||
const args = await favoriteRequest.getCachedFavorites({
|
||||
n: 300,
|
||||
offset: 0
|
||||
});
|
||||
|
||||
expect(mockFetchWithEntityPolicy).toHaveBeenCalled();
|
||||
expect(args.cache).toBe(true);
|
||||
});
|
||||
|
||||
test('favorite mutations invalidate active favorite queries', async () => {
|
||||
mockRequest.mockResolvedValue({ ok: true });
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { beforeEach, describe, expect, test, vi } from 'vitest';
|
||||
|
||||
const mockRequest = vi.fn();
|
||||
const mockFetchWithEntityPolicy = vi.fn();
|
||||
const mockInvalidateQueries = vi.fn().mockResolvedValue();
|
||||
const mockApplyUser = vi.fn((json) => json);
|
||||
|
||||
@@ -16,20 +15,8 @@ vi.mock('../../stores/user', () => ({
|
||||
}));
|
||||
|
||||
vi.mock('../../queries', () => ({
|
||||
entityQueryPolicies: {
|
||||
friendList: {
|
||||
staleTime: 20000,
|
||||
gcTime: 90000,
|
||||
retry: 1,
|
||||
refetchOnWindowFocus: false
|
||||
}
|
||||
},
|
||||
fetchWithEntityPolicy: (...args) => mockFetchWithEntityPolicy(...args),
|
||||
queryClient: {
|
||||
invalidateQueries: (...args) => mockInvalidateQueries(...args)
|
||||
},
|
||||
queryKeys: {
|
||||
friends: (params) => ['friends', params]
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -40,22 +27,6 @@ describe('friend query sync', () => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
test('getCachedFriends uses query policy wrapper', async () => {
|
||||
mockFetchWithEntityPolicy.mockResolvedValue({
|
||||
data: {
|
||||
json: [{ id: 'usr_1', displayName: 'A' }],
|
||||
params: { n: 50, offset: 0 }
|
||||
},
|
||||
cache: true
|
||||
});
|
||||
|
||||
const args = await friendRequest.getCachedFriends({ n: 50, offset: 0 });
|
||||
|
||||
expect(mockFetchWithEntityPolicy).toHaveBeenCalled();
|
||||
expect(args.cache).toBe(true);
|
||||
expect(args.json[0].id).toBe('usr_1');
|
||||
});
|
||||
|
||||
test('friend mutations invalidate active friends queries', async () => {
|
||||
mockRequest.mockResolvedValue({ ok: true });
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { beforeEach, describe, expect, test, vi } from 'vitest';
|
||||
|
||||
const mockRequest = vi.fn();
|
||||
const mockFetchWithEntityPolicy = vi.fn();
|
||||
const mockInvalidateQueries = vi.fn().mockResolvedValue();
|
||||
const mockApplyGroup = vi.fn((json) => json);
|
||||
|
||||
@@ -19,32 +18,8 @@ vi.mock('../../stores', () => ({
|
||||
}));
|
||||
|
||||
vi.mock('../../queries', () => ({
|
||||
entityQueryPolicies: {
|
||||
group: {
|
||||
staleTime: 60000,
|
||||
gcTime: 300000,
|
||||
retry: 1,
|
||||
refetchOnWindowFocus: false
|
||||
},
|
||||
groupCollection: {
|
||||
staleTime: 60000,
|
||||
gcTime: 300000,
|
||||
retry: 1,
|
||||
refetchOnWindowFocus: false
|
||||
}
|
||||
},
|
||||
fetchWithEntityPolicy: (...args) => mockFetchWithEntityPolicy(...args),
|
||||
queryClient: {
|
||||
invalidateQueries: (...args) => mockInvalidateQueries(...args)
|
||||
},
|
||||
queryKeys: {
|
||||
group: (groupId, includeRoles) => ['group', groupId, Boolean(includeRoles)],
|
||||
groupPosts: (params) => ['group', params.groupId, 'posts', params],
|
||||
groupMember: (params) => ['group', params.groupId, 'member', params.userId],
|
||||
groupMembers: (params) => ['group', params.groupId, 'members', params],
|
||||
groupGallery: (params) => ['group', params.groupId, 'gallery', params.galleryId, params],
|
||||
groupCalendar: (groupId) => ['group', groupId, 'calendar'],
|
||||
groupCalendarEvent: (params) => ['group', params.groupId, 'calendarEvent', params.eventId]
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -55,29 +30,6 @@ describe('group query sync', () => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
test('cached group resources use fetchWithEntityPolicy', async () => {
|
||||
mockFetchWithEntityPolicy.mockResolvedValue({
|
||||
data: { json: [], params: { groupId: 'grp_1', n: 100, offset: 0 } },
|
||||
cache: true
|
||||
});
|
||||
|
||||
const a = await groupRequest.getCachedGroupMembers({
|
||||
groupId: 'grp_1',
|
||||
n: 100,
|
||||
offset: 0,
|
||||
sort: 'joinedAt:desc'
|
||||
});
|
||||
const b = await groupRequest.getCachedGroupGallery({
|
||||
groupId: 'grp_1',
|
||||
galleryId: 'gal_1',
|
||||
n: 100,
|
||||
offset: 0
|
||||
});
|
||||
|
||||
expect(mockFetchWithEntityPolicy).toHaveBeenCalledTimes(2);
|
||||
expect(a.cache && b.cache).toBe(true);
|
||||
});
|
||||
|
||||
test('group mutations invalidate scoped active group queries', async () => {
|
||||
mockRequest.mockResolvedValue({ ok: true });
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { beforeEach, describe, expect, test, vi } from 'vitest';
|
||||
|
||||
const mockRequest = vi.fn();
|
||||
const mockFetchWithEntityPolicy = vi.fn();
|
||||
const mockInvalidateQueries = vi.fn().mockResolvedValue();
|
||||
const mockRemoveQueries = vi.fn();
|
||||
|
||||
@@ -16,27 +15,6 @@ vi.mock('../../stores', () => ({
|
||||
}));
|
||||
|
||||
vi.mock('../../queries', () => ({
|
||||
entityQueryPolicies: {
|
||||
galleryCollection: {
|
||||
staleTime: 60000,
|
||||
gcTime: 300000,
|
||||
retry: 1,
|
||||
refetchOnWindowFocus: false
|
||||
},
|
||||
inventoryCollection: {
|
||||
staleTime: 20000,
|
||||
gcTime: 120000,
|
||||
retry: 1,
|
||||
refetchOnWindowFocus: false
|
||||
},
|
||||
fileObject: {
|
||||
staleTime: 60000,
|
||||
gcTime: 300000,
|
||||
retry: 1,
|
||||
refetchOnWindowFocus: false
|
||||
}
|
||||
},
|
||||
fetchWithEntityPolicy: (...args) => mockFetchWithEntityPolicy(...args),
|
||||
queryClient: {
|
||||
invalidateQueries: (...args) => mockInvalidateQueries(...args),
|
||||
removeQueries: (...args) => mockRemoveQueries(...args)
|
||||
@@ -51,7 +29,6 @@ vi.mock('../../queries', () => ({
|
||||
}
|
||||
}));
|
||||
|
||||
import inventoryRequest from '../inventory';
|
||||
import miscRequest from '../misc';
|
||||
import vrcPlusIconRequest from '../vrcPlusIcon';
|
||||
import vrcPlusImageRequest from '../vrcPlusImage';
|
||||
@@ -61,25 +38,6 @@ describe('media and inventory query sync', () => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
test('cached media/inventory reads go through fetchWithEntityPolicy', async () => {
|
||||
mockFetchWithEntityPolicy.mockResolvedValue({
|
||||
data: { json: [], params: {} },
|
||||
cache: true
|
||||
});
|
||||
|
||||
const a = await vrcPlusIconRequest.getCachedFileList({ tag: 'icon', n: 100 });
|
||||
const b = await vrcPlusImageRequest.getCachedPrints({ n: 100 });
|
||||
const c = await inventoryRequest.getCachedInventoryItems({
|
||||
n: 100,
|
||||
offset: 0,
|
||||
order: 'newest'
|
||||
});
|
||||
const d = await miscRequest.getCachedFile({ fileId: 'file_1' });
|
||||
|
||||
expect(mockFetchWithEntityPolicy).toHaveBeenCalledTimes(4);
|
||||
expect(a.cache && b.cache && c.cache && d.cache).toBe(true);
|
||||
});
|
||||
|
||||
test('media mutations invalidate gallery queries and file delete removes file query', async () => {
|
||||
mockRequest.mockResolvedValue({ ok: true });
|
||||
|
||||
|
||||
174
src/api/__tests__/queryRequest.test.js
Normal file
174
src/api/__tests__/queryRequest.test.js
Normal file
@@ -0,0 +1,174 @@
|
||||
import { beforeEach, describe, expect, test, vi } from 'vitest';
|
||||
|
||||
const mockFetchWithEntityPolicy = vi.fn();
|
||||
const mockGetUser = vi.fn();
|
||||
const mockGetWorlds = vi.fn();
|
||||
const mockGetGroupCalendar = vi.fn();
|
||||
|
||||
vi.mock('../../queries', () => ({
|
||||
entityQueryPolicies: {
|
||||
user: { staleTime: 20000, gcTime: 90000, retry: 1, refetchOnWindowFocus: false },
|
||||
worldCollection: { staleTime: 60000, gcTime: 300000, retry: 1, refetchOnWindowFocus: false },
|
||||
groupCollection: { staleTime: 60000, gcTime: 300000, retry: 1, refetchOnWindowFocus: false },
|
||||
avatar: { staleTime: 60000, gcTime: 300000, retry: 1, refetchOnWindowFocus: false },
|
||||
world: { staleTime: 60000, gcTime: 300000, retry: 1, refetchOnWindowFocus: false },
|
||||
group: { staleTime: 60000, gcTime: 300000, retry: 1, refetchOnWindowFocus: false },
|
||||
friendList: { staleTime: 20000, gcTime: 90000, retry: 1, refetchOnWindowFocus: false },
|
||||
favoriteCollection: { staleTime: 60000, gcTime: 300000, retry: 1, refetchOnWindowFocus: false },
|
||||
galleryCollection: { staleTime: 60000, gcTime: 300000, retry: 1, refetchOnWindowFocus: false },
|
||||
inventoryCollection: { staleTime: 20000, gcTime: 120000, retry: 1, refetchOnWindowFocus: false },
|
||||
fileObject: { staleTime: 60000, gcTime: 300000, retry: 1, refetchOnWindowFocus: false }
|
||||
},
|
||||
fetchWithEntityPolicy: (...args) => mockFetchWithEntityPolicy(...args),
|
||||
queryKeys: {
|
||||
user: (userId) => ['user', userId],
|
||||
worldsByUser: (params) => ['worlds', 'user', params.userId, params],
|
||||
groupCalendar: (groupId) => ['group', groupId, 'calendar'],
|
||||
avatar: (avatarId) => ['avatar', avatarId],
|
||||
world: (worldId) => ['world', worldId],
|
||||
group: (groupId, includeRoles) => ['group', groupId, Boolean(includeRoles)],
|
||||
groupPosts: (params) => ['group', params.groupId, 'posts', params],
|
||||
groupMember: (params) => ['group', params.groupId, 'member', params.userId],
|
||||
groupMembers: (params) => ['group', params.groupId, 'members', params],
|
||||
groupGallery: (params) => ['group', params.groupId, 'gallery', params.galleryId, params],
|
||||
groupCalendarEvent: (params) => ['group', params.groupId, 'calendarEvent', params.eventId],
|
||||
friends: (params) => ['friends', params],
|
||||
favoriteLimits: () => ['favorite', 'limits'],
|
||||
favorites: (params) => ['favorite', 'items', params],
|
||||
favoriteGroups: (params) => ['favorite', 'groups', params],
|
||||
favoriteWorlds: (params) => ['favorite', 'worlds', params],
|
||||
favoriteAvatars: (params) => ['favorite', 'avatars', params],
|
||||
galleryFiles: (params) => ['gallery', 'files', params],
|
||||
prints: (params) => ['gallery', 'prints', params],
|
||||
print: (printId) => ['gallery', 'print', printId],
|
||||
userInventoryItem: (params) => ['inventory', 'item', params.userId, params.inventoryId],
|
||||
inventoryItems: (params) => ['inventory', 'items', params],
|
||||
file: (fileId) => ['file', fileId]
|
||||
}
|
||||
}));
|
||||
|
||||
vi.mock('../user', () => ({
|
||||
default: {
|
||||
getUser: (...args) => mockGetUser(...args)
|
||||
}
|
||||
}));
|
||||
|
||||
vi.mock('../world', () => ({
|
||||
default: {
|
||||
getWorlds: (...args) => mockGetWorlds(...args),
|
||||
getWorld: vi.fn()
|
||||
}
|
||||
}));
|
||||
|
||||
vi.mock('../group', () => ({
|
||||
default: {
|
||||
getGroupCalendar: (...args) => mockGetGroupCalendar(...args),
|
||||
getGroup: vi.fn(),
|
||||
getGroupPosts: vi.fn(),
|
||||
getGroupMember: vi.fn(),
|
||||
getGroupMembers: vi.fn(),
|
||||
getGroupGallery: vi.fn(),
|
||||
getGroupCalendarEvent: vi.fn()
|
||||
}
|
||||
}));
|
||||
|
||||
vi.mock('../avatar', () => ({ default: { getAvatar: vi.fn() } }));
|
||||
vi.mock('../friend', () => ({ default: { getFriends: vi.fn() } }));
|
||||
vi.mock('../favorite', () => ({
|
||||
default: {
|
||||
getFavoriteLimits: vi.fn(),
|
||||
getFavorites: vi.fn(),
|
||||
getFavoriteGroups: vi.fn(),
|
||||
getFavoriteWorlds: vi.fn(),
|
||||
getFavoriteAvatars: vi.fn()
|
||||
}
|
||||
}));
|
||||
vi.mock('../vrcPlusIcon', () => ({ default: { getFileList: vi.fn() } }));
|
||||
vi.mock('../vrcPlusImage', () => ({
|
||||
default: { getPrints: vi.fn(), getPrint: vi.fn() }
|
||||
}));
|
||||
vi.mock('../inventory', () => ({
|
||||
default: { getUserInventoryItem: vi.fn(), getInventoryItems: vi.fn() }
|
||||
}));
|
||||
vi.mock('../misc', () => ({ default: { getFile: vi.fn() } }));
|
||||
|
||||
import queryRequest from '../queryRequest';
|
||||
|
||||
describe('queryRequest', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
test('routes user fetch through policy wrapper and returns cache marker', async () => {
|
||||
const data = { json: { id: 'usr_1' }, params: { userId: 'usr_1' } };
|
||||
mockGetUser.mockResolvedValue(data);
|
||||
mockFetchWithEntityPolicy.mockImplementation(async ({ queryFn }) => ({
|
||||
data: await queryFn(),
|
||||
cache: true
|
||||
}));
|
||||
|
||||
const args = await queryRequest.fetch('user', { userId: 'usr_1' });
|
||||
|
||||
expect(mockFetchWithEntityPolicy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
queryKey: ['user', 'usr_1']
|
||||
})
|
||||
);
|
||||
expect(args.cache).toBe(true);
|
||||
expect(args.json.id).toBe('usr_1');
|
||||
});
|
||||
|
||||
test('supports worldsByUser option routing', async () => {
|
||||
const params = {
|
||||
userId: 'usr_me',
|
||||
n: 50,
|
||||
offset: 0,
|
||||
sort: 'updated',
|
||||
order: 'descending',
|
||||
user: 'me',
|
||||
releaseStatus: 'all',
|
||||
option: 'featured'
|
||||
};
|
||||
mockGetWorlds.mockResolvedValue({ json: [], params });
|
||||
mockFetchWithEntityPolicy.mockImplementation(async ({ queryFn }) => ({
|
||||
data: await queryFn(),
|
||||
cache: false
|
||||
}));
|
||||
|
||||
await queryRequest.fetch('worldsByUser', params);
|
||||
|
||||
expect(mockGetWorlds).toHaveBeenCalledWith(params, 'featured');
|
||||
expect(mockFetchWithEntityPolicy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
queryKey: ['worlds', 'user', 'usr_me', params]
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test('supports groupCalendar resource shape', async () => {
|
||||
mockGetGroupCalendar.mockResolvedValue({
|
||||
json: { results: [] },
|
||||
params: { groupId: 'grp_1' }
|
||||
});
|
||||
mockFetchWithEntityPolicy.mockImplementation(async ({ queryFn }) => ({
|
||||
data: await queryFn(),
|
||||
cache: false
|
||||
}));
|
||||
|
||||
await queryRequest.fetch('groupCalendar', { groupId: 'grp_1' });
|
||||
|
||||
expect(mockGetGroupCalendar).toHaveBeenCalledWith('grp_1');
|
||||
expect(mockFetchWithEntityPolicy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
queryKey: ['group', 'grp_1', 'calendar']
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test('throws on unknown resource', async () => {
|
||||
await expect(
|
||||
// @ts-expect-error verifying runtime guard
|
||||
queryRequest.fetch('missing_resource', {})
|
||||
).rejects.toThrow('Unknown query resource');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user