重构服务端为接口式(WebSocketServer(port)+addHandler 工厂,每连接独立 handler),组管理与发送收敛为链式门面 group(name)/conn(connId),统一 send 三重重载,升级版本 1.1.0
This commit is contained in:
+209
-68
@@ -1,7 +1,8 @@
|
||||
/*
|
||||
* simcu::websocket.tests —— 端到端集成测试(cjpm test)。
|
||||
* 覆盖:文本回声 / 广播 / 关闭握手(客户端主动、服务端主动)/ terminate /
|
||||
* ping 心跳后连接仍可用 / 路径过滤 / maxPayload 超限(1009)。
|
||||
* ping 心跳后连接仍可用 / 路径路由(多 handler 按注册路径)/ maxPayload 超限(1009) /
|
||||
* 组播(group.send)/ 单播(conn.send)。
|
||||
*
|
||||
* 运行:在 websocket-cj 目录执行 cjpm test
|
||||
*/
|
||||
@@ -17,6 +18,7 @@ import simcu::websocket.client.WebSocketClient
|
||||
import simcu::websocket.common.MessageType
|
||||
import simcu::websocket.common.WebSocketException
|
||||
import simcu::websocket.common.WebSocketMessage
|
||||
import simcu::websocket.server.IWebsocketHandler
|
||||
import simcu::websocket.server.WebSocketConnection
|
||||
import simcu::websocket.server.WebSocketServer
|
||||
|
||||
@@ -45,27 +47,115 @@ private func waitUntil(timeoutMs: Int64, condition: () -> Bool): Bool {
|
||||
condition()
|
||||
}
|
||||
|
||||
/// 测试用 handler:把事件回调逻辑收进接口方法,行为写入 harness。
|
||||
class TestWsHandler <: IWebsocketHandler {
|
||||
let harness: WsTestHarness
|
||||
|
||||
init(harness: WsTestHarness) {
|
||||
this.harness = harness
|
||||
}
|
||||
|
||||
public override func onConnect(conn: WebSocketConnection): Unit {
|
||||
harness.serverConn = Some(conn)
|
||||
}
|
||||
|
||||
public override func onMessage(conn: WebSocketConnection, msg: WebSocketMessage): Unit {
|
||||
harness.connMessage = msg.text.getOrThrow()
|
||||
conn.send("echo: ${msg.text.getOrThrow()}")
|
||||
}
|
||||
|
||||
public override func onClose(conn: WebSocketConnection, code: Int64, reason: String): Unit {
|
||||
harness.connCloseCode = code
|
||||
harness.connCloseReason = reason
|
||||
}
|
||||
|
||||
public override func onError(conn: WebSocketConnection, e: Exception): Unit {
|
||||
if (let we: WebSocketException <- e) {
|
||||
harness.connErrorCode = we.code
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 广播场景:harness1 收到广播后转发文本。
|
||||
class EchoHandler <: IWebsocketHandler {
|
||||
let harness: WsTestHarness
|
||||
|
||||
init(harness: WsTestHarness) {
|
||||
this.harness = harness
|
||||
}
|
||||
|
||||
public override func onConnect(conn: WebSocketConnection): Unit {
|
||||
()
|
||||
}
|
||||
|
||||
public override func onMessage(conn: WebSocketConnection, msg: WebSocketMessage): Unit {
|
||||
harness.connMessage = msg.text.getOrThrow()
|
||||
}
|
||||
|
||||
public override func onClose(conn: WebSocketConnection, code: Int64, reason: String): Unit {
|
||||
()
|
||||
}
|
||||
|
||||
public override func onError(conn: WebSocketConnection, e: Exception): Unit {
|
||||
()
|
||||
}
|
||||
}
|
||||
|
||||
/// 服务端主动关闭场景:连接建立后立即 close。
|
||||
class CloseOnConnectHandler <: IWebsocketHandler {
|
||||
init() {}
|
||||
|
||||
public override func onConnect(conn: WebSocketConnection): Unit {
|
||||
conn.close(code: 4000, reason: "svr")
|
||||
}
|
||||
|
||||
public override func onMessage(conn: WebSocketConnection, msg: WebSocketMessage): Unit {
|
||||
()
|
||||
}
|
||||
|
||||
public override func onClose(conn: WebSocketConnection, code: Int64, reason: String): Unit {
|
||||
()
|
||||
}
|
||||
|
||||
public override func onError(conn: WebSocketConnection, e: Exception): Unit {
|
||||
()
|
||||
}
|
||||
}
|
||||
|
||||
/// 分组场景:连接建立后加入 "room" 组,便于验证组播/单播。
|
||||
class GroupJoinHandler <: IWebsocketHandler {
|
||||
public override func onConnect(conn: WebSocketConnection): Unit {
|
||||
conn.joinGroup("room")
|
||||
}
|
||||
|
||||
public override func onMessage(conn: WebSocketConnection, msg: WebSocketMessage): Unit {
|
||||
()
|
||||
}
|
||||
|
||||
public override func onClose(conn: WebSocketConnection, code: Int64, reason: String): Unit {
|
||||
()
|
||||
}
|
||||
|
||||
public override func onError(conn: WebSocketConnection, e: Exception): Unit {
|
||||
()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public class WebSocketTest {
|
||||
/// 1. 文本回声:client.sendText → server 收到后回发 → client 收到
|
||||
/// 1. 文本回声:client.send → server 收到后回发 → client 收到
|
||||
@TestCase
|
||||
public func echoText(): Unit {
|
||||
let h = WsTestHarness()
|
||||
let server = WebSocketServer(bindAt: 0)
|
||||
server.on("connection", { conn: WebSocketConnection =>
|
||||
h.serverConn = Some(conn)
|
||||
conn.on("message", { m: WebSocketMessage =>
|
||||
h.connMessage = m.text.getOrThrow()
|
||||
conn.sendText("echo: ${m.text.getOrThrow()}")
|
||||
})
|
||||
})
|
||||
server.listen()
|
||||
let client = WebSocketClient("127.0.0.1", server.localPort)
|
||||
let server = WebSocketServer(0)
|
||||
server.addHandler("/ws") { => TestWsHandler(h) }
|
||||
server.start()
|
||||
let client = WebSocketClient("127.0.0.1", server.localPort, path: "/ws")
|
||||
client.onMessage = { m: WebSocketMessage =>
|
||||
h.clientMessage = m.text.getOrThrow()
|
||||
}
|
||||
client.connect()
|
||||
client.sendText("hello")
|
||||
client.send("hello")
|
||||
|
||||
@Expect(waitUntil(5000) { => h.connMessage == Some("hello") }, true)
|
||||
@Expect(waitUntil(5000) { => h.clientMessage == Some("echo: hello") }, true)
|
||||
@@ -73,27 +163,28 @@ public class WebSocketTest {
|
||||
server.close()
|
||||
}
|
||||
|
||||
/// 2. 广播:server.broadcastText 两个客户端都收到
|
||||
/// 2. 广播:server.broadcast 两个客户端都收到
|
||||
@TestCase
|
||||
public func broadcast(): Unit {
|
||||
let h1 = WsTestHarness()
|
||||
let h2 = WsTestHarness()
|
||||
let server = WebSocketServer(bindAt: 0)
|
||||
server.listen()
|
||||
let server = WebSocketServer(0)
|
||||
server.addHandler("/ws") { => EchoHandler(h1) }
|
||||
server.start()
|
||||
|
||||
let c1 = WebSocketClient("127.0.0.1", server.localPort)
|
||||
let c1 = WebSocketClient("127.0.0.1", server.localPort, path: "/ws")
|
||||
c1.onMessage = { m: WebSocketMessage =>
|
||||
h1.clientMessage = m.text.getOrThrow()
|
||||
}
|
||||
c1.connect()
|
||||
let c2 = WebSocketClient("127.0.0.1", server.localPort)
|
||||
let c2 = WebSocketClient("127.0.0.1", server.localPort, path: "/ws")
|
||||
c2.onMessage = { m: WebSocketMessage =>
|
||||
h2.clientMessage = m.text.getOrThrow()
|
||||
}
|
||||
c2.connect()
|
||||
@Expect(waitUntil(5000) { => server.connectionCount == 2 }, true)
|
||||
|
||||
server.broadcastText("hi-all")
|
||||
server.broadcast("hi-all")
|
||||
@Expect(waitUntil(5000) { => h1.clientMessage == Some("hi-all") }, true)
|
||||
@Expect(waitUntil(5000) { => h2.clientMessage == Some("hi-all") }, true)
|
||||
c1.close()
|
||||
@@ -105,15 +196,10 @@ public class WebSocketTest {
|
||||
@TestCase
|
||||
public func closeHandshake(): Unit {
|
||||
let h = WsTestHarness()
|
||||
let server = WebSocketServer(bindAt: 0)
|
||||
server.on("connection", { conn: WebSocketConnection =>
|
||||
conn.on("close", { code: Int64, reason: String =>
|
||||
h.connCloseCode = code
|
||||
h.connCloseReason = reason
|
||||
})
|
||||
})
|
||||
server.listen()
|
||||
let client = WebSocketClient("127.0.0.1", server.localPort)
|
||||
let server = WebSocketServer(0)
|
||||
server.addHandler("/ws") { => TestWsHandler(h) }
|
||||
server.start()
|
||||
let client = WebSocketClient("127.0.0.1", server.localPort, path: "/ws")
|
||||
client.onClose = { code: Int64, reason: String =>
|
||||
h.clientCloseCode = code
|
||||
h.clientCloseReason = reason
|
||||
@@ -130,13 +216,10 @@ public class WebSocketTest {
|
||||
@TestCase
|
||||
public func serverClose(): Unit {
|
||||
let h = WsTestHarness()
|
||||
let server = WebSocketServer(bindAt: 0)
|
||||
server.on("connection", { conn: WebSocketConnection =>
|
||||
h.serverConn = Some(conn)
|
||||
conn.close(code: 4000, reason: "svr")
|
||||
})
|
||||
server.listen()
|
||||
let client = WebSocketClient("127.0.0.1", server.localPort)
|
||||
let server = WebSocketServer(0)
|
||||
server.addHandler("/ws") { => CloseOnConnectHandler() }
|
||||
server.start()
|
||||
let client = WebSocketClient("127.0.0.1", server.localPort, path: "/ws")
|
||||
client.onClose = { code: Int64, reason: String =>
|
||||
h.clientCloseCode = code
|
||||
h.clientCloseReason = reason
|
||||
@@ -151,15 +234,10 @@ public class WebSocketTest {
|
||||
@TestCase
|
||||
public func terminate(): Unit {
|
||||
let h = WsTestHarness()
|
||||
let server = WebSocketServer(bindAt: 0)
|
||||
server.on("connection", { conn: WebSocketConnection =>
|
||||
conn.on("close", { code: Int64, reason: String =>
|
||||
h.connCloseCode = code
|
||||
h.connCloseReason = reason
|
||||
})
|
||||
})
|
||||
server.listen()
|
||||
let client = WebSocketClient("127.0.0.1", server.localPort)
|
||||
let server = WebSocketServer(0)
|
||||
server.addHandler("/ws") { => TestWsHandler(h) }
|
||||
server.start()
|
||||
let client = WebSocketClient("127.0.0.1", server.localPort, path: "/ws")
|
||||
client.connect()
|
||||
client.terminate()
|
||||
|
||||
@@ -171,29 +249,27 @@ public class WebSocketTest {
|
||||
@TestCase
|
||||
public func ping(): Unit {
|
||||
let h = WsTestHarness()
|
||||
let server = WebSocketServer(bindAt: 0)
|
||||
server.on("connection", { conn: WebSocketConnection =>
|
||||
conn.on("message", { m: WebSocketMessage =>
|
||||
h.connMessage = m.text.getOrThrow()
|
||||
})
|
||||
})
|
||||
server.listen()
|
||||
let client = WebSocketClient("127.0.0.1", server.localPort)
|
||||
let server = WebSocketServer(0)
|
||||
server.addHandler("/ws") { => TestWsHandler(h) }
|
||||
server.start()
|
||||
let client = WebSocketClient("127.0.0.1", server.localPort, path: "/ws")
|
||||
client.connect()
|
||||
|
||||
client.ping()
|
||||
sleep(Duration.millisecond * 100)
|
||||
client.sendText("after-ping")
|
||||
client.send("after-ping")
|
||||
@Expect(waitUntil(5000) { => h.connMessage == Some("after-ping") }, true)
|
||||
client.close()
|
||||
server.close()
|
||||
}
|
||||
|
||||
/// 7. 路径过滤:path=Some("/ws") 时 /ws 握手成功,/other 被拒(connect 抛异常)
|
||||
/// 7. 路径路由:注册 /ws 与 /admin 两个 handler,/ws 握手成功,/other 被拒(connect 抛异常)
|
||||
@TestCase
|
||||
public func pathFilter(): Unit {
|
||||
let server = WebSocketServer(bindAt: 0, path: Some("/ws"))
|
||||
server.listen()
|
||||
public func pathRoute(): Unit {
|
||||
let server = WebSocketServer(0)
|
||||
server.addHandler("/ws") { => TestWsHandler(WsTestHarness()) }
|
||||
server.addHandler("/admin") { => TestWsHandler(WsTestHarness()) }
|
||||
server.start()
|
||||
|
||||
let ok = WebSocketClient("127.0.0.1", server.localPort, path: "/ws")
|
||||
ok.connect()
|
||||
@@ -214,24 +290,89 @@ public class WebSocketTest {
|
||||
@TestCase
|
||||
public func maxPayload(): Unit {
|
||||
let h = WsTestHarness()
|
||||
let server = WebSocketServer(bindAt: 0, maxPayload: 10)
|
||||
server.on("connection", { conn: WebSocketConnection =>
|
||||
conn.on("error", { e: Exception =>
|
||||
if (let we: WebSocketException <- e) {
|
||||
h.connErrorCode = we.code
|
||||
}
|
||||
})
|
||||
})
|
||||
server.listen()
|
||||
let client = WebSocketClient("127.0.0.1", server.localPort)
|
||||
let server = WebSocketServer(0, maxPayload: 10)
|
||||
server.addHandler("/ws") { => TestWsHandler(h) }
|
||||
server.start()
|
||||
let client = WebSocketClient("127.0.0.1", server.localPort, path: "/ws")
|
||||
client.onError = { e: Exception =>
|
||||
h.clientErrorCount += 1
|
||||
}
|
||||
client.connect()
|
||||
client.sendText("this message is way too long")
|
||||
client.send("this message is way too long")
|
||||
|
||||
@Expect(waitUntil(5000) { => h.connErrorCode == 1009 }, true)
|
||||
client.terminate()
|
||||
server.close()
|
||||
}
|
||||
|
||||
/// 9. 组播:两个连接加入同组("room"),group("room").send 两连接都收到;关闭一个后组计数减一
|
||||
@TestCase
|
||||
public func groupMulticast(): Unit {
|
||||
let h1 = WsTestHarness()
|
||||
let h2 = WsTestHarness()
|
||||
let server = WebSocketServer(0)
|
||||
server.addHandler("/ws") { => GroupJoinHandler() }
|
||||
server.start()
|
||||
|
||||
let c1 = WebSocketClient("127.0.0.1", server.localPort, path: "/ws")
|
||||
c1.onMessage = { m: WebSocketMessage =>
|
||||
h1.clientMessage = m.text.getOrThrow()
|
||||
}
|
||||
c1.connect()
|
||||
let c2 = WebSocketClient("127.0.0.1", server.localPort, path: "/ws")
|
||||
c2.onMessage = { m: WebSocketMessage =>
|
||||
h2.clientMessage = m.text.getOrThrow()
|
||||
}
|
||||
c2.connect()
|
||||
@Expect(waitUntil(5000) { => server.group("room").count() == 2 }, true)
|
||||
|
||||
server.group("room").send("team-msg")
|
||||
@Expect(waitUntil(5000) { => h1.clientMessage == Some("team-msg") }, true)
|
||||
@Expect(waitUntil(5000) { => h2.clientMessage == Some("team-msg") }, true)
|
||||
|
||||
// 关闭一个连接后自动退组,组计数减一
|
||||
c1.close()
|
||||
@Expect(waitUntil(5000) { => server.group("room").count() == 1 }, true)
|
||||
server.group("room").send("after-leave")
|
||||
@Expect(waitUntil(5000) { => h2.clientMessage == Some("after-leave") }, true)
|
||||
c2.close()
|
||||
server.close()
|
||||
}
|
||||
|
||||
/// 10. 单播:conn(connId).send 只发到指定连接Id,另一连接收不到
|
||||
@TestCase
|
||||
public func unicast(): Unit {
|
||||
let h1 = WsTestHarness()
|
||||
let h2 = WsTestHarness()
|
||||
let server = WebSocketServer(0)
|
||||
server.addHandler("/ws") { => GroupJoinHandler() }
|
||||
server.start()
|
||||
|
||||
let c1 = WebSocketClient("127.0.0.1", server.localPort, path: "/ws")
|
||||
c1.onMessage = { m: WebSocketMessage =>
|
||||
h1.clientMessage = m.text.getOrThrow()
|
||||
}
|
||||
c1.connect()
|
||||
let c2 = WebSocketClient("127.0.0.1", server.localPort, path: "/ws")
|
||||
c2.onMessage = { m: WebSocketMessage =>
|
||||
h2.clientMessage = m.text.getOrThrow()
|
||||
}
|
||||
c2.connect()
|
||||
@Expect(waitUntil(5000) { => server.group("room").connIds().size == 2 }, true)
|
||||
|
||||
h1.clientMessage = None
|
||||
h2.clientMessage = None
|
||||
let ids = server.group("room").connIds()
|
||||
let target = ids[0]
|
||||
server.conn(target).send("only-you")
|
||||
@Expect(waitUntil(5000) { => h1.clientMessage == Some("only-you") || h2.clientMessage == Some("only-you") }, true)
|
||||
// 稍等片刻后断言:仅目标接收,另一连接未收到
|
||||
sleep(Duration.millisecond * 200)
|
||||
let exactlyOne = (h1.clientMessage == Some("only-you") && h2.clientMessage != Some("only-you")) ||
|
||||
(h2.clientMessage == Some("only-you") && h1.clientMessage != Some("only-you"))
|
||||
@Expect(exactlyOne, true)
|
||||
c1.close()
|
||||
c2.close()
|
||||
server.close()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user