test:添加位置广播系统端到端测试

- 添加并发用户测试场景
- 实现数据库恢复集成测试
- 重命名登录测试文件以符合命名规范
This commit is contained in:
moyin
2026-01-08 23:06:11 +08:00
parent c31cbe559d
commit 71bc317c57
7 changed files with 2580 additions and 0 deletions

View File

@@ -0,0 +1,432 @@
/**
* 位置广播端到端测试
*
* 功能描述:
* - 测试位置广播系统的完整功能
* - 验证WebSocket连接和消息传递
* - 确保会话管理和用户状态同步
* - 测试位置更新和广播机制
*
* 最近修改:
* - 2026-01-08: 文件重命名 - 修正kebab-case为snake_case命名规范 (修改者: moyin)
*
* @author original
* @version 1.0.1
* @since 2025-01-01
* @lastModified 2026-01-08
*/
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import { io, Socket } from 'socket.io-client';
import { LocationBroadcastModule } from '../../location_broadcast.module';
describe('LocationBroadcast (e2e)', () => {
let app: INestApplication;
let authToken: string;
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [LocationBroadcastModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
await app.listen(0);
authToken = 'test-jwt-token';
});
afterAll(async () => {
await app.close();
});
describe('WebSocket连接测试', () => {
it('应该成功建立WebSocket连接', (done) => {
const port = app.getHttpServer().address().port;
const client = io(`http://localhost:${port}/location-broadcast`, {
auth: { token: authToken },
transports: ['websocket'],
});
client.on('connect', () => {
expect(client.connected).toBe(true);
client.disconnect();
done();
});
client.on('connect_error', (error) => {
done(error);
});
});
it('应该拒绝无效的认证令牌', (done) => {
const port = app.getHttpServer().address().port;
const client = io(`http://localhost:${port}/location-broadcast`, {
auth: { token: 'invalid-token' },
transports: ['websocket'],
});
client.on('connect_error', (error) => {
expect(error).toBeDefined();
done();
});
client.on('connect', () => {
client.disconnect();
done(new Error('应该拒绝无效令牌'));
});
});
});
describe('会话管理测试', () => {
let client: Socket;
beforeEach((done) => {
const port = app.getHttpServer().address().port;
client = io(`http://localhost:${port}/location-broadcast`, {
auth: { token: authToken },
transports: ['websocket'],
});
client.on('connect', () => {
done();
});
});
afterEach(() => {
if (client) {
client.disconnect();
}
});
it('应该成功加入会话', (done) => {
client.emit('join_session', {
type: 'join_session',
sessionId: 'test-session-001',
initialPosition: { x: 100, y: 200, mapId: 'plaza' },
});
client.on('session_joined', (response) => {
expect(response.success).toBe(true);
expect(response.sessionId).toBe('test-session-001');
done();
});
client.on('error', (error) => {
done(error);
});
});
it('应该成功离开会话', (done) => {
const sessionId = 'test-session-leave';
// 先加入会话
client.emit('join_session', {
type: 'join_session',
sessionId,
initialPosition: { x: 100, y: 200, mapId: 'plaza' },
});
client.on('session_joined', () => {
// 然后离开会话
client.emit('leave_session', {
type: 'leave_session',
sessionId,
});
});
client.on('session_left', (response) => {
expect(response.success).toBe(true);
expect(response.sessionId).toBe(sessionId);
done();
});
client.on('error', (error) => {
done(error);
});
});
});
describe('位置更新测试', () => {
let client: Socket;
const sessionId = 'position-test-session';
beforeEach((done) => {
const port = app.getHttpServer().address().port;
client = io(`http://localhost:${port}/location-broadcast`, {
auth: { token: authToken },
transports: ['websocket'],
});
client.on('connect', () => {
client.emit('join_session', {
type: 'join_session',
sessionId,
initialPosition: { x: 100, y: 200, mapId: 'plaza' },
});
});
client.on('session_joined', () => {
done();
});
});
afterEach(() => {
if (client) {
client.disconnect();
}
});
it('应该成功更新位置', (done) => {
client.emit('position_update', {
type: 'position_update',
x: 150,
y: 250,
mapId: 'plaza',
});
client.on('position_update_success', (response) => {
expect(response.success).toBe(true);
expect(response.position.x).toBe(150);
expect(response.position.y).toBe(250);
done();
});
client.on('error', (error) => {
done(error);
});
});
it('应该拒绝无效的位置数据', (done) => {
client.emit('position_update', {
type: 'position_update',
x: 'invalid',
y: 250,
mapId: 'plaza',
});
client.on('error', (error) => {
expect(error.message).toContain('Invalid position data');
done();
});
client.on('position_update_success', () => {
done(new Error('应该拒绝无效位置数据'));
});
});
});
describe('位置广播测试', () => {
let client1: Socket;
let client2: Socket;
const sessionId = 'broadcast-test-session';
beforeEach((done) => {
const port = app.getHttpServer().address().port;
let connectedClients = 0;
client1 = io(`http://localhost:${port}/location-broadcast`, {
auth: { token: authToken },
transports: ['websocket'],
});
client2 = io(`http://localhost:${port}/location-broadcast`, {
auth: { token: authToken },
transports: ['websocket'],
});
const checkConnections = () => {
connectedClients++;
if (connectedClients === 2) {
// 两个客户端都加入同一会话
client1.emit('join_session', {
type: 'join_session',
sessionId,
initialPosition: { x: 100, y: 200, mapId: 'plaza' },
});
client2.emit('join_session', {
type: 'join_session',
sessionId,
initialPosition: { x: 300, y: 400, mapId: 'plaza' },
});
}
};
let joinedClients = 0;
const checkJoins = () => {
joinedClients++;
if (joinedClients === 2) {
done();
}
};
client1.on('connect', checkConnections);
client2.on('connect', checkConnections);
client1.on('session_joined', checkJoins);
client2.on('session_joined', checkJoins);
});
afterEach(() => {
if (client1) client1.disconnect();
if (client2) client2.disconnect();
});
it('应该向同会话的其他用户广播位置更新', (done) => {
// client2 监听广播
client2.on('position_broadcast', (broadcast) => {
expect(broadcast.userId).toBeDefined();
expect(broadcast.position.x).toBe(150);
expect(broadcast.position.y).toBe(250);
done();
});
// client1 更新位置
client1.emit('position_update', {
type: 'position_update',
x: 150,
y: 250,
mapId: 'plaza',
});
});
it('不应该向不同会话的用户广播位置更新', (done) => {
const differentSessionId = 'different-session';
let broadcastReceived = false;
// client2 加入不同会话
client2.emit('leave_session', {
type: 'leave_session',
sessionId,
});
client2.on('session_left', () => {
client2.emit('join_session', {
type: 'join_session',
sessionId: differentSessionId,
initialPosition: { x: 300, y: 400, mapId: 'plaza' },
});
});
client2.on('session_joined', () => {
// 监听广播
client2.on('position_broadcast', () => {
broadcastReceived = true;
});
// client1 更新位置
client1.emit('position_update', {
type: 'position_update',
x: 150,
y: 250,
mapId: 'plaza',
});
// 等待一段时间确认没有收到广播
const timeoutId = setTimeout(() => {
expect(broadcastReceived).toBe(false);
done();
}, 1000);
});
});
});
describe('心跳机制测试', () => {
let client: Socket;
beforeEach((done) => {
const port = app.getHttpServer().address().port;
client = io(`http://localhost:${port}/location-broadcast`, {
auth: { token: authToken },
transports: ['websocket'],
});
client.on('connect', () => {
done();
});
});
afterEach(() => {
if (client) {
client.disconnect();
}
});
it('应该响应心跳消息', (done) => {
const timestamp = Date.now();
client.emit('heartbeat', {
type: 'heartbeat',
timestamp,
});
client.on('heartbeat_response', (response) => {
expect(response.timestamp).toBe(timestamp);
expect(response.serverTime).toBeDefined();
done();
});
client.on('error', (error) => {
done(error);
});
});
});
describe('错误处理测试', () => {
let client: Socket;
beforeEach((done) => {
const port = app.getHttpServer().address().port;
client = io(`http://localhost:${port}/location-broadcast`, {
auth: { token: authToken },
transports: ['websocket'],
});
client.on('connect', () => {
done();
});
});
afterEach(() => {
if (client) {
client.disconnect();
}
});
it('应该处理无效的消息格式', (done) => {
client.emit('invalid_message', 'not an object');
client.on('error', (error) => {
expect(error.message).toContain('Invalid message format');
done();
});
});
it('应该处理未知的消息类型', (done) => {
client.emit('unknown_type', {
type: 'unknown_message_type',
data: 'test',
});
client.on('error', (error) => {
expect(error.message).toContain('Unknown message type');
done();
});
});
it('应该处理在未加入会话时的位置更新', (done) => {
client.emit('position_update', {
type: 'position_update',
x: 100,
y: 200,
mapId: 'plaza',
});
client.on('error', (error) => {
expect(error.message).toContain('Not in session');
done();
});
});
});
});