Skip to content

Commit ea207a9

Browse files
authored
Merge pull request #2 from hubot-friends/server-commands
feat: Servers can have scripts and handle messages itself
2 parents 9e1cfd1 + 3a4c761 commit ea207a9

3 files changed

Lines changed: 126 additions & 31 deletions

File tree

DiscoveryService.mjs

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -499,21 +499,13 @@ export class DiscoveryService {
499499
})
500500

501501
this.robot.receiveMiddleware(async context => {
502-
if (!Array.from([/help/
503-
, /test\s+routing/
504-
, /discover\s+services/i
505-
, /discovery\s+status/i
506-
, /(?:load.?balancer|lb)\s+reset/
507-
, /(?:load.?balancer|lb)\s+strategy\s+(\w+)/
508-
, /(?:load.?balancer|lb)\s+status/i
509-
]).some(matcher => {
510-
return matcher.test(context.response.message.text)
502+
if (!this.robot.listeners.some(listener => {
503+
return listener instanceof TextListener && listener.test(context.response.message)
511504
})) {
512505
this.robot.logger.debug('Routing message')
513506
const result = await this.routeMessage(context.response.message)
514507
return false
515508
}
516-
517509
return true
518510
})
519511

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import { test, describe, beforeEach, afterEach, mock } from 'node:test'
2+
import assert from 'node:assert'
3+
import { EventEmitter } from 'events'
4+
import { existsSync, rmSync, mkdirSync } from 'fs'
5+
import { join } from 'path'
6+
import DiscoveryServiceScript from '../DiscoveryService.mjs'
7+
import { TextMessage, TextListener, Robot, Adapter, Middleware} from 'hubot'
8+
9+
10+
class MockAdapter extends Adapter {
11+
constructor (robot) {
12+
super(robot)
13+
this.name = 'MockAdapter'
14+
}
15+
16+
async send (envelope, ...strings) {
17+
this.emit('send', envelope, ...strings)
18+
}
19+
20+
async reply (envelope, ...strings) {
21+
this.emit('reply', envelope, ...strings)
22+
}
23+
24+
async topic (envelope, ...strings) {
25+
this.emit('topic', envelope, ...strings)
26+
}
27+
28+
async play (envelope, ...strings) {
29+
this.emit('play', envelope, ...strings)
30+
}
31+
32+
run () {
33+
// This is required to get the scripts loaded
34+
this.emit('connected')
35+
}
36+
37+
close () {
38+
this.emit('closed')
39+
}
40+
}
41+
42+
describe('Incoming Message Handling as Server', () => {
43+
let robot
44+
let testDir
45+
let originalEnv
46+
47+
beforeEach(async () => {
48+
// Save original environment
49+
originalEnv = { ...process.env }
50+
51+
// Setup test environment with more unique directory naming
52+
const timestamp = Date.now()
53+
const random = Math.floor(Math.random() * 10000)
54+
testDir = join(process.cwd(), 'test-data', `message-test-${timestamp}-${random}`)
55+
mkdirSync(testDir, { recursive: true })
56+
57+
process.env.HUBOT_DISCOVERY_STORAGE = testDir
58+
process.env.HUBOT_DISCOVERY_PORT = '0' // Use random port
59+
process.env.HUBOT_INSTANCE_ID = 'test-instance'
60+
process.env.HUBOT_HOST = 'localhost'
61+
process.env.HUBOT_PORT = '8080'
62+
process.env.NODE_ENV = 'test'
63+
64+
robot = new Robot({
65+
async use(robot) {
66+
return new MockAdapter(robot)
67+
}
68+
}, false, 'MockityMcMockFace')
69+
await robot.loadAdapter()
70+
await robot.run()
71+
})
72+
73+
afterEach(async () => {
74+
// Restore environment
75+
process.env = originalEnv
76+
77+
// Cleanup service discovery
78+
if (robot.discoveryService) {
79+
await robot.discoveryService.stop()
80+
}
81+
82+
// Cleanup test directory
83+
if (existsSync(testDir)) {
84+
rmSync(testDir, { recursive: true, force: true })
85+
}
86+
87+
robot.shutdown()
88+
})
89+
90+
test('Server can handle messages and not route them to the workers', async () => {
91+
await DiscoveryServiceScript(robot)
92+
let wasRouted = false
93+
robot.discoveryService.routeMessage = async (message) => {
94+
wasRouted = true
95+
}
96+
robot.respond(/handled by server/, async res => {
97+
await res.reply('This message was handled by the server')
98+
assert.ok(!wasRouted)
99+
})
100+
const testMessage = new TextMessage({ user: { id: 'U123', name: 'tester', room: 'general' } }, '@MockityMcMockFace handled by server', Date.now())
101+
await robot.receive(testMessage)
102+
assert.ok(!wasRouted)
103+
})
104+
})

test/DiscoveryServiceScript.test.mjs

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { EventEmitter } from 'events'
44
import { existsSync, rmSync, mkdirSync } from 'fs'
55
import { join } from 'path'
66
import DiscoveryServiceScript from '../DiscoveryService.mjs'
7+
import { TextListener } from 'hubot'
78

89
// Mock robot for testing
910
class MockRobot extends EventEmitter {
@@ -23,21 +24,19 @@ class MockRobot extends EventEmitter {
2324
connectToPeer: mock.fn(),
2425
getPeerCount: mock.fn(() => 0)
2526
}
26-
this.commands = []
27+
this.listeners = []
2728
}
2829

2930
parseHelp(fileName) {
3031
// Mock help parsing
3132
}
3233

33-
respond(pattern, callback) {
34+
respond(regex, options, callback) {
3435
// Mock respond method for command registration
35-
this.commands.push({ pattern, callback })
36+
this.hear(regex, options, callback)
3637
}
37-
38-
hear(pattern, callback) {
39-
// Mock hear method for command registration
40-
this.commands.push({ pattern, callback })
38+
hear(regex, options, callback) {
39+
this.listeners.push(new TextListener(this, regex, options, callback))
4140
}
4241
}
4342

@@ -109,18 +108,18 @@ describe('DiscoveryService Script', () => {
109108
await DiscoveryServiceScript(robot)
110109

111110
// Check that commands were registered
112-
assert(robot.commands.length > 0, 'Should have registered some commands')
113-
114-
// Check for specific command patterns based on current design
115-
const patterns = robot.commands.map(cmd => cmd.pattern.toString())
116-
assert(patterns.some(p => p.includes('discover')), 'Should register discover command')
117-
assert(patterns.some(p => p.includes('status')), 'Should register status command')
118-
111+
assert(robot.listeners.length > 0, 'Should have registered some commands')
112+
113+
// Check for specific command regexes based on current design
114+
const regexes = robot.listeners.map(cmd => cmd.regex.toString())
115+
assert(regexes.some(r => r.includes('discover')), 'Should register discover command')
116+
assert(regexes.some(r => r.includes('status')), 'Should register status command')
117+
119118
// Load balancer commands are always available (DiscoveryService is always a server)
120119
const DiscoveryService = robot.discoveryService
121120
if (DiscoveryService.loadBalancer) {
122-
assert(patterns.some(p => p.includes('load') || p.includes('lb')), 'Should register load balancer commands')
123-
assert(patterns.some(p => p.includes('routing')), 'Should register routing test command')
121+
assert(regexes.some(r => r.includes('load') || r.includes('lb')), 'Should register load balancer commands')
122+
assert(regexes.some(r => r.includes('routing')), 'Should register routing test command')
124123
}
125124
})
126125

@@ -293,8 +292,8 @@ describe('DiscoveryService Script', () => {
293292
// Start as server
294293

295294
// Find the discover command
296-
const discoverCommand = robot.commands.find(cmd =>
297-
cmd.pattern.toString().includes('discover')
295+
const discoverCommand = robot.listeners.find(cmd =>
296+
cmd.regex.toString().includes('discover')
298297
)
299298
assert(discoverCommand, 'Should have discover command')
300299

@@ -315,8 +314,8 @@ describe('DiscoveryService Script', () => {
315314
// Start as server
316315

317316
// Find the status command
318-
const statusCommand = robot.commands.find(cmd =>
319-
cmd.pattern.toString().includes('status')
317+
const statusCommand = robot.listeners.find(cmd =>
318+
cmd.regex.toString().includes('status')
320319
)
321320
assert(statusCommand, 'Should have status command')
322321

@@ -388,4 +387,4 @@ describe('DiscoveryService Script', () => {
388387
assert.strictEqual(selfInstance.host, 'localhost')
389388
assert.strictEqual(selfInstance.port, 8080)
390389
})
391-
})
390+
})

0 commit comments

Comments
 (0)