Skip to content

Commit e82e88d

Browse files
committed
test: add tab-lock-manager tests and panel webhook coverage
1 parent 4b50e1e commit e82e88d

File tree

2 files changed

+122
-0
lines changed

2 files changed

+122
-0
lines changed
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { describe, it, expect, vi, beforeEach } from 'vitest';
2+
import { TabLockManager } from '../tab-lock-manager';
3+
4+
describe('TabLockManager', () => {
5+
let lm: TabLockManager;
6+
7+
beforeEach(() => {
8+
lm = new TabLockManager();
9+
});
10+
11+
describe('acquire()', () => {
12+
it('user always acquires, even when locked by agent', () => {
13+
lm.acquire('tab-1', 'claude');
14+
const result = lm.acquire('tab-1', 'user');
15+
expect(result.acquired).toBe(true);
16+
expect(lm.getOwner('tab-1')).toBe('user');
17+
});
18+
19+
it('emits lock-overridden when user overrides agent lock', () => {
20+
const handler = vi.fn();
21+
lm.on('lock-overridden', handler);
22+
23+
lm.acquire('tab-1', 'claude');
24+
lm.acquire('tab-1', 'user');
25+
26+
expect(handler).toHaveBeenCalledWith({
27+
tabId: 'tab-1',
28+
previousOwner: 'claude',
29+
newOwner: 'user',
30+
});
31+
});
32+
33+
it('does not emit lock-overridden when user acquires unlocked tab', () => {
34+
const handler = vi.fn();
35+
lm.on('lock-overridden', handler);
36+
37+
lm.acquire('tab-1', 'user');
38+
expect(handler).not.toHaveBeenCalled();
39+
});
40+
41+
it('agent cannot acquire tab locked by another agent', () => {
42+
lm.acquire('tab-1', 'claude');
43+
const result = lm.acquire('tab-1', 'other-agent');
44+
expect(result.acquired).toBe(false);
45+
expect(result.owner).toBe('claude');
46+
});
47+
48+
it('agent can renew own lock', () => {
49+
lm.acquire('tab-1', 'claude');
50+
const result = lm.acquire('tab-1', 'claude');
51+
expect(result.acquired).toBe(true);
52+
});
53+
});
54+
55+
describe('release()', () => {
56+
it('user can release any lock', () => {
57+
lm.acquire('tab-1', 'claude');
58+
expect(lm.release('tab-1', 'user')).toBe(true);
59+
expect(lm.isLocked('tab-1')).toBe(false);
60+
});
61+
62+
it('agent cannot release another agent lock', () => {
63+
lm.acquire('tab-1', 'claude');
64+
expect(lm.release('tab-1', 'other-agent')).toBe(false);
65+
expect(lm.isLocked('tab-1')).toBe(true);
66+
});
67+
68+
it('returns true for unlocked tab', () => {
69+
expect(lm.release('tab-1', 'claude')).toBe(true);
70+
});
71+
});
72+
});

src/panel/tests/manager.test.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,56 @@ describe('PanelManager reply notifications', () => {
8989
expect(lastTyping[1]).toEqual({ typing: false });
9090
});
9191

92+
it('does not fire webhook for wingman messages', async () => {
93+
const win = createWindowStub();
94+
const manager = new PanelManager(win as never);
95+
96+
const mockConfigManager = {
97+
getConfig: vi.fn().mockReturnValue({
98+
webhook: { enabled: true, url: 'http://localhost:9999', notifyOnRobinChat: true },
99+
}),
100+
};
101+
(manager as any).configManager = mockConfigManager;
102+
103+
const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue({ ok: true } as Response);
104+
105+
manager.addChatMessage('wingman', 'AI response');
106+
107+
// Give the async fireWebhook a tick to run
108+
await new Promise(r => setTimeout(r, 10));
109+
110+
expect(fetchSpy).not.toHaveBeenCalled();
111+
fetchSpy.mockRestore();
112+
});
113+
114+
it('fires webhook for user messages when configured', async () => {
115+
const win = createWindowStub();
116+
const manager = new PanelManager(win as never);
117+
118+
const mockConfigManager = {
119+
getConfig: vi.fn().mockReturnValue({
120+
webhook: { enabled: true, url: 'http://localhost:9999', notifyOnRobinChat: true },
121+
}),
122+
};
123+
(manager as any).configManager = mockConfigManager;
124+
125+
const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue({ ok: true } as Response);
126+
127+
manager.addChatMessage('user', 'Hello from user');
128+
129+
await new Promise(r => setTimeout(r, 10));
130+
131+
expect(fetchSpy).toHaveBeenCalledTimes(1);
132+
expect(fetchSpy).toHaveBeenCalledWith(
133+
'http://localhost:9999/hooks/wake',
134+
expect.objectContaining({
135+
method: 'POST',
136+
body: expect.stringContaining('Hello from user'),
137+
}),
138+
);
139+
fetchSpy.mockRestore();
140+
});
141+
92142
it('falls back to an image message when there is no text', () => {
93143
const win = createWindowStub();
94144
const manager = new PanelManager(win as never);

0 commit comments

Comments
 (0)