mega-framework 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/sample/crud/apps/main/channels/chat-channel.js +73 -76
- package/sample/crud/mega.config.js +9 -0
- package/sample/crud/test/apps/main/chat-channel.test.js +66 -61
- package/sample/crud/test/apps/main/ws-chat.integration.test.js +7 -6
- package/src/core/boot.js +30 -0
- package/src/core/config-validator.js +68 -0
- package/src/core/mega-app.js +55 -0
- package/src/core/scope-registry.js +1 -0
- package/src/core/ws-cluster.js +352 -0
- package/src/core/ws-upgrade.js +31 -0
- package/sample/crud/apps/main/channels/chat-bus.js +0 -115
- package/sample/crud/test/apps/main/chat-bus.test.js +0 -101
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
/**
|
|
3
|
-
* chat-bus 단위 테스트(ADR-158) — redis pub/sub cluster broadcast 의 송수신 계약.
|
|
4
|
-
*
|
|
5
|
-
* 인프라 불필요: ioredis 를 fake(duplicate→fake subscriber, publish spy)로 대체해
|
|
6
|
-
* PUBLISH 직렬화·SUBSCRIBE→로컬 전달·exceptSessionIds 제외·손상 페이로드 무시를 검증한다.
|
|
7
|
-
*/
|
|
8
|
-
import { describe, test, expect, vi, afterEach } from 'vitest'
|
|
9
|
-
import {
|
|
10
|
-
ensureSubscriber,
|
|
11
|
-
registerConn,
|
|
12
|
-
unregisterConn,
|
|
13
|
-
publish,
|
|
14
|
-
closeChatBus,
|
|
15
|
-
BCAST_CHANNEL,
|
|
16
|
-
} from '../../../apps/main/channels/chat-bus.js'
|
|
17
|
-
|
|
18
|
-
/** fake ioredis — duplicate 는 메시지 핸들러를 캡처하는 fake 구독자를 돌려준다. */
|
|
19
|
-
function fakeRedis() {
|
|
20
|
-
const handlers = {}
|
|
21
|
-
const sub = {
|
|
22
|
-
on: vi.fn((/** @type {string} */ ev, /** @type {Function} */ cb) => {
|
|
23
|
-
handlers[ev] = cb
|
|
24
|
-
}),
|
|
25
|
-
subscribe: vi.fn(async () => 1),
|
|
26
|
-
quit: vi.fn(async () => 'OK'),
|
|
27
|
-
}
|
|
28
|
-
return {
|
|
29
|
-
duplicate: vi.fn(() => sub),
|
|
30
|
-
publish: vi.fn(async () => 1),
|
|
31
|
-
_sub: sub,
|
|
32
|
-
/** 구독 채널로 메시지 1건 주입. */
|
|
33
|
-
emit: (raw) => handlers.message?.(BCAST_CHANNEL, raw),
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const app = { fastify: { log: { warn: vi.fn(), error: vi.fn() } } }
|
|
38
|
-
const mkSock = () => ({ isOpen: true, send: vi.fn() })
|
|
39
|
-
|
|
40
|
-
afterEach(async () => {
|
|
41
|
-
await closeChatBus()
|
|
42
|
-
vi.clearAllMocks()
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
test('publish 는 채널에 envelope 을 직렬화해 PUBLISH 한다', async () => {
|
|
46
|
-
const redis = fakeRedis()
|
|
47
|
-
const env = { message: { type: 'chat.msg', payload: { text: 'hi' } } }
|
|
48
|
-
await publish(/** @type {any} */ (redis), env)
|
|
49
|
-
expect(redis.publish).toHaveBeenCalledWith(BCAST_CHANNEL, JSON.stringify(env))
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
test('ensureSubscriber 는 워커당 1회만 구독자를 만든다', () => {
|
|
53
|
-
const redis = fakeRedis()
|
|
54
|
-
ensureSubscriber(app, /** @type {any} */ (redis))
|
|
55
|
-
ensureSubscriber(app, /** @type {any} */ (redis))
|
|
56
|
-
expect(redis.duplicate).toHaveBeenCalledTimes(1)
|
|
57
|
-
expect(redis._sub.subscribe).toHaveBeenCalledWith(BCAST_CHANNEL)
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
test('구독 메시지를 로컬 연결에 전달하고 exceptSessionIds 는 건너뛴다', () => {
|
|
61
|
-
const redis = fakeRedis()
|
|
62
|
-
ensureSubscriber(app, /** @type {any} */ (redis))
|
|
63
|
-
const a = mkSock()
|
|
64
|
-
const b = mkSock()
|
|
65
|
-
registerConn(a, { sessionId: 'sA', userName: 'a' })
|
|
66
|
-
registerConn(b, { sessionId: 'sB', userName: 'b' })
|
|
67
|
-
|
|
68
|
-
redis.emit(JSON.stringify({ message: { type: 'chat.msg', payload: { text: 'yo' } }, exceptSessionIds: ['sA'] }))
|
|
69
|
-
|
|
70
|
-
expect(a.send).not.toHaveBeenCalled() // 제외됨.
|
|
71
|
-
expect(b.send).toHaveBeenCalledWith({ type: 'chat.msg', payload: { text: 'yo' } })
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
test('except 없으면 모든 로컬 연결에 전달(본인 echo 포함)', () => {
|
|
75
|
-
const redis = fakeRedis()
|
|
76
|
-
ensureSubscriber(app, /** @type {any} */ (redis))
|
|
77
|
-
const a = mkSock()
|
|
78
|
-
registerConn(a, { sessionId: 'sA', userName: 'a' })
|
|
79
|
-
redis.emit(JSON.stringify({ message: { type: 'chat.msg', payload: { text: 'echo' } } }))
|
|
80
|
-
expect(a.send).toHaveBeenCalledWith({ type: 'chat.msg', payload: { text: 'echo' } })
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
test('unregister 된 연결에는 전달하지 않는다', () => {
|
|
84
|
-
const redis = fakeRedis()
|
|
85
|
-
ensureSubscriber(app, /** @type {any} */ (redis))
|
|
86
|
-
const a = mkSock()
|
|
87
|
-
registerConn(a, { sessionId: 'sA', userName: 'a' })
|
|
88
|
-
unregisterConn(a)
|
|
89
|
-
redis.emit(JSON.stringify({ message: { type: 'chat.msg', payload: {} } }))
|
|
90
|
-
expect(a.send).not.toHaveBeenCalled()
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
test('손상 페이로드는 전달하지 않고 warn 로그', () => {
|
|
94
|
-
const redis = fakeRedis()
|
|
95
|
-
ensureSubscriber(app, /** @type {any} */ (redis))
|
|
96
|
-
const a = mkSock()
|
|
97
|
-
registerConn(a, { sessionId: 'sA', userName: 'a' })
|
|
98
|
-
redis.emit('{not json')
|
|
99
|
-
expect(a.send).not.toHaveBeenCalled()
|
|
100
|
-
expect(app.fastify.log.warn).toHaveBeenCalled()
|
|
101
|
-
})
|