|
|
@@ -13,10 +13,16 @@ function createGifAssets(dir: string, names: string[]): void {
|
|
|
describe('DivoomManager', () => {
|
|
|
let tempDir: string;
|
|
|
let calls: DivoomSenderCall[];
|
|
|
+ let pythonPath: string;
|
|
|
+ let scriptPath: string;
|
|
|
|
|
|
beforeEach(() => {
|
|
|
tempDir = mkdtempSync(path.join(tmpdir(), 'divoom-test-'));
|
|
|
calls = [];
|
|
|
+ pythonPath = path.join(tempDir, 'python');
|
|
|
+ scriptPath = path.join(tempDir, 'divoom_send.py');
|
|
|
+ writeFileSync(pythonPath, 'python');
|
|
|
+ writeFileSync(scriptPath, 'script');
|
|
|
createGifAssets(tempDir, [
|
|
|
'intro.gif',
|
|
|
'orchestrator.gif',
|
|
|
@@ -34,13 +40,17 @@ describe('DivoomManager', () => {
|
|
|
return new DivoomManager(
|
|
|
{
|
|
|
enabled: config.enabled ?? true,
|
|
|
+ python: pythonPath,
|
|
|
+ script: scriptPath,
|
|
|
+ },
|
|
|
+ (call) => {
|
|
|
+ calls.push(call);
|
|
|
},
|
|
|
- (call) => calls.push(call),
|
|
|
{ assetDir: tempDir },
|
|
|
);
|
|
|
}
|
|
|
|
|
|
- test('does nothing when disabled', () => {
|
|
|
+ test('does nothing when disabled', async () => {
|
|
|
const manager = createManager({ enabled: false });
|
|
|
|
|
|
manager.onPluginLoad();
|
|
|
@@ -50,20 +60,22 @@ describe('DivoomManager', () => {
|
|
|
args: { subagent_type: 'explorer' },
|
|
|
});
|
|
|
manager.onTaskEnd({ parentSessionId: 'parent', callId: 'call-1' });
|
|
|
+ await manager.flush();
|
|
|
|
|
|
expect(calls).toHaveLength(0);
|
|
|
});
|
|
|
|
|
|
- test('shows intro on plugin load when enabled', () => {
|
|
|
+ test('shows intro on plugin load when enabled', async () => {
|
|
|
const manager = createManager();
|
|
|
|
|
|
manager.onPluginLoad();
|
|
|
+ await manager.flush();
|
|
|
|
|
|
expect(calls).toHaveLength(1);
|
|
|
expect(calls[0].args[1]).toBe(path.join(tempDir, 'intro.gif'));
|
|
|
});
|
|
|
|
|
|
- test('shows task agent then orchestrator after a single task', () => {
|
|
|
+ test('shows task agent then orchestrator after a single task', async () => {
|
|
|
const manager = createManager();
|
|
|
|
|
|
manager.onTaskStart({
|
|
|
@@ -71,7 +83,9 @@ describe('DivoomManager', () => {
|
|
|
callId: 'call-1',
|
|
|
args: { subagent_type: 'explorer' },
|
|
|
});
|
|
|
+ await manager.flush();
|
|
|
manager.onTaskEnd({ parentSessionId: 'parent', callId: 'call-1' });
|
|
|
+ await manager.flush();
|
|
|
|
|
|
expect(calls.map((call) => call.args[1])).toEqual([
|
|
|
path.join(tempDir, 'explorer.gif'),
|
|
|
@@ -79,7 +93,7 @@ describe('DivoomManager', () => {
|
|
|
]);
|
|
|
});
|
|
|
|
|
|
- test('shows orchestrator while busy and intro when idle', () => {
|
|
|
+ test('shows orchestrator while busy and intro when idle', async () => {
|
|
|
const manager = createManager();
|
|
|
|
|
|
manager.onOrchestratorStatus({
|
|
|
@@ -87,11 +101,13 @@ describe('DivoomManager', () => {
|
|
|
status: 'busy',
|
|
|
isOrchestrator: true,
|
|
|
});
|
|
|
+ await manager.flush();
|
|
|
manager.onOrchestratorStatus({
|
|
|
sessionId: 'parent',
|
|
|
status: 'idle',
|
|
|
isOrchestrator: true,
|
|
|
});
|
|
|
+ await manager.flush();
|
|
|
|
|
|
expect(calls.map((call) => call.args[1])).toEqual([
|
|
|
path.join(tempDir, 'orchestrator.gif'),
|
|
|
@@ -99,7 +115,7 @@ describe('DivoomManager', () => {
|
|
|
]);
|
|
|
});
|
|
|
|
|
|
- test('does not override active child agent with orchestrator status', () => {
|
|
|
+ test('idle clears active child state and returns to intro', async () => {
|
|
|
const manager = createManager();
|
|
|
|
|
|
manager.onTaskStart({
|
|
|
@@ -107,31 +123,35 @@ describe('DivoomManager', () => {
|
|
|
callId: 'call-1',
|
|
|
args: { subagent_type: 'explorer' },
|
|
|
});
|
|
|
+ await manager.flush();
|
|
|
manager.onOrchestratorStatus({
|
|
|
sessionId: 'parent',
|
|
|
status: 'busy',
|
|
|
isOrchestrator: true,
|
|
|
});
|
|
|
+ await manager.flush();
|
|
|
manager.onOrchestratorStatus({
|
|
|
sessionId: 'parent',
|
|
|
status: 'idle',
|
|
|
isOrchestrator: true,
|
|
|
});
|
|
|
+ await manager.flush();
|
|
|
manager.onTaskEnd({ parentSessionId: 'parent', callId: 'call-1' });
|
|
|
+ await manager.flush();
|
|
|
manager.onOrchestratorStatus({
|
|
|
sessionId: 'parent',
|
|
|
status: 'idle',
|
|
|
isOrchestrator: true,
|
|
|
});
|
|
|
+ await manager.flush();
|
|
|
|
|
|
expect(calls.map((call) => call.args[1])).toEqual([
|
|
|
path.join(tempDir, 'explorer.gif'),
|
|
|
- path.join(tempDir, 'orchestrator.gif'),
|
|
|
path.join(tempDir, 'intro.gif'),
|
|
|
]);
|
|
|
});
|
|
|
|
|
|
- test('keeps first agent visible for parallel tasks', () => {
|
|
|
+ test('keeps first agent visible for parallel tasks', async () => {
|
|
|
const manager = createManager();
|
|
|
|
|
|
manager.onTaskStart({
|
|
|
@@ -139,13 +159,17 @@ describe('DivoomManager', () => {
|
|
|
callId: 'call-1',
|
|
|
args: { subagent_type: 'explorer' },
|
|
|
});
|
|
|
+ await manager.flush();
|
|
|
manager.onTaskStart({
|
|
|
parentSessionId: 'parent',
|
|
|
callId: 'call-2',
|
|
|
args: { subagent_type: 'fixer' },
|
|
|
});
|
|
|
+ await manager.flush();
|
|
|
manager.onTaskEnd({ parentSessionId: 'parent', callId: 'call-1' });
|
|
|
+ await manager.flush();
|
|
|
manager.onTaskEnd({ parentSessionId: 'parent', callId: 'call-2' });
|
|
|
+ await manager.flush();
|
|
|
|
|
|
expect(calls.map((call) => call.args[1])).toEqual([
|
|
|
path.join(tempDir, 'explorer.gif'),
|
|
|
@@ -153,7 +177,7 @@ describe('DivoomManager', () => {
|
|
|
]);
|
|
|
});
|
|
|
|
|
|
- test('falls back to orchestrator gif for unknown agents', () => {
|
|
|
+ test('falls back to orchestrator gif for unknown agents', async () => {
|
|
|
const manager = createManager();
|
|
|
|
|
|
manager.onTaskStart({
|
|
|
@@ -161,20 +185,25 @@ describe('DivoomManager', () => {
|
|
|
callId: 'call-1',
|
|
|
args: { subagent_type: 'custom-agent' },
|
|
|
});
|
|
|
+ await manager.flush();
|
|
|
|
|
|
expect(calls.map((call) => call.args[1])).toEqual([
|
|
|
path.join(tempDir, 'orchestrator.gif'),
|
|
|
]);
|
|
|
});
|
|
|
|
|
|
- test('uses configured sender settings and gif overrides', () => {
|
|
|
+ test('uses configured sender settings and gif overrides', async () => {
|
|
|
const customGif = path.join(tempDir, 'custom-oracle.gif');
|
|
|
+ const customPython = path.join(tempDir, 'custom-python');
|
|
|
+ const customScript = path.join(tempDir, 'custom-divoom-send.py');
|
|
|
writeFileSync(customGif, 'gif');
|
|
|
+ writeFileSync(customPython, 'python');
|
|
|
+ writeFileSync(customScript, 'script');
|
|
|
const manager = new DivoomManager(
|
|
|
{
|
|
|
enabled: true,
|
|
|
- python: '/custom/python',
|
|
|
- script: '/custom/divoom_send.py',
|
|
|
+ python: customPython,
|
|
|
+ script: customScript,
|
|
|
size: 64,
|
|
|
fps: 12,
|
|
|
speed: 250,
|
|
|
@@ -182,7 +211,9 @@ describe('DivoomManager', () => {
|
|
|
posterizeBits: 4,
|
|
|
gifs: { oracle: customGif },
|
|
|
},
|
|
|
- (call) => calls.push(call),
|
|
|
+ (call) => {
|
|
|
+ calls.push(call);
|
|
|
+ },
|
|
|
{ assetDir: tempDir },
|
|
|
);
|
|
|
|
|
|
@@ -191,11 +222,12 @@ describe('DivoomManager', () => {
|
|
|
callId: 'call-1',
|
|
|
args: { subagent_type: 'oracle' },
|
|
|
});
|
|
|
+ await manager.flush();
|
|
|
|
|
|
expect(calls[0]).toEqual({
|
|
|
- command: '/custom/python',
|
|
|
+ command: customPython,
|
|
|
args: [
|
|
|
- '/custom/divoom_send.py',
|
|
|
+ customScript,
|
|
|
customGif,
|
|
|
'--size',
|
|
|
'64',
|
|
|
@@ -210,4 +242,46 @@ describe('DivoomManager', () => {
|
|
|
],
|
|
|
});
|
|
|
});
|
|
|
+
|
|
|
+ test('drops stale queued sends and keeps latest requested gif', async () => {
|
|
|
+ const manager = createManager();
|
|
|
+
|
|
|
+ manager.onPluginLoad();
|
|
|
+ manager.onOrchestratorStatus({
|
|
|
+ sessionId: 'parent',
|
|
|
+ status: 'busy',
|
|
|
+ isOrchestrator: true,
|
|
|
+ });
|
|
|
+ manager.onTaskStart({
|
|
|
+ parentSessionId: 'parent',
|
|
|
+ callId: 'call-1',
|
|
|
+ args: { subagent_type: 'fixer' },
|
|
|
+ });
|
|
|
+ await manager.flush();
|
|
|
+
|
|
|
+ expect(calls.map((call) => call.args[1])).toEqual([
|
|
|
+ path.join(tempDir, 'fixer.gif'),
|
|
|
+ ]);
|
|
|
+ });
|
|
|
+
|
|
|
+ test('missing executable does not poison future attempts', async () => {
|
|
|
+ const manager = new DivoomManager(
|
|
|
+ {
|
|
|
+ enabled: true,
|
|
|
+ python: path.join(tempDir, 'missing-python'),
|
|
|
+ script: scriptPath,
|
|
|
+ },
|
|
|
+ (call) => {
|
|
|
+ calls.push(call);
|
|
|
+ },
|
|
|
+ { assetDir: tempDir },
|
|
|
+ );
|
|
|
+
|
|
|
+ manager.onPluginLoad();
|
|
|
+ await manager.flush();
|
|
|
+ manager.onPluginLoad();
|
|
|
+ await manager.flush();
|
|
|
+
|
|
|
+ expect(calls).toHaveLength(0);
|
|
|
+ });
|
|
|
});
|