swagger_generator_flutter/tests/enhanced_validator_test.dart

477 lines
14 KiB
Dart

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(
includeWarnings: true,
);
});
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',
),
],
components: ApiComponents(
schemas: {},
securitySchemes: {},
),
paths: {
'/users': const ApiPath(
path: '/users',
method: HttpMethod.get,
summary: 'Get users',
description: 'Retrieve all users',
operationId: 'getUsers',
tags: ['users'],
parameters: [],
responses: {
'200': const ApiResponse(
code: '200',
description: 'Success',
content: {
'application/json': const ApiMediaType(
schema: {'type': 'array'},
),
},
),
},
),
},
models: {},
controllers: {},
security: [],
);
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: '',
servers: [],
components: ApiComponents(schemas: {}, securitySchemes: {}),
paths: {}, // Empty paths
models: {},
controllers: {},
security: [],
);
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',
servers: [],
components: ApiComponents(schemas: {}, securitySchemes: {}),
paths: {
'/users/{id}': const 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': const ApiResponse(
code: '200',
description: 'Success',
),
},
),
},
models: {},
controllers: {},
security: [],
);
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',
servers: [],
components: ApiComponents(schemas: {}, securitySchemes: {}),
paths: {
'/users/{id}': const 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': const ApiResponse(
code: '200',
description: 'Success',
),
},
),
},
models: {},
controllers: {},
security: [],
);
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',
servers: [],
components: ApiComponents(
schemas: {},
securitySchemes: {
'apiKey': const ApiSecurityScheme(
type: SecuritySchemeType.apiKey,
description: 'API Key',
name: '', // Missing name
location: ApiKeyLocation.header,
),
'bearer': const ApiSecurityScheme(
type: SecuritySchemeType.http,
description: 'Bearer token',
scheme: '', // Missing scheme
),
},
),
paths: {
'/test': const ApiPath(
path: '/test',
method: HttpMethod.get,
summary: 'Test',
description: 'Test endpoint',
operationId: 'test',
tags: [],
parameters: [],
responses: {
'200': const ApiResponse(
code: '200',
description: 'Success',
),
},
),
},
models: {},
controllers: {},
security: [],
);
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
servers: [], // Missing servers
components: ApiComponents(schemas: {}, securitySchemes: {}),
paths: {
'/test': const ApiPath(
path: '/test',
method: HttpMethod.get,
summary: '', // Missing summary
description: 'Test endpoint',
operationId: '', // Missing operationId
tags: [],
parameters: [],
responses: {
'200': const ApiResponse(
code: '200',
description: '', // Missing response description
),
},
),
},
models: {},
controllers: {},
security: [],
);
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',
servers: [],
components: ApiComponents(schemas: {}, securitySchemes: {}),
paths: {
'/test': const ApiPath(
path: '/test',
method: HttpMethod.get,
summary: 'Test',
description: 'Test endpoint',
operationId: 'test',
tags: [],
parameters: [],
responses: {}, // Missing responses
),
},
models: {},
controllers: {},
security: [],
);
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')],
components: ApiComponents(schemas: {}, securitySchemes: {}),
paths: {
'/test': const ApiPath(
path: '/test',
method: HttpMethod.get,
summary: 'Test',
description: 'Test endpoint',
operationId: 'test',
tags: [], // No tags
parameters: [],
responses: {
'200': const ApiResponse(
code: '200',
description: 'Success',
),
// No error responses
},
),
},
models: {},
controllers: {},
security: [],
);
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 = <String, ApiProperty>{};
for (int 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': const 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: '',
servers: [],
components: ApiComponents(schemas: {}, securitySchemes: {}),
paths: {},
models: {},
controllers: {},
security: [],
);
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: '',
servers: [],
components: ApiComponents(schemas: {}, securitySchemes: {}),
paths: {},
models: {},
controllers: {},
security: [],
);
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
servers: [], // Missing servers
components: ApiComponents(schemas: {}, securitySchemes: {}),
paths: {
'/test': const ApiPath(
path: '/test',
method: HttpMethod.get,
summary: 'Test',
description: 'Test endpoint',
operationId: 'test',
tags: ['test'],
parameters: [],
responses: {
'200': const ApiResponse(
code: '200',
description: 'Success',
),
},
),
},
models: {},
controllers: {},
security: [],
);
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));
});
});
}