File size: 3,529 Bytes
63522a5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | /* Integration: useCompactContextAction end-to-end through real stores. */
import { act, renderHook } from "@testing-library/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import React from "react";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import useMetricsStore from "#/stores/metrics-store";
import { useEventStore } from "#/stores/use-event-store";
import type { OpenHandsEvent } from "#/types/agent-server/core";
const mutateMock = vi.fn();
vi.mock("#/hooks/mutation/use-condense-conversation", () => ({
useCondenseConversation: () => ({ mutate: mutateMock, isPending: false }),
}));
vi.mock("#/hooks/query/use-active-conversation", () => ({
useActiveConversation: () => ({
data: { id: "c1", conversation_url: null, session_api_key: null },
}),
}));
vi.mock("#/hooks/use-agent-state", () => ({
useAgentState: () => ({ curAgentState: "FINISHED" }),
}));
const successToast = vi.fn();
const errorToast = vi.fn();
vi.mock("#/utils/custom-toast-handlers", () => ({
displaySuccessToast: (...args: unknown[]) => successToast(...args),
displayErrorToast: (...args: unknown[]) => errorToast(...args),
}));
import { useCompactContextAction } from "#/hooks/use-compact-context-action";
import { I18nKey } from "#/i18n/declaration";
function condensationEvent(id: string): OpenHandsEvent {
return {
id,
timestamp: new Date().toISOString(),
source: "environment",
kind: "Condensation",
forgotten_event_ids: ["a"],
} as OpenHandsEvent;
}
const usage = (perTurn: number) => ({
prompt_tokens: perTurn,
completion_tokens: 0,
cache_read_tokens: 0,
cache_write_tokens: 0,
context_window: 262144,
per_turn_token: perTurn,
});
function wrapper({ children }: { children: React.ReactNode }) {
return React.createElement(
QueryClientProvider,
{ client: new QueryClient() },
children,
);
}
describe("useCompactContextAction (integration)", () => {
beforeEach(() => {
vi.useFakeTimers();
useEventStore.getState().clearEvents();
useMetricsStore.getState().setMetrics({
cost: null,
max_budget_per_task: null,
usage: usage(50_000),
});
mutateMock.mockImplementation((_vars, opts) => opts.onSuccess());
successToast.mockClear();
errorToast.mockClear();
});
afterEach(() => {
vi.useRealTimers();
useEventStore.getState().clearEvents();
});
it("toasts counts when condensation lands and metrics drop", () => {
const { result } = renderHook(() => useCompactContextAction(50_000), {
wrapper,
});
act(() => result.current.handleCompact());
expect(successToast).toHaveBeenCalledWith(
I18nKey.CONVERSATION$COMPACT_CONTEXT_STARTED,
);
act(() => {
useEventStore.getState().addEvent(condensationEvent("cond-x"));
useMetricsStore.getState().setMetrics({
cost: null,
max_budget_per_task: null,
usage: usage(20_000),
});
});
expect(successToast).toHaveBeenCalledWith(
I18nKey.CONVERSATION$COMPACT_CONTEXT_COMPLETE,
);
expect(errorToast).not.toHaveBeenCalled();
});
it("toasts error when no condensation arrives in time", () => {
const { result } = renderHook(() => useCompactContextAction(50_000), {
wrapper,
});
act(() => result.current.handleCompact());
act(() => {
vi.advanceTimersByTime(90_000);
});
expect(errorToast).toHaveBeenCalledWith(
I18nKey.CONVERSATION$COMPACT_CONTEXT_FAILED,
);
});
});
|