import 'package:swagger_generator_flutter/core/error_reporter.dart'; import 'package:swagger_generator_flutter/core/models.dart'; import 'package:swagger_generator_flutter/validators/enhanced_validator.dart'; import 'package:test/test.dart'; void main() { group('EnhancedValidator', () { late EnhancedValidator validator; setUp(() { validator = EnhancedValidator( ); }); test('validates valid document successfully', () { const document = SwaggerDocument( title: 'Test API', version: '1.0.0', description: 'A test API', servers: [ ApiServer( url: 'https://api.example.com', description: 'Production server', ), ], paths: { '/users': ApiPath( path: '/users', method: HttpMethod.get, summary: 'Get users', description: 'Retrieve all users', operationId: 'getUsers', tags: ['users'], parameters: [], responses: { '200': ApiResponse( code: '200', description: 'Success', content: { 'application/json': ApiMediaType( schema: {'type': 'array'}, ), }, ), }, ), }, models: {}, controllers: {}, ); final isValid = validator.validateDocument(document); expect(isValid, true); expect(validator.errorReporter.hasErrorsOrCritical, false); }); test('detects missing required fields', () { const document = SwaggerDocument( title: '', // Missing title version: '', // Missing version description: '', paths: {}, // Empty paths models: {}, controllers: {}, ); final isValid = validator.validateDocument(document); expect(isValid, false); expect(validator.errorReporter.hasErrorsOrCritical, true); final errors = validator.errorReporter.errors; expect(errors.any((e) => e.id == 'MISSING_INFO_TITLE'), true); expect(errors.any((e) => e.id == 'MISSING_INFO_VERSION'), true); expect(errors.any((e) => e.id == 'EMPTY_PATHS'), true); }); test('validates path parameters correctly', () { const document = SwaggerDocument( title: 'Test API', version: '1.0.0', description: 'Test', paths: { '/users/{id}': ApiPath( path: '/users/{id}', method: HttpMethod.get, summary: 'Get user', description: 'Get user by ID', operationId: 'getUser', tags: ['users'], parameters: [ // Missing path parameter declaration for 'id' ], responses: { '200': ApiResponse( code: '200', description: 'Success', ), }, ), }, models: {}, controllers: {}, ); final isValid = validator.validateDocument(document); expect(isValid, false); final errors = validator.errorReporter.errors; expect(errors.any((e) => e.id == 'UNDECLARED_PATH_PARAMETER'), true); }); test('validates path parameter requirements', () { const document = SwaggerDocument( title: 'Test API', version: '1.0.0', description: 'Test', paths: { '/users/{id}': ApiPath( path: '/users/{id}', method: HttpMethod.get, summary: 'Get user', description: 'Get user by ID', operationId: 'getUser', tags: ['users'], parameters: [ ApiParameter( name: 'id', location: ParameterLocation.path, required: false, // Path parameters must be required type: PropertyType.integer, description: 'User ID', ), ], responses: { '200': ApiResponse( code: '200', description: 'Success', ), }, ), }, models: {}, controllers: {}, ); final isValid = validator.validateDocument(document); expect(isValid, false); final errors = validator.errorReporter.errors; expect(errors.any((e) => e.id == 'PATH_PARAMETER_NOT_REQUIRED'), true); }); test('validates security schemes', () { const document = SwaggerDocument( title: 'Test API', version: '1.0.0', description: 'Test', components: ApiComponents( securitySchemes: { 'apiKey': ApiSecurityScheme( type: SecuritySchemeType.apiKey, description: 'API Key', name: '', // Missing name location: ApiKeyLocation.header, ), 'bearer': ApiSecurityScheme( type: SecuritySchemeType.http, description: 'Bearer token', scheme: '', // Missing scheme ), }, ), paths: { '/test': ApiPath( path: '/test', method: HttpMethod.get, summary: 'Test', description: 'Test endpoint', operationId: 'test', tags: [], parameters: [], responses: { '200': ApiResponse( code: '200', description: 'Success', ), }, ), }, models: {}, controllers: {}, ); final isValid = validator.validateDocument(document); expect(isValid, false); final errors = validator.errorReporter.errors; expect(errors.any((e) => e.id == 'MISSING_API_KEY_NAME'), true); expect(errors.any((e) => e.id == 'MISSING_HTTP_SCHEME'), true); }); test('generates warnings for missing optional fields', () { const document = SwaggerDocument( title: 'Test API', version: '1.0.0', description: '', // Missing description paths: { '/test': ApiPath( path: '/test', method: HttpMethod.get, summary: '', // Missing summary description: 'Test endpoint', operationId: '', // Missing operationId tags: [], parameters: [], responses: { '200': ApiResponse( code: '200', description: '', // Missing response description ), }, ), }, models: {}, controllers: {}, ); final isValid = validator.validateDocument(document); expect(isValid, true); // Should be valid but with warnings final warnings = validator.errorReporter.getErrorsBySeverity(ErrorSeverity.warning); expect(warnings.isNotEmpty, true); expect(warnings.any((w) => w.id == 'MISSING_INFO_DESCRIPTION'), true); expect(warnings.any((w) => w.id == 'MISSING_SERVERS'), true); expect(warnings.any((w) => w.id == 'MISSING_OPERATION_ID'), true); expect(warnings.any((w) => w.id == 'MISSING_RESPONSE_DESCRIPTION'), true); }); test('validates responses correctly', () { const document = SwaggerDocument( title: 'Test API', version: '1.0.0', description: 'Test', paths: { '/test': ApiPath( path: '/test', method: HttpMethod.get, summary: 'Test', description: 'Test endpoint', operationId: 'test', tags: [], parameters: [], responses: {}, // Missing responses ), }, models: {}, controllers: {}, ); final isValid = validator.validateDocument(document); expect(isValid, false); final errors = validator.errorReporter.errors; expect(errors.any((e) => e.id == 'MISSING_OPERATION_RESPONSES'), true); }); test('checks for best practices', () { const document = SwaggerDocument( title: 'Test API', version: '1.0.0', description: 'Test API', servers: [ApiServer(url: 'https://api.example.com')], paths: { '/test': ApiPath( path: '/test', method: HttpMethod.get, summary: 'Test', description: 'Test endpoint', operationId: 'test', tags: [], // No tags parameters: [], responses: { '200': ApiResponse( code: '200', description: 'Success', ), // No error responses }, ), }, models: {}, controllers: {}, ); final isValid = validator.validateDocument(document); expect(isValid, true); final infos = validator.errorReporter.getErrorsBySeverity(ErrorSeverity.info); expect(infos.any((i) => i.id == 'NO_OPERATION_TAGS'), true); expect(infos.any((i) => i.id == 'NO_ERROR_RESPONSE'), true); }); test('validates large schemas', () { // Create a model with many properties final properties = {}; for (var i = 0; i < 25; i++) { properties['property$i'] = ApiProperty( name: 'property$i', type: PropertyType.string, description: 'Property $i', required: false, ); } final largeModel = ApiModel( name: 'LargeModel', description: 'A model with many properties', properties: properties, required: [], ); final document = SwaggerDocument( title: 'Test API', version: '1.0.0', description: 'Test API', servers: [const ApiServer(url: 'https://api.example.com')], components: ApiComponents( schemas: {'LargeModel': largeModel}, securitySchemes: {}, ), paths: { '/test': const ApiPath( path: '/test', method: HttpMethod.get, summary: 'Test', description: 'Test endpoint', operationId: 'test', tags: ['test'], parameters: [], responses: { '200': ApiResponse( code: '200', description: 'Success', ), }, ), }, models: {'LargeModel': largeModel}, controllers: {}, security: [], ); final isValid = validator.validateDocument(document); expect(isValid, true); final infos = validator.errorReporter.getErrorsBySeverity(ErrorSeverity.info); expect(infos.any((i) => i.id == 'LARGE_SCHEMA_OBJECT'), true); }); test('generates detailed error report', () { const document = SwaggerDocument( title: '', version: '', description: '', paths: {}, models: {}, controllers: {}, ); validator.validateDocument(document); final report = validator.errorReporter.generateReport(); expect(report, isNotEmpty); expect(report, contains('Error Summary')); expect(report, contains('Missing API Title')); expect(report, contains('Missing API Version')); expect(report, contains('Empty Paths Object')); }); test('generates JSON error report', () { const document = SwaggerDocument( title: '', version: '', description: '', paths: {}, models: {}, controllers: {}, ); validator.validateDocument(document); final jsonReport = validator.errorReporter.generateJsonReport(); expect(jsonReport, isNotEmpty); expect(jsonReport, contains('"timestamp"')); expect(jsonReport, contains('"summary"')); expect(jsonReport, contains('"errors"')); }); test('strict mode validation', () { final strictValidator = EnhancedValidator( includeWarnings: false, ); const document = SwaggerDocument( title: 'Test API', version: '1.0.0', description: '', // Missing description paths: { '/test': ApiPath( path: '/test', method: HttpMethod.get, summary: 'Test', description: 'Test endpoint', operationId: 'test', tags: ['test'], parameters: [], responses: { '200': ApiResponse( code: '200', description: 'Success', ), }, ), }, models: {}, controllers: {}, ); final isValid = strictValidator.validateDocument(document); expect(isValid, true); // Should have no warnings in strict mode with includeWarnings: false final warnings = strictValidator.errorReporter .getErrorsBySeverity(ErrorSeverity.warning); expect(warnings.length, equals(0)); }); }); }