import 'package:swagger_generator_flutter/core/models.dart'; import 'package:swagger_generator_flutter/generators/optimized_retrofit_generator.dart'; import 'package:swagger_generator_flutter/generators/performance_generator.dart'; import 'package:swagger_generator_flutter/generators/retrofit_api_generator.dart'; import 'package:test/test.dart'; void main() { group('Comprehensive Generator Tests', () { late SwaggerDocument testDocument; setUp(() { testDocument = const SwaggerDocument( title: 'Test API', version: '1.0.0', description: 'A comprehensive test API', servers: [ ApiServer( url: 'https://api.example.com', description: 'Production server', ), ], components: ApiComponents( schemas: {}, securitySchemes: { 'bearerAuth': const ApiSecurityScheme( type: SecuritySchemeType.http, description: 'Bearer token', scheme: 'bearer', bearerFormat: 'JWT', ), 'apiKey': const ApiSecurityScheme( type: SecuritySchemeType.apiKey, description: 'API Key', name: 'X-API-Key', location: ApiKeyLocation.header, ), }, ), paths: { '/users': const ApiPath( path: '/users', method: HttpMethod.get, summary: 'Get all users', description: 'Retrieve a list of all users', operationId: 'getUsers', tags: ['users'], parameters: [ ApiParameter( name: 'page', location: ParameterLocation.query, required: false, type: PropertyType.integer, description: 'Page number', ), ApiParameter( name: 'limit', location: ParameterLocation.query, required: false, type: PropertyType.integer, description: 'Items per page', ), ], responses: { '200': const ApiResponse( code: '200', description: 'Successful response', content: { 'application/json': const ApiMediaType( schema: { 'type': 'array', 'items': { '\$ref': '#/components/schemas/User', }, }, ), }, ), '400': const ApiResponse( code: '400', description: 'Bad request', ), '401': const ApiResponse( code: '401', description: 'Unauthorized', ), }, security: [ ApiSecurityRequirement( requirements: {'bearerAuth': []}, ), ], ), '/users/{id}': const ApiPath( path: '/users/{id}', method: HttpMethod.get, summary: 'Get user by ID', description: 'Retrieve a specific user by their ID', operationId: 'getUserById', tags: ['users'], parameters: [ ApiParameter( name: 'id', location: ParameterLocation.path, required: true, type: PropertyType.integer, description: 'User ID', ), ], responses: { '200': const ApiResponse( code: '200', description: 'User found', content: { 'application/json': const ApiMediaType( schema: { '\$ref': '#/components/schemas/User', }, ), }, ), '404': const ApiResponse( code: '404', description: 'User not found', ), }, ), '/users/create': const ApiPath( path: '/users/create', method: HttpMethod.post, summary: 'Create user', description: 'Create a new user', operationId: 'createUser', tags: ['users'], parameters: [], requestBody: ApiRequestBody( description: 'User data', required: true, content: { 'application/json': const ApiMediaType( schema: { '\$ref': '#/components/schemas/CreateUserRequest', }, ), }, ), responses: { '201': const ApiResponse( code: '201', description: 'User created', content: { 'application/json': const ApiMediaType( schema: { '\$ref': '#/components/schemas/User', }, ), }, ), '400': const ApiResponse( code: '400', description: 'Invalid input', ), }, ), '/files/upload': const ApiPath( path: '/files/upload', method: HttpMethod.post, summary: 'Upload file', description: 'Upload a file to the server', operationId: 'uploadFile', tags: ['files'], parameters: [], requestBody: ApiRequestBody( description: 'File to upload', required: true, content: { 'multipart/form-data': const ApiMediaType( schema: { 'type': 'object', 'properties': { 'file': { 'type': 'string', 'format': 'binary', }, 'description': { 'type': 'string', }, }, }, ), }, ), responses: { '200': const ApiResponse( code: '200', description: 'File uploaded successfully', content: { 'application/json': const ApiMediaType( schema: { '\$ref': '#/components/schemas/FileUploadResult', }, ), }, ), }, ), }, models: { 'User': const ApiModel( name: 'User', description: 'User model', properties: { 'id': const ApiProperty( name: 'id', type: PropertyType.integer, description: 'User ID', required: true, ), 'name': const ApiProperty( name: 'name', type: PropertyType.string, description: 'User name', required: true, ), 'email': const ApiProperty( name: 'email', type: PropertyType.string, description: 'User email', required: true, ), 'createdAt': const ApiProperty( name: 'createdAt', type: PropertyType.string, description: 'Creation timestamp', required: false, ), }, required: ['id', 'name', 'email'], ), 'CreateUserRequest': const ApiModel( name: 'CreateUserRequest', description: 'Request model for creating a user', properties: { 'name': const ApiProperty( name: 'name', type: PropertyType.string, description: 'User name', required: true, ), 'email': const ApiProperty( name: 'email', type: PropertyType.string, description: 'User email', required: true, ), }, required: ['name', 'email'], ), 'FileUploadResult': const ApiModel( name: 'FileUploadResult', description: 'Result of file upload', properties: { 'url': const ApiProperty( name: 'url', type: PropertyType.string, description: 'File URL', required: true, ), 'filename': const ApiProperty( name: 'filename', type: PropertyType.string, description: 'Original filename', required: true, ), 'size': const ApiProperty( name: 'size', type: PropertyType.integer, description: 'File size in bytes', required: true, ), }, required: ['url', 'filename', 'size'], ), }, controllers: {}, security: [ ApiSecurityRequirement( requirements: {'bearerAuth': []}, ), ], ); }); group('RetrofitApiGenerator', () { test('generates basic Retrofit API', () { final generator = RetrofitApiGenerator( className: 'TestApiService', splitByTags: false, ); final result = generator.generateFromDocument(testDocument); expect(result, isNotEmpty); expect(result, contains('abstract class TestApiService')); expect(result, contains('@RestApi()')); expect(result, contains('factory TestApiService(Dio dio')); expect(result, contains('@GET(\'/users\')')); expect(result, contains('@POST(\'/users\')')); expect(result, contains('@Path(\'id\')')); expect(result, contains('@Query(\'page\')')); expect(result, contains('@Body()')); }); test('generates split APIs by tags', () { final generator = RetrofitApiGenerator( className: 'ApiService', splitByTags: true, ); final result = generator.generateFromDocument(testDocument); expect(result, isNotEmpty); expect(result, contains('UsersApi')); expect(result, contains('FilesApi')); expect(result, contains('class ApiService')); expect(result, contains('late final UsersApi users')); expect(result, contains('late final FilesApi files')); }); test('handles file upload endpoints', () { final generator = RetrofitApiGenerator(); final result = generator.generateFromDocument(testDocument); expect(result, contains('@POST(\'/files/upload\')')); expect(result, contains('@MultiPart()')); expect(result, contains('MultipartFile')); }); test('generates proper parameter annotations', () { final generator = RetrofitApiGenerator(); final result = generator.generateFromDocument(testDocument); // Path parameters expect(result, contains('@Path(\'id\') int id')); // Query parameters expect(result, contains('@Query(\'page\') int? page')); expect(result, contains('@Query(\'limit\') int? limit')); // Body parameters expect(result, contains('@Body() CreateUserRequest body')); }); test('generates security annotations', () { final generator = RetrofitApiGenerator(); final result = generator.generateFromDocument(testDocument); // Should include headers for authentication expect( result, anyOf([contains('Authorization'), contains('X-API-Key')])); }); }); group('OptimizedRetrofitGenerator', () { test('generates optimized code with base types', () { final generator = OptimizedRetrofitGenerator( className: 'OptimizedApiService', generateModularApis: true, generateBaseResult: true, generatePagination: true, generateFileUpload: true, ); final result = generator.generateFromDocument(testDocument); expect(result, isNotEmpty); expect(result, contains('class BaseResult')); expect(result, contains('class BasePageResult')); expect(result, contains('class FileUploadRequest')); expect(result, contains('class FileUploadResult')); expect(result, contains('class ApiUtils')); }); test('generates modular APIs', () { final generator = OptimizedRetrofitGenerator( generateModularApis: true, ); final result = generator.generateFromDocument(testDocument); expect(result, contains('class UsersApi')); expect(result, contains('class FilesApi')); expect(result, contains('class ApiService')); }); test('generates single API when modular is disabled', () { final generator = OptimizedRetrofitGenerator( generateModularApis: false, className: 'SingleApiService', ); final result = generator.generateFromDocument(testDocument); expect(result, contains('abstract class SingleApiService')); expect(result, isNot(contains('class UsersApi'))); expect(result, isNot(contains('class FilesApi'))); }); test('includes utility methods', () { final generator = OptimizedRetrofitGenerator( generateFileUpload: true, generatePagination: true, ); final result = generator.generateFromDocument(testDocument); expect(result, contains('class ApiUtils')); expect(result, contains('createFileUpload')); expect(result, contains('createPageParam')); }); }); group('PerformanceGenerator', () { test('generates code with performance tracking', () async { final generator = PerformanceGenerator( maxConcurrency: 2, enableCaching: true, enableParallel: true, ); final result = await generator.generateFromDocument(testDocument); expect(result, isNotEmpty); expect(result, contains('Generated API for Test API')); expect(result, contains('class User')); expect(result, contains('CommonApi')); final stats = generator.getStats(); expect(stats.totalTasks, greaterThan(0)); expect(stats.completedTasks, greaterThan(0)); }); test('caching improves performance', () async { final generator = PerformanceGenerator( enableCaching: true, ); // First generation final stopwatch1 = Stopwatch()..start(); await generator.generateFromDocument(testDocument); stopwatch1.stop(); // Second generation (should use cache) final stopwatch2 = Stopwatch()..start(); await generator.generateFromDocument(testDocument); stopwatch2.stop(); // Second should be faster due to caching expect(stopwatch2.elapsedMicroseconds, lessThan(stopwatch1.elapsedMicroseconds)); final cacheStats = generator.getCacheStats(); expect(cacheStats.hits, greaterThan(0)); }); }); group('Code Quality', () { test('generated code is valid Dart syntax', () { final generator = RetrofitApiGenerator(); final result = generator.generateFromDocument(testDocument); // Basic syntax checks expect(result, isNot(contains(';;'))); // No double semicolons expect(result, isNot(contains(',,'))); // No double commas expect(result, isNot(contains(' '))); // No double spaces (basic formatting) // Check for proper imports expect(result, contains('import \'package:dio/dio.dart\';')); expect(result, contains('import \'package:retrofit/retrofit.dart\';')); // Check for proper class structure final classMatches = RegExp(r'class \w+').allMatches(result); final abstractClassMatches = RegExp(r'abstract class \w+').allMatches(result); expect( classMatches.length + abstractClassMatches.length, greaterThan(0)); }); test('handles special characters in names', () { const specialDocument = SwaggerDocument( title: 'API with Special-Characters_and.dots', version: '1.0.0', description: 'Test API', servers: [], components: ApiComponents(schemas: {}, securitySchemes: {}), paths: { '/special-endpoint_with.dots': const ApiPath( path: '/special-endpoint_with.dots', method: HttpMethod.get, summary: 'Special endpoint', description: 'Endpoint with special characters', operationId: 'getSpecialEndpoint', tags: ['special-tag_with.dots'], parameters: [], responses: { '200': const ApiResponse( code: '200', description: 'Success', ), }, ), }, models: {}, controllers: {}, security: [], ); final generator = RetrofitApiGenerator(); final result = generator.generateFromDocument(specialDocument); expect(result, isNotEmpty); // Should handle special characters in class names expect(result, contains('class')); }); test('generates proper JSON annotations', () { final generator = OptimizedRetrofitGenerator( generateBaseResult: true, ); final result = generator.generateFromDocument(testDocument); expect(result, contains('@JsonSerializable()')); expect(result, contains('fromJson')); expect(result, contains('toJson')); expect(result, contains('_\$')); }); test('handles nullable and required fields correctly', () { final generator = RetrofitApiGenerator(); final result = generator.generateFromDocument(testDocument); // Required path parameters should not be nullable expect(result, contains('@Path(\'id\') int id')); // Optional query parameters should be nullable expect(result, contains('@Query(\'page\') int? page')); expect(result, contains('@Query(\'limit\') int? limit')); }); }); group('Error Handling', () { test('handles empty document gracefully', () { const emptyDocument = SwaggerDocument( title: 'Empty API', version: '1.0.0', description: 'Empty test API', servers: [], components: ApiComponents(schemas: {}, securitySchemes: {}), paths: {}, models: {}, controllers: {}, security: [], ); final generator = RetrofitApiGenerator(); final result = generator.generateFromDocument(emptyDocument); expect(result, isNotEmpty); expect(result, contains('Empty API')); // Should still generate basic structure even with no paths }); test('handles missing operation IDs', () { const documentWithoutOperationIds = SwaggerDocument( title: 'Test API', version: '1.0.0', description: 'Test', servers: [], components: ApiComponents(schemas: {}, securitySchemes: {}), paths: { '/test': const ApiPath( path: '/test', method: HttpMethod.get, summary: 'Test endpoint', description: 'Test', operationId: '', // Empty operation ID tags: [], parameters: [], responses: { '200': const ApiResponse( code: '200', description: 'Success', ), }, ), }, models: {}, controllers: {}, security: [], ); final generator = RetrofitApiGenerator(); expect( () => generator.generateFromDocument(documentWithoutOperationIds), returnsNormally); }); }); }); }