import 'package:swagger_generator_flutter/core/models.dart'; import 'package:swagger_generator_flutter/utils/reference_resolver.dart'; import 'package:test/test.dart'; void main() { group('ReferenceResolver', () { late ReferenceResolver resolver; setUp(() { resolver = ReferenceResolver(); }); tearDown(() { resolver.clearCache(); }); test('resolves simple models without references', () { final componentsJson = { 'schemas': { 'User': { 'type': 'object', 'description': 'User model', 'properties': { 'id': {'type': 'integer'}, 'name': {'type': 'string'}, }, 'required': ['id', 'name'], }, 'Product': { 'type': 'object', 'description': 'Product model', 'properties': { 'id': {'type': 'integer'}, 'title': {'type': 'string'}, 'price': {'type': 'number'}, }, 'required': ['id', 'title'], }, }, }; final models = resolver.resolveModels(componentsJson); expect(models.length, 2); expect(models['User'], isNotNull); expect(models['User']!.name, 'User'); expect(models['User']!.properties.length, 2); expect(models['User']!.required, ['id', 'name']); expect(models['Product'], isNotNull); expect(models['Product']!.name, 'Product'); expect(models['Product']!.properties.length, 3); expect(models['Product']!.required, ['id', 'title']); }); test('resolves models with allOf composition', () { final componentsJson = { 'schemas': { 'BaseEntity': { 'type': 'object', 'properties': { 'id': {'type': 'integer'}, 'createdAt': {'type': 'string', 'format': 'date-time'}, }, 'required': ['id'], }, 'User': { 'allOf': [ {'\$ref': '#/components/schemas/BaseEntity'}, { 'type': 'object', 'properties': { 'name': {'type': 'string'}, 'email': {'type': 'string'}, }, 'required': ['name'], }, ], }, }, }; final models = resolver.resolveModels(componentsJson); expect(models.length, 2); expect(models['User'], isNotNull); expect(models['User']!.isAllOf, true); expect(models['User']!.allOf.length, 2); // 检查合并的属性 expect( models['User']!.properties.length, 4); // id, createdAt, name, email expect(models['User']!.properties['id'], isNotNull); expect(models['User']!.properties['name'], isNotNull); expect(models['User']!.properties['email'], isNotNull); expect(models['User']!.properties['createdAt'], isNotNull); }); test('detects and handles circular references', () { final componentsJson = { 'schemas': { 'Node': { 'type': 'object', 'properties': { 'id': {'type': 'integer'}, 'parent': {'\$ref': '#/components/schemas/Node'}, 'children': { 'type': 'array', 'items': {'\$ref': '#/components/schemas/Node'}, }, }, 'required': ['id'], }, }, }; final models = resolver.resolveModels(componentsJson); expect(models.length, 1); expect(models['Node'], isNotNull); expect(models['Node']!.name, 'Node'); expect(models['Node']!.properties.length, 3); // 循环引用应该被正确处理,不会导致无限递归 expect(models['Node']!.properties['parent'], isNotNull); expect(models['Node']!.properties['children'], isNotNull); }); test('handles complex nested structures', () { final componentsJson = { 'schemas': { 'Address': { 'type': 'object', 'properties': { 'street': {'type': 'string'}, 'city': {'type': 'string'}, 'coordinates': { 'type': 'object', 'properties': { 'lat': {'type': 'number'}, 'lng': {'type': 'number'}, }, 'required': ['lat', 'lng'], }, }, 'required': ['street', 'city'], }, 'User': { 'type': 'object', 'properties': { 'id': {'type': 'integer'}, 'name': {'type': 'string'}, 'addresses': { 'type': 'array', 'items': {'\$ref': '#/components/schemas/Address'}, }, }, 'required': ['id', 'name'], }, }, }; final models = resolver.resolveModels(componentsJson); expect(models.length, 2); expect(models['User'], isNotNull); expect(models['Address'], isNotNull); final userModel = models['User']!; expect(userModel.properties['addresses'], isNotNull); expect(userModel.properties['addresses']!.type, PropertyType.array); final addressModel = models['Address']!; expect(addressModel.properties['coordinates'], isNotNull); expect(addressModel.properties['coordinates']!.type, PropertyType.object); }); test('handles enum models', () { final componentsJson = { 'schemas': { 'Status': { 'type': 'string', 'enum': ['active', 'inactive', 'pending'], 'description': 'User status', }, 'Priority': { 'type': 'integer', 'enum': [1, 2, 3, 4, 5], 'description': 'Priority level', }, }, }; final models = resolver.resolveModels(componentsJson); expect(models.length, 2); final statusModel = models['Status']!; expect(statusModel.isEnum, true); expect(statusModel.enumValues, ['active', 'inactive', 'pending']); expect(statusModel.enumType, PropertyType.string); final priorityModel = models['Priority']!; expect(priorityModel.isEnum, true); expect(priorityModel.enumValues, [1, 2, 3, 4, 5]); expect(priorityModel.enumType, PropertyType.integer); }); test('handles oneOf with discriminator', () { final componentsJson = { 'schemas': { 'Pet': { 'oneOf': [ {'\$ref': '#/components/schemas/Cat'}, {'\$ref': '#/components/schemas/Dog'}, ], 'discriminator': { 'propertyName': 'petType', 'mapping': { 'cat': '#/components/schemas/Cat', 'dog': '#/components/schemas/Dog', }, }, }, 'Cat': { 'type': 'object', 'properties': { 'petType': {'type': 'string'}, 'meowSound': {'type': 'string'}, }, 'required': ['petType'], }, 'Dog': { 'type': 'object', 'properties': { 'petType': {'type': 'string'}, 'barkSound': {'type': 'string'}, }, 'required': ['petType'], }, }, }; final models = resolver.resolveModels(componentsJson); expect(models.length, 3); final petModel = models['Pet']!; expect(petModel.isOneOf, true); expect(petModel.hasDiscriminator, true); expect(petModel.discriminator?.propertyName, 'petType'); expect(petModel.discriminator?.mapping.length, 2); expect(models['Cat'], isNotNull); expect(models['Dog'], isNotNull); }); test('respects maximum depth limit', () { final resolver = ReferenceResolver(maxDepth: 3); final componentsJson = { 'schemas': { 'DeepNested': { 'type': 'object', 'properties': { 'level1': { 'type': 'object', 'properties': { 'level2': { 'type': 'object', 'properties': { 'level3': { 'type': 'object', 'properties': { 'level4': {'type': 'string'}, }, }, }, }, }, }, }, }, }, }; final models = resolver.resolveModels(componentsJson); expect(models.length, 1); expect(models['DeepNested'], isNotNull); // 应该能够解析,但深度受限 final model = models['DeepNested']!; expect(model.properties['level1'], isNotNull); }); test('handles parsing errors gracefully', () { final componentsJson = { 'schemas': { 'ValidModel': { 'type': 'object', 'properties': { 'id': {'type': 'integer'}, }, }, 'InvalidModel': { // 故意创建一个可能导致解析错误的结构 'properties': 'invalid_properties_value', // 这会导致类型错误 'required': 123, // 这也会导致类型错误 }, }, }; final models = resolver.resolveModels(componentsJson); expect(models.length, 2); expect(models['ValidModel'], isNotNull); expect(models['InvalidModel'], isNotNull); // 无效模型应该被创建为后备模型 final invalidModel = models['InvalidModel']!; expect(invalidModel.description, '解析失败的模型'); }); }); }