mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-15 16:42:15 +00:00
Keep guest drawer discovery reads non-suspending
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
import { describe, it, expect, vi, afterEach, beforeEach } from 'vitest';
|
||||
import { render, screen, fireEvent, cleanup, waitFor, within } from '@solidjs/testing-library';
|
||||
import { Suspense } from 'solid-js';
|
||||
import type { WorkloadGuest } from '@/types/workloads';
|
||||
import type { Memory, Disk, GuestNetworkInterface } from '@/types/api';
|
||||
import { resetCreateNonSuspendingQueryCacheForTest } from '@/hooks/createNonSuspendingQuery';
|
||||
import { getCanonicalWorkloadId } from '@/utils/workloads';
|
||||
|
||||
// ── Mocks ──────────────────────────────────────────────────────────────
|
||||
@@ -118,6 +120,8 @@ const makeHistoryPoints = (base: number) => [
|
||||
];
|
||||
|
||||
beforeEach(() => {
|
||||
resetCreateNonSuspendingQueryCacheForTest();
|
||||
discoveryApiMocks.getDiscovery.mockResolvedValue(null);
|
||||
chartsApiMocks.getMetricsHistory.mockResolvedValue({
|
||||
resourceType: 'vm',
|
||||
resourceId: 'inst1:node1:100',
|
||||
@@ -216,13 +220,30 @@ describe('GuestDrawer', () => {
|
||||
it('hides the Identified Service card when the discovery record is null or empty', async () => {
|
||||
discoveryApiMocks.getDiscovery.mockResolvedValueOnce(null);
|
||||
render(() => <GuestDrawer guest={makeGuest()} onClose={vi.fn()} />);
|
||||
// Allow the resource to resolve and a render cycle to happen.
|
||||
await waitFor(() => {
|
||||
expect(discoveryApiMocks.getDiscovery).toHaveBeenCalled();
|
||||
});
|
||||
expect(screen.queryByText('Identified Service')).toBeNull();
|
||||
});
|
||||
|
||||
it('keeps the passive discovery lookup out of the parent Suspense fallback', () => {
|
||||
discoveryApiMocks.getDiscovery.mockImplementationOnce(
|
||||
() =>
|
||||
new Promise<import('@/types/discovery').ResourceDiscovery | null>(() => undefined),
|
||||
);
|
||||
|
||||
render(() => (
|
||||
<Suspense fallback={<div data-testid="parent-suspense-fallback">Loading page</div>}>
|
||||
<GuestDrawer guest={makeGuest()} onClose={vi.fn()} />
|
||||
</Suspense>
|
||||
));
|
||||
|
||||
expect(screen.queryByTestId('parent-suspense-fallback')).toBeNull();
|
||||
expect(screen.getByText('Overview')).toBeInTheDocument();
|
||||
expect(screen.getByText('History')).toBeInTheDocument();
|
||||
expect(screen.getByText('Discovery')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('switches to Discovery tab on click', async () => {
|
||||
const { container } = render(() => <GuestDrawer guest={makeGuest()} onClose={vi.fn()} />);
|
||||
await fireEvent.click(screen.getByText('Discovery'));
|
||||
|
||||
+3
@@ -1266,6 +1266,9 @@ describe('Workloads performance contract', () => {
|
||||
expect(guestDrawerStateSource).toContain('historyRange');
|
||||
expect(guestDrawerStateSource).not.toContain('buildInfrastructureHrefForWorkload');
|
||||
expect(guestDrawerStateSource).not.toContain("from '@/routing/resourceLinks'");
|
||||
expect(guestDrawerStateSource).toContain("from '@/hooks/createNonSuspendingQuery'");
|
||||
expect(guestDrawerStateSource).toContain('createNonSuspendingQuery<ResourceDiscovery | null');
|
||||
expect(guestDrawerStateSource).not.toContain('createResource');
|
||||
expect(guestDrawerStateSource).toContain("from './workloadTopology'");
|
||||
expect(guestDrawerStateSource).not.toContain('./infrastructureLink');
|
||||
expect(guestDrawerStateSource).toContain('getDiscoveryResourceTypeForWorkload');
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { createEffect, createMemo, createResource, createSignal } from 'solid-js';
|
||||
import { createEffect, createMemo, createSignal } from 'solid-js';
|
||||
|
||||
import { getDiscovery } from '@/api/discovery';
|
||||
import type { HistoryTimeRange } from '@/api/charts';
|
||||
import { createNonSuspendingQuery } from '@/hooks/createNonSuspendingQuery';
|
||||
import { useAlertsActivation } from '@/stores/alertsActivation';
|
||||
import type { ResourceDiscovery, ResourceType as DiscoveryResourceType } from '@/types/discovery';
|
||||
import {
|
||||
getDiscoveryIdentifiedSummary,
|
||||
getDiscoveryLoadingState,
|
||||
@@ -36,6 +38,12 @@ import {
|
||||
type GuestDrawerTab,
|
||||
} from './guestDrawerModel';
|
||||
|
||||
interface GuestDiscoverySourceKey {
|
||||
type: DiscoveryResourceType;
|
||||
agent: string;
|
||||
resource: string;
|
||||
}
|
||||
|
||||
export function useGuestDrawerState(props: GuestDrawerProps) {
|
||||
const alertsActivation = useAlertsActivation();
|
||||
const [activeTab, setActiveTab] = createSignal<GuestDrawerTab>('overview');
|
||||
@@ -97,18 +105,22 @@ export function useGuestDrawerState(props: GuestDrawerProps) {
|
||||
const agent = discoveryAgentId();
|
||||
const resource = discoveryResourceId();
|
||||
if (!type || !agent || !resource) return null;
|
||||
return { type, agent, resource } as const;
|
||||
return { type, agent, resource } as GuestDiscoverySourceKey;
|
||||
});
|
||||
const [discoveryRecord] = createResource(discoverySourceKey, async (key) => {
|
||||
if (!key) return null;
|
||||
try {
|
||||
return await getDiscovery(key.type, key.agent, key.resource);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
const discoveryRecord = createNonSuspendingQuery<ResourceDiscovery | null, GuestDiscoverySourceKey>({
|
||||
source: discoverySourceKey,
|
||||
initialValue: null,
|
||||
cacheKey: (key) => `guest-drawer-discovery:${key.type}:${key.agent}:${key.resource}`,
|
||||
fetcher: async (key) => {
|
||||
try {
|
||||
return await getDiscovery(key.type, key.agent, key.resource);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
});
|
||||
const discoveryIdentifiedSummary = createMemo(() =>
|
||||
getDiscoveryIdentifiedSummary(discoveryRecord()),
|
||||
getDiscoveryIdentifiedSummary(discoveryRecord.value()),
|
||||
);
|
||||
|
||||
const switchTab = (tab: GuestDrawerTab) => {
|
||||
|
||||
Reference in New Issue
Block a user