From 90843374f9ea9fc1966f8044e3024dd23bb6ad72 Mon Sep 17 00:00:00 2001 From: Joywayer Date: Sun, 28 Jun 2026 14:22:47 +0800 Subject: [PATCH] =?UTF-8?q?feat(framework):=20net=20Transport=20=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=20+=20=E6=B5=8B=E8=AF=95=E7=94=A8=20FakeTransport?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 (1M context) --- .../assets/framework/net/transport.ts | 14 ++++++++ .../framework-tests/helpers/fake-transport.ts | 27 ++++++++++++++ .../framework-tests/net/transport.test.ts | 36 +++++++++++++++++++ 3 files changed, 77 insertions(+) create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/net/transport.ts create mode 100644 cocoscreator_projects/framework-tests/helpers/fake-transport.ts create mode 100644 cocoscreator_projects/framework-tests/net/transport.test.ts diff --git a/cocoscreator_projects/YouleNexus/assets/framework/net/transport.ts b/cocoscreator_projects/YouleNexus/assets/framework/net/transport.ts new file mode 100644 index 0000000..9f3be63 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/net/transport.ts @@ -0,0 +1,14 @@ +/** + * WebSocket 抽象(依赖注入点,D2)。 + * 生产:Cocos/浏览器 WebSocket adapter(见 cocos-transport.ts)。 + * 测试:内存 FakeTransport(模拟 server)。 + */ +export interface Transport { + send(frame: string): void; + close(): void; + onOpen(cb: () => void): void; + onMessage(cb: (frame: string) => void): void; + onClose(cb: () => void): void; + /** 发起连接(建立后应触发 onOpen)。 */ + connect(url: string): void; +} diff --git a/cocoscreator_projects/framework-tests/helpers/fake-transport.ts b/cocoscreator_projects/framework-tests/helpers/fake-transport.ts new file mode 100644 index 0000000..c8ad944 --- /dev/null +++ b/cocoscreator_projects/framework-tests/helpers/fake-transport.ts @@ -0,0 +1,27 @@ +import type { Transport } from '../../YouleNexus/assets/framework/net/transport.ts'; + +/** 内存 Transport:模拟服务器。用 microtask 队列异步触发回调,贴近真实 WS 时序。 */ +export class FakeTransport implements Transport { + sent: string[] = []; + url = ''; + private openCb?: () => void; + private msgCb?: (f: string) => void; + private closeCb?: () => void; + private pending: Array<() => void> = []; + private closed = false; + + connect(url: string): void { this.url = url; this.pending.push(() => this.openCb?.()); } + send(frame: string): void { if (!this.closed) this.sent.push(frame); } + close(): void { if (this.closed) return; this.closed = true; this.pending.push(() => this.closeCb?.()); } + onOpen(cb: () => void): void { this.openCb = cb; } + onMessage(cb: (f: string) => void): void { this.msgCb = cb; } + onClose(cb: () => void): void { this.closeCb = cb; } + + /** 测试侧:模拟服务器推一帧给客户端。 */ + serverPush(frame: string): void { if (!this.closed) this.pending.push(() => this.msgCb?.(frame)); } + + /** 冲刷所有挂起回调(解析微任务)。 */ + async flush(): Promise { + while (this.pending.length) { const fn = this.pending.shift()!; fn(); await Promise.resolve(); } + } +} diff --git a/cocoscreator_projects/framework-tests/net/transport.test.ts b/cocoscreator_projects/framework-tests/net/transport.test.ts new file mode 100644 index 0000000..03f6114 --- /dev/null +++ b/cocoscreator_projects/framework-tests/net/transport.test.ts @@ -0,0 +1,36 @@ +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { FakeTransport } from '../helpers/fake-transport.ts'; + +test('FakeTransport:connect 后异步触发 onOpen', async () => { + const t = new FakeTransport(); + let opened = false; + t.onOpen(() => { opened = true; }); + t.connect('ws://x'); + await t.flush(); + assert.equal(opened, true); +}); + +test('FakeTransport:客户端 send 进入 sent 队列;serverPush 触发 onMessage', async () => { + const t = new FakeTransport(); + const got: string[] = []; + t.onMessage((f) => got.push(f)); + t.connect('ws://x'); + await t.flush(); + t.send('hello'); + assert.deepEqual(t.sent, ['hello']); + t.serverPush('world'); + await t.flush(); + assert.deepEqual(got, ['world']); +}); + +test('FakeTransport:close 触发 onClose', async () => { + const t = new FakeTransport(); + let closed = false; + t.onClose(() => { closed = true; }); + t.connect('ws://x'); + await t.flush(); + t.close(); + await t.flush(); + assert.equal(closed, true); +});