Skip to content

Commit 550d09e

Browse files
committed
chore: add one more test case
1 parent bc3d095 commit 550d09e

File tree

2 files changed

+39
-27
lines changed

2 files changed

+39
-27
lines changed

site/src/modules/resources/useAgentLogs.test.ts

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ function generateMockLogs(
1717
): readonly WorkspaceAgentLog[] {
1818
return Array.from({ length: logCount }, (_, i) => {
1919
// Make sure that the logs generated each have unique timestamps, so
20-
// that we can test whether they're being sorted properly before being
21-
// returned by the hook
20+
// that we can test whether the hook is sorting them properly as it's
21+
// receiving them over time
2222
const logDate = new Date(baseDate.getTime() + i * millisecondsInOneMinute);
2323
return {
2424
id: i,
@@ -32,25 +32,22 @@ function generateMockLogs(
3232

3333
// A mutable object holding the most recent mock WebSocket publisher. The inner
3434
// value will change as the hook opens/closes new connections
35-
type PublisherResult = {
36-
current: MockWebSocketPublisher;
37-
};
35+
type PublisherResult = { current: MockWebSocketPublisher; };
3836

3937
type MountHookResult = Readonly<{
40-
// Note: the value of `current` should be readonly, but the `current`
41-
// property itself should be mutable
42-
hookResult: {
43-
current: readonly WorkspaceAgentLog[];
44-
};
45-
rerender: (props: { enabled: boolean }) => void;
38+
rerender: (props: { agentId: string, enabled: boolean }) => void;
4639
publisherResult: PublisherResult;
40+
41+
// Note: the `current` property is only "halfway" readonly; the value is
42+
// readonly, but the key is still mutable
43+
hookResult: { current: readonly WorkspaceAgentLog[]; };
4744
}>;
4845

49-
function mountHook(): MountHookResult {
46+
function mountHook(initialAgentId: string): MountHookResult {
5047
// Have to cheat the types a little bit to avoid a chicken-and-the-egg
5148
// scenario. publisherResult will be initialized with an undefined current
52-
// value, but it'll be guaranteed not to be undefined by the time this
53-
// function returns.
49+
// value, but it'll be guaranteed not to be undefined by the time mountHook
50+
// returns its value.
5451
const publisherResult: Partial<PublisherResult> = { current: undefined };
5552
const useAgentLogs = createUseAgentLogs((agentId, params) => {
5653
return new OneWayWebSocket({
@@ -68,8 +65,8 @@ function mountHook(): MountHookResult {
6865
});
6966

7067
const { result, rerender } = renderHook(
71-
({ enabled }) => useAgentLogs(MockWorkspaceAgent, enabled),
72-
{ initialProps: { enabled: true } },
68+
({ agentId, enabled }) => useAgentLogs(agentId, enabled),
69+
{ initialProps: { agentId: initialAgentId, enabled: true }, },
7370
);
7471

7572
return {
@@ -81,10 +78,11 @@ function mountHook(): MountHookResult {
8178

8279
describe("useAgentLogs", () => {
8380
it("Automatically sorts logs that are received out of order", async () => {
84-
const { hookResult, publisherResult } = mountHook();
81+
const { hookResult, publisherResult } = mountHook(MockWorkspaceAgent.id);
8582
const logs = generateMockLogs(10, new Date("september 9, 1999"));
83+
const reversed = logs.toReversed();
8684

87-
for (const log of logs.toReversed()) {
85+
for (const log of reversed) {
8886
act(() => {
8987
publisherResult.current.publishMessage(
9088
new MessageEvent<string>("message", {
@@ -97,16 +95,30 @@ describe("useAgentLogs", () => {
9795
});
9896

9997
it("Automatically closes the socket connection when the hook is disabled", async () => {
100-
const { publisherResult, rerender } = mountHook();
98+
const { publisherResult, rerender } = mountHook(MockWorkspaceAgent.id);
10199
expect(publisherResult.current.isConnectionOpen()).toBe(true);
102-
rerender({ enabled: false });
100+
rerender({ agentId: MockWorkspaceAgent.id, enabled: false });
103101
await waitFor(() => {
104102
expect(publisherResult.current.isConnectionOpen()).toBe(false);
105103
})
106104
});
107105

106+
it("Automatically closes the old connection when the agent ID changes", () => {
107+
const { publisherResult, rerender } = mountHook(MockWorkspaceAgent.id);
108+
const publisher1 = publisherResult.current;
109+
expect(publisher1.isConnectionOpen()).toBe(true);
110+
111+
const newAgentId = `${MockWorkspaceAgent.id}-2`;
112+
rerender({ agentId: newAgentId, enabled: true });
113+
114+
const publisher2 = publisherResult.current;
115+
expect(publisher1.isConnectionOpen()).toBe(false);
116+
expect(publisher2.isConnectionOpen()).toBe(true);
117+
});
118+
108119
it("Clears logs when hook becomes disabled (protection to avoid duplicate logs when hook goes back to being re-enabled)", async () => {
109-
const { hookResult, publisherResult, rerender } = mountHook();
120+
const { hookResult, publisherResult, rerender }
121+
= mountHook(MockWorkspaceAgent.id);
110122

111123
// Send initial logs so that we have something to clear out later
112124
const initialLogs = generateMockLogs(3, new Date("april 5, 1997"));
@@ -118,12 +130,12 @@ describe("useAgentLogs", () => {
118130

119131
// Disable the hook (and have the hook close the connection behind the
120132
// scenes)
121-
rerender({ enabled: false });
133+
rerender({ agentId: MockWorkspaceAgent.id, enabled: false });
122134
await waitFor(() => expect(hookResult.current).toHaveLength(0));
123135

124136
// Re-enable the hook (creating an entirely new connection), and send
125137
// new logs
126-
rerender({ enabled: true });
138+
rerender({ agentId: MockWorkspaceAgent.id, enabled: true });
127139
const newLogs = generateMockLogs(3, new Date("october 3, 2005"));
128140
const newEvent = new MessageEvent<string>("message", {
129141
data: JSON.stringify(newLogs),

site/src/modules/resources/useAgentLogs.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { watchWorkspaceAgentLogs, type WatchWorkspaceAgentLogsParams } from "api/api";
2-
import type { WorkspaceAgent, WorkspaceAgentLog } from "api/typesGenerated";
2+
import type { WorkspaceAgentLog } from "api/typesGenerated";
33
import { displayError } from "components/GlobalSnackbar/utils";
44
import { useEffect, useState } from "react";
55
import type { OneWayWebSocket } from "utils/OneWayWebSocket";
@@ -11,7 +11,7 @@ export type CreateSocket = (
1111

1212
export function createUseAgentLogs(createSocket: CreateSocket) {
1313
return function useAgentLogs(
14-
agent: WorkspaceAgent,
14+
agentId: string,
1515
enabled: boolean,
1616
): readonly WorkspaceAgentLog[] {
1717
const [logs, setLogs] = useState<readonly WorkspaceAgentLog[]>([]);
@@ -35,7 +35,7 @@ export function createUseAgentLogs(createSocket: CreateSocket) {
3535
// this in the future, but it would add some complexity in the code
3636
// that might not be worth it.
3737
const createdAtMap = new Map<string, number>();
38-
const socket = createSocket(agent.id, { after: 0 });
38+
const socket = createSocket(agentId, { after: 0 });
3939
socket.addEventListener("message", (e) => {
4040
if (e.parseError) {
4141
console.warn("Error parsing agent log: ", e.parseError);
@@ -76,7 +76,7 @@ export function createUseAgentLogs(createSocket: CreateSocket) {
7676
});
7777

7878
return () => socket.close();
79-
}, [createSocket, agent.id, enabled]);
79+
}, [createSocket, agentId, enabled]);
8080

8181
return logs;
8282
};

0 commit comments

Comments
 (0)