477 lines
14 KiB
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));
|
|
});
|
|
});
|
|
}
|