/** * Protobuf序列化器边界情况测试 */ import { Component } from '../../../src/ECS/Component'; import { BigIntFactory } from '../../../src/ECS/Utils/BigIntCompatibility'; import { ProtobufSerializer } from '../../../src/Utils/Serialization/ProtobufSerializer'; import { ProtoSerializable, ProtoFloat, ProtoInt32, ProtoString, ProtoBool, ProtoBytes, ProtoTimestamp, ProtoDouble, ProtoInt64, ProtoStruct } from '../../../src/Utils/Serialization/ProtobufDecorators'; // 边界测试组件 @ProtoSerializable('EdgeCaseComponent') class EdgeCaseComponent extends Component { @ProtoFloat(1) public floatValue: number = 0; @ProtoDouble(2) public doubleValue: number = 0; @ProtoInt32(3) public intValue: number = 0; @ProtoInt64(4) public bigIntValue: any = BigIntFactory.zero(); @ProtoString(5) public stringValue: string = ''; @ProtoBool(6) public boolValue: boolean = false; @ProtoBytes(7) public bytesValue: Uint8Array = new Uint8Array(); @ProtoTimestamp(8) public timestampValue: Date = new Date(); @ProtoStruct(9) public structValue: any = {}; @ProtoFloat(10, { repeated: true }) public arrayValue: number[] = []; constructor() { super(); } } // 不完整的组件(缺少字段) @ProtoSerializable('IncompleteComponent') class IncompleteComponent extends Component { @ProtoString(1) public name: string = ''; // 故意添加没有装饰器的字段 public undecoratedField: number = 42; constructor(name: string = '') { super(); this.name = name; } } // 有循环引用的组件 @ProtoSerializable('CircularComponent') class CircularComponent extends Component { @ProtoString(1) public name: string = ''; @ProtoStruct(2) public circular: any = null; constructor(name: string = '') { super(); this.name = name; // 创建循环引用 this.circular = this; } } // 没有protobuf装饰器的组件 class NonSerializableComponent extends Component { public data: string = 'test'; serialize(): any { return { data: this.data }; } deserialize(data: any): void { this.data = data.data || this.data; } } // Mock protobuf.js const mockProtobuf = { parse: jest.fn().mockReturnValue({ root: { lookupType: jest.fn().mockImplementation((typeName: string) => { return { verify: jest.fn().mockReturnValue(null), create: jest.fn().mockImplementation((data) => data), encode: jest.fn().mockReturnValue({ finish: jest.fn().mockReturnValue(new Uint8Array([1, 2, 3, 4])) }), decode: jest.fn().mockImplementation(() => ({ floatValue: 3.14, doubleValue: 2.718, intValue: 42, bigIntValue: BigIntFactory.create(999), stringValue: 'test', boolValue: true, bytesValue: new Uint8Array([65, 66, 67]), timestampValue: { seconds: 1609459200, nanos: 0 }, structValue: { fields: { key: { stringValue: 'value' } } }, arrayValue: [1.1, 2.2, 3.3], name: 'TestComponent' })), toObject: jest.fn().mockImplementation((message) => message) }; }), lookupTypeOrEnum: jest.fn().mockImplementation((typeName: string) => { if (typeName === 'google.protobuf.Timestamp') { return { verify: jest.fn().mockReturnValue(null), create: jest.fn().mockImplementation((data) => data), encode: jest.fn().mockReturnValue({ finish: jest.fn().mockReturnValue(new Uint8Array([1, 2, 3, 4])) }), decode: jest.fn().mockImplementation(() => ({ seconds: 1609459200, nanos: 0 })) }; } return null; }) } }) }; describe('ProtobufSerializer边界情况测试', () => { let serializer: ProtobufSerializer; beforeEach(() => { serializer = ProtobufSerializer.getInstance(); serializer.initialize(mockProtobuf); jest.clearAllMocks(); }); describe('极值测试', () => { it('应该处理极大值', () => { const component = new EdgeCaseComponent(); component.floatValue = Number.MAX_VALUE; component.doubleValue = Number.MAX_VALUE; component.intValue = Number.MAX_SAFE_INTEGER; component.bigIntValue = BigIntFactory.create(Number.MAX_SAFE_INTEGER); const result = serializer.serialize(component); expect(result.type).toBe('protobuf'); expect(result.size).toBeGreaterThan(0); }); it('应该处理极小值', () => { const component = new EdgeCaseComponent(); component.floatValue = Number.MIN_VALUE; component.doubleValue = Number.MIN_VALUE; component.intValue = Number.MIN_SAFE_INTEGER; component.bigIntValue = BigIntFactory.create(Number.MIN_SAFE_INTEGER); const result = serializer.serialize(component); expect(result.type).toBe('protobuf'); }); it('应该处理特殊数值', () => { const component = new EdgeCaseComponent(); component.floatValue = NaN; component.doubleValue = Infinity; component.intValue = 0; const result = serializer.serialize(component); expect(result.type).toBe('protobuf'); }); }); describe('空值和undefined测试', () => { it('应该处理null值', () => { const component = new EdgeCaseComponent(); (component as any).stringValue = null; (component as any).structValue = null; const result = serializer.serialize(component); expect(result.type).toBe('protobuf'); }); it('应该处理undefined值', () => { const component = new EdgeCaseComponent(); (component as any).stringValue = undefined; (component as any).floatValue = undefined; const result = serializer.serialize(component); expect(result.type).toBe('protobuf'); }); it('应该处理空数组', () => { const component = new EdgeCaseComponent(); component.arrayValue = []; const result = serializer.serialize(component); expect(result.type).toBe('protobuf'); }); }); describe('复杂数据类型测试', () => { it('应该处理复杂对象结构', () => { const component = new EdgeCaseComponent(); component.structValue = { nested: { array: [1, 2, 3], object: { key: 'value' }, date: new Date(), null: null, undefined: undefined } }; const result = serializer.serialize(component); expect(result.type).toBe('protobuf'); }); it('应该处理Date对象', () => { const component = new EdgeCaseComponent(); component.timestampValue = new Date('2021-01-01T00:00:00Z'); const result = serializer.serialize(component); expect(result.type).toBe('protobuf'); }); it('应该处理Uint8Array', () => { const component = new EdgeCaseComponent(); component.bytesValue = new Uint8Array([0, 255, 128, 64]); const result = serializer.serialize(component); expect(result.type).toBe('protobuf'); }); }); describe('循环引用测试', () => { it('应该拒绝循环引用对象并抛出错误', () => { const component = new CircularComponent('circular'); // 循环引用应该抛出错误,不再回退到JSON序列化 expect(() => { serializer.serialize(component); }).toThrow(); }); }); describe('不完整组件测试', () => { it('应该处理缺少装饰器的字段', () => { const component = new IncompleteComponent('test'); const result = serializer.serialize(component); expect(result.type).toBe('protobuf'); expect(result.componentType).toBe('IncompleteComponent'); }); }); describe('非序列化组件测试', () => { it('应该拒绝非protobuf组件并抛出错误', () => { const component = new NonSerializableComponent(); // 没有protobuf装饰器的组件应该抛出错误,不再回退到JSON序列化 expect(() => { serializer.serialize(component); }).toThrow(); }); }); describe('批量序列化边界测试', () => { it('应该处理空数组', () => { const results = serializer.serializeBatch([]); expect(results).toEqual([]); }); it('应该处理混合组件类型', () => { const components = [ new EdgeCaseComponent(), new NonSerializableComponent(), new IncompleteComponent('mixed'), ]; // continueOnError: true 时,只有可序列化的组件能成功,其他会被跳过 const results = serializer.serializeBatch(components, { continueOnError: true }); expect(results.length).toBeGreaterThanOrEqual(1); expect(results.every(r => r.type === 'protobuf')).toBe(true); // continueOnError: false 时应该抛出错误 expect(() => { serializer.serializeBatch(components, { continueOnError: false }); }).toThrow(); }); it('应该处理批量数据', () => { const components = Array.from({ length: 50 }, () => new EdgeCaseComponent()); const results = serializer.serializeBatch(components); expect(results).toHaveLength(50); expect(results.every(r => r.type === 'protobuf')).toBe(true); }); it('应该处理序列化错误', () => { // 创建会导致序列化失败的组件 const components = [new NonSerializableComponent()]; // continueOnError = false 应该抛出异常 expect(() => { serializer.serializeBatch(components, { continueOnError: false }); }).toThrow(); // continueOnError = true 应该返回空数组(跳过失败的组件) const results = serializer.serializeBatch(components, { continueOnError: true }); expect(results).toHaveLength(0); }); }); describe('反序列化边界测试', () => { it('应该拒绝JSON类型的反序列化并抛出错误', () => { const component = new NonSerializableComponent(); const serializedData = { type: 'json' as const, componentType: 'NonSerializableComponent', data: { data: 'deserialized' }, size: 100 }; // JSON类型的数据应该被拒绝,抛出错误 expect(() => { serializer.deserialize(component, serializedData); }).toThrow(); }); it('应该优雅处理反序列化错误', () => { const component = new EdgeCaseComponent(); const invalidData = { type: 'protobuf' as const, componentType: 'EdgeCaseComponent', data: new Uint8Array([255, 255, 255, 255]), size: 4 }; // 模拟解码失败 const mockType = mockProtobuf.parse().root.lookupType('ecs.EdgeCaseComponent'); mockType.decode.mockImplementation(() => { throw new Error('Decode failed'); }); // 不应该抛出异常 expect(() => { serializer.deserialize(component, invalidData); }).not.toThrow(); }); it('应该处理缺失的proto定义', () => { const component = new EdgeCaseComponent(); // 清除proto名称以模拟缺失情况 (component as any)._protoName = undefined; const serializedData = { type: 'protobuf' as const, componentType: 'EdgeCaseComponent', data: new Uint8Array([1, 2, 3, 4]), size: 4 }; // 不应该抛出异常 expect(() => { serializer.deserialize(component, serializedData); }).not.toThrow(); }); }); describe('缓存测试', () => { it('应该能清空所有缓存', () => { serializer.clearAllCaches(); const stats = serializer.getStats(); expect(stats.messageTypeCacheSize).toBe(0); expect(stats.componentDataCacheSize).toBe(0); }); }); describe('性能选项测试', () => { it('应该能禁用数据验证', () => { serializer.setPerformanceOptions({ enableValidation: false }); const component = new EdgeCaseComponent(); const result = serializer.serialize(component); expect(result.type).toBe('protobuf'); }); it('应该能禁用组件数据缓存', () => { serializer.setPerformanceOptions({ enableComponentDataCache: false }); const component = new EdgeCaseComponent(); serializer.serialize(component); const stats = serializer.getStats(); expect(stats.componentDataCacheSize).toBe(0); }); }); describe('统计信息测试', () => { it('应该返回正确的统计信息', () => { const stats = serializer.getStats(); expect(typeof stats.registeredComponents).toBe('number'); expect(typeof stats.protobufAvailable).toBe('boolean'); expect(typeof stats.messageTypeCacheSize).toBe('number'); expect(typeof stats.componentDataCacheSize).toBe('number'); expect(typeof stats.enableComponentDataCache).toBe('boolean'); expect(typeof stats.maxCacheSize).toBe('number'); }); }); });