import 'package:swagger_generator_flutter/core/models.dart'; import 'package:test/test.dart'; void main() { group('Comprehensive Parser Tests', () { group('OpenAPI 3.0 Core Features', () { test('parses basic OpenAPI 3.0 document', () { final json = { 'openapi': '3.0.3', 'info': { 'title': 'Test API', 'version': '1.0.0', 'description': 'A test API', }, 'servers': [ { 'url': 'https://api.example.com', 'description': 'Production server', }, ], 'paths': { '/users': { 'get': { 'summary': 'Get users', 'operationId': 'getUsers', 'responses': { '200': { 'description': 'Success', 'content': { 'application/json': { 'schema': { 'type': 'array', 'items': { r'$ref': '#/components/schemas/User', }, }, }, }, }, }, }, }, }, 'components': { 'schemas': { 'User': { 'type': 'object', 'properties': { 'id': { 'type': 'integer', 'format': 'int64', }, 'name': { 'type': 'string', }, }, 'required': ['id', 'name'], }, }, }, }; final document = SwaggerDocument.fromJson(json); expect(document.title, equals('Test API')); expect(document.version, equals('1.0.0')); expect(document.description, equals('A test API')); expect(document.servers, hasLength(1)); expect(document.servers.first.url, equals('https://api.example.com')); expect(document.paths, hasLength(1)); expect(document.models, hasLength(1)); expect(document.models.containsKey('User'), isTrue); }); test('parses servers with variables', () { final json = { 'openapi': '3.0.3', 'info': {'title': 'Test', 'version': '1.0.0'}, 'servers': [ { 'url': 'https://{environment}.example.com/{basePath}', 'description': 'Configurable server', 'variables': { 'environment': { 'default': 'api', 'enum': ['api', 'staging', 'dev'], 'description': 'Environment name', }, 'basePath': { 'default': 'v1', 'description': 'API version', }, }, }, ], 'paths': {}, }; final document = SwaggerDocument.fromJson(json); final server = document.servers.first; expect( server.url, equals('https://{environment}.example.com/{basePath}'), ); expect(server.variables, hasLength(2)); expect(server.variables.containsKey('environment'), isTrue); expect(server.variables['environment']!.defaultValue, equals('api')); expect(server.variables['environment']!.enumValues, hasLength(3)); }); test('parses complex request body', () { final json = { 'openapi': '3.0.3', 'info': {'title': 'Test', 'version': '1.0.0'}, 'paths': { '/users': { 'post': { 'summary': 'Create user', 'requestBody': { 'description': 'User to create', 'required': true, 'content': { 'application/json': { 'schema': { r'$ref': '#/components/schemas/User', }, 'examples': { 'user1': { 'summary': 'Example user', 'value': { 'name': 'John Doe', 'email': 'john@example.com', }, }, }, }, 'application/xml': { 'schema': { r'$ref': '#/components/schemas/User', }, }, }, }, 'responses': { '201': {'description': 'Created'}, }, }, }, }, 'components': { 'schemas': { 'User': { 'type': 'object', 'properties': { 'name': {'type': 'string'}, 'email': {'type': 'string'}, }, }, }, }, }; final document = SwaggerDocument.fromJson(json); final path = document.findPath('/users', HttpMethod.post)!; expect(path.requestBody, isNotNull); expect(path.requestBody!.required, isTrue); expect(path.requestBody!.content, hasLength(2)); expect( path.requestBody!.content.containsKey('application/json'), isTrue, ); expect( path.requestBody!.content.containsKey('application/xml'), isTrue, ); final jsonContent = path.requestBody!.content['application/json']!; expect(jsonContent.examples, hasLength(1)); expect(jsonContent.examples.containsKey('user1'), isTrue); }); test('parses complex responses with headers and links', () { final json = { 'openapi': '3.0.3', 'info': {'title': 'Test', 'version': '1.0.0'}, 'paths': { '/users/{id}': { 'get': { 'summary': 'Get user', 'parameters': [ { 'name': 'id', 'in': 'path', 'required': true, 'schema': {'type': 'integer'}, }, ], 'responses': { '200': { 'description': 'User found', 'headers': { 'X-Rate-Limit': { 'description': 'Rate limit', 'schema': {'type': 'integer'}, }, }, 'content': { 'application/json': { 'schema': { r'$ref': '#/components/schemas/User', }, }, }, 'links': { 'getUserPosts': { 'operationId': 'getUserPosts', 'parameters': { 'userId': r'$response.body#/id', }, }, }, }, '404': { 'description': 'User not found', }, }, }, }, }, 'components': { 'schemas': { 'User': { 'type': 'object', 'properties': { 'id': {'type': 'integer'}, 'name': {'type': 'string'}, }, }, }, }, }; final document = SwaggerDocument.fromJson(json); final path = document.findPath('/users/{id}', HttpMethod.get)!; expect(path.parameters, hasLength(1)); expect(path.parameters.first.name, equals('id')); expect(path.parameters.first.location, equals(ParameterLocation.path)); expect(path.parameters.first.required, isTrue); expect(path.responses, hasLength(2)); final successResponse = path.responses['200']!; expect(successResponse.headers, hasLength(1)); expect(successResponse.headers.containsKey('X-Rate-Limit'), isTrue); expect(successResponse.links, hasLength(1)); expect(successResponse.links.containsKey('getUserPosts'), isTrue); }); test('parses security schemes and requirements', () { final json = { 'openapi': '3.0.3', 'info': {'title': 'Test', 'version': '1.0.0'}, 'security': [ {'bearerAuth': []}, {'apiKey': []}, ], 'paths': { '/protected': { 'get': { 'summary': 'Protected endpoint', 'security': [ { 'bearerAuth': ['read:users'], }, ], 'responses': { '200': {'description': 'Success'}, }, }, }, }, 'components': { 'securitySchemes': { 'bearerAuth': { 'type': 'http', 'scheme': 'bearer', 'bearerFormat': 'JWT', }, 'apiKey': { 'type': 'apiKey', 'in': 'header', 'name': 'X-API-Key', }, 'oauth2': { 'type': 'oauth2', 'flows': { 'authorizationCode': { 'authorizationUrl': 'https://example.com/oauth/authorize', 'tokenUrl': 'https://example.com/oauth/token', 'scopes': { 'read:users': 'Read user data', 'write:users': 'Write user data', }, }, }, }, }, }, }; final document = SwaggerDocument.fromJson(json); expect(document.security, hasLength(2)); expect(document.components.securitySchemes, hasLength(3)); final bearerAuth = document.components.securitySchemes['bearerAuth']!; expect(bearerAuth.type, equals(SecuritySchemeType.http)); expect(bearerAuth.scheme, equals('bearer')); expect(bearerAuth.bearerFormat, equals('JWT')); final apiKey = document.components.securitySchemes['apiKey']!; expect(apiKey.type, equals(SecuritySchemeType.apiKey)); expect(apiKey.location, equals(ApiKeyLocation.header)); expect(apiKey.name, equals('X-API-Key')); final oauth2 = document.components.securitySchemes['oauth2']!; expect(oauth2.type, equals(SecuritySchemeType.oauth2)); expect(oauth2.flows, isNotNull); expect(oauth2.flows!.authorizationCode, isNotNull); expect(oauth2.flows!.authorizationCode!.scopes, hasLength(2)); final path = document.findPath('/protected', HttpMethod.get)!; expect(path.security, hasLength(1)); expect(path.security.first.schemeNames, contains('bearerAuth')); }); }); group('Schema Validation', () { test('parses allOf composition', () { final json = { 'openapi': '3.0.3', 'info': {'title': 'Test', 'version': '1.0.0'}, 'paths': {}, 'components': { 'schemas': { 'Pet': { 'type': 'object', 'properties': { 'name': {'type': 'string'}, }, 'required': ['name'], }, 'Dog': { 'allOf': [ {r'$ref': '#/components/schemas/Pet'}, { 'type': 'object', 'properties': { 'breed': {'type': 'string'}, }, 'required': ['breed'], }, ], }, }, }, }; final document = SwaggerDocument.fromJson(json); expect(document.models, hasLength(2)); expect(document.models.containsKey('Pet'), isTrue); expect(document.models.containsKey('Dog'), isTrue); final dog = document.models['Dog']!; expect(dog.name, equals('Dog')); // allOf 处理逻辑会在实际实现中更复杂 }); test('parses oneOf and anyOf', () { final json = { 'openapi': '3.0.3', 'info': {'title': 'Test', 'version': '1.0.0'}, 'paths': {}, 'components': { 'schemas': { 'StringOrNumber': { 'oneOf': [ {'type': 'string'}, {'type': 'number'}, ], }, 'FlexibleType': { 'anyOf': [ {'type': 'string'}, {'type': 'integer'}, {'type': 'boolean'}, ], }, }, }, }; final document = SwaggerDocument.fromJson(json); expect(document.models, hasLength(2)); expect(document.models.containsKey('StringOrNumber'), isTrue); expect(document.models.containsKey('FlexibleType'), isTrue); }); test('parses discriminator', () { final json = { 'openapi': '3.0.3', 'info': {'title': 'Test', 'version': '1.0.0'}, 'paths': {}, 'components': { 'schemas': { 'Pet': { 'type': 'object', 'discriminator': { 'propertyName': 'petType', 'mapping': { 'dog': '#/components/schemas/Dog', 'cat': '#/components/schemas/Cat', }, }, 'properties': { 'petType': {'type': 'string'}, 'name': {'type': 'string'}, }, 'required': ['petType', 'name'], }, 'Dog': { 'allOf': [ {r'$ref': '#/components/schemas/Pet'}, { 'type': 'object', 'properties': { 'breed': {'type': 'string'}, }, }, ], }, 'Cat': { 'allOf': [ {r'$ref': '#/components/schemas/Pet'}, { 'type': 'object', 'properties': { 'color': {'type': 'string'}, }, }, ], }, }, }, }; final document = SwaggerDocument.fromJson(json); expect(document.models, hasLength(3)); final pet = document.models['Pet']!; expect(pet.name, equals('Pet')); // discriminator 处理逻辑会在实际实现中更复杂 }); }); group('Error Handling', () { test('handles missing required fields gracefully', () { final json = { 'openapi': '3.0.3', // Missing info object 'paths': {}, }; expect( () => SwaggerDocument.fromJson(json), throwsA(isA()), ); }); test('handles invalid OpenAPI version', () { final json = { 'openapi': '2.0', // Invalid version 'info': {'title': 'Test', 'version': '1.0.0'}, 'paths': {}, }; // Should still parse but might have warnings expect(() => SwaggerDocument.fromJson(json), returnsNormally); }); test('handles malformed paths', () { final json = { 'openapi': '3.0.3', 'info': {'title': 'Test', 'version': '1.0.0'}, 'paths': { '/valid': { 'get': { 'responses': { '200': {'description': 'OK'}, }, }, }, '/invalid': { 'invalidMethod': { 'responses': { '200': {'description': 'OK'}, }, }, }, }, }; final document = SwaggerDocument.fromJson(json); // Should parse valid paths and skip invalid ones expect(document.paths.length, greaterThanOrEqualTo(1)); }); test('handles circular references', () { final json = { 'openapi': '3.0.3', 'info': {'title': 'Test', 'version': '1.0.0'}, 'paths': {}, 'components': { 'schemas': { 'Node': { 'type': 'object', 'properties': { 'value': {'type': 'string'}, 'children': { 'type': 'array', 'items': {r'$ref': '#/components/schemas/Node'}, }, }, }, }, }, }; // Should handle circular references without infinite recursion expect(() => SwaggerDocument.fromJson(json), returnsNormally); final document = SwaggerDocument.fromJson(json); expect(document.models.containsKey('Node'), isTrue); }); }); group('Edge Cases', () { test('handles empty document', () { final json = { 'openapi': '3.0.3', 'info': {'title': 'Empty API', 'version': '1.0.0'}, 'paths': {}, }; final document = SwaggerDocument.fromJson(json); expect(document.title, equals('Empty API')); expect(document.paths, isEmpty); expect(document.models, isEmpty); }); test('handles very large documents', () { final paths = {}; final schemas = {}; // Create a large number of paths and schemas for (var i = 0; i < 1000; i++) { paths['/resource$i'] = { 'get': { 'summary': 'Get resource $i', 'responses': { '200': {'description': 'Success'}, }, }, }; schemas['Model$i'] = { 'type': 'object', 'properties': { 'id': {'type': 'integer'}, 'name': {'type': 'string'}, }, }; } final json = { 'openapi': '3.0.3', 'info': {'title': 'Large API', 'version': '1.0.0'}, 'paths': paths, 'components': {'schemas': schemas}, }; final stopwatch = Stopwatch()..start(); final document = SwaggerDocument.fromJson(json); stopwatch.stop(); expect(document.paths.length, greaterThan(500)); expect(document.models.length, greaterThan(500)); expect( stopwatch.elapsedMilliseconds, lessThan(10000), ); // Should complete within 10 seconds }); test('handles unicode and special characters', () { final json = { 'openapi': '3.0.3', 'info': { 'title': 'API with 中文 and émojis 🚀', 'version': '1.0.0', 'description': 'Supports unicode: αβγ, 日本語, العربية', }, 'paths': { '/测试': { 'get': { 'summary': 'Test with unicode path', 'responses': { '200': {'description': 'Success'}, }, }, }, }, }; final document = SwaggerDocument.fromJson(json); expect(document.title, contains('中文')); expect(document.title, contains('🚀')); expect(document.description, contains('日本語')); expect( document.paths.keys.any((key) => key.pattern == '/测试'), isTrue, ); }); }); }); } // 忽略测试构造数据的类型推断告警,便于保持示例简洁 // ignore_for_file: inference_failure_on_collection_literal