swagger_generator_flutter/tests/integration_test.dart

558 lines
18 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'dart:convert';
import 'dart:io';
import 'package:swagger_generator_flutter/core/error_reporter.dart';
import 'package:swagger_generator_flutter/core/performance_parser.dart';
import 'package:swagger_generator_flutter/generators/retrofit_api_generator.dart';
import 'package:swagger_generator_flutter/validators/enhanced_validator.dart';
import 'package:test/test.dart';
void main() {
group('Integration Tests', () {
group('End-to-End Workflow', () {
test('complete workflow from JSON to generated code', () async {
// 1. 准备测试数据
final testApiJson = {
'openapi': '3.0.3',
'info': {
'title': 'Integration Test API',
'version': '1.0.0',
'description': 'API for integration testing',
},
'servers': [
{
'url': 'https://api.example.com',
'description': 'Production server',
},
],
'paths': {
'/users': {
'get': {
'summary': 'Get users',
'operationId': 'getUsers',
'tags': ['users'],
'parameters': [
{
'name': 'page',
'in': 'query',
'required': false,
'schema': {'type': 'integer', 'default': 1},
'description': 'Page number',
},
{
'name': 'limit',
'in': 'query',
'required': false,
'schema': {'type': 'integer', 'default': 20},
'description': 'Items per page',
},
],
'responses': {
'200': {
'description': 'Success',
'content': {
'application/json': {
'schema': {
'type': 'object',
'properties': {
'data': {
'type': 'array',
'items': {
r'$ref': '#/components/schemas/User',
},
},
'total': {'type': 'integer'},
'page': {'type': 'integer'},
'limit': {'type': 'integer'},
},
},
},
},
},
'400': {
'description': 'Bad request',
},
},
},
'post': {
'summary': 'Create user',
'operationId': 'createUser',
'tags': ['users'],
'requestBody': {
'required': true,
'content': {
'application/json': {
'schema': {
r'$ref': '#/components/schemas/CreateUserRequest',
},
},
},
},
'responses': {
'201': {
'description': 'User created',
'content': {
'application/json': {
'schema': {
r'$ref': '#/components/schemas/User',
},
},
},
},
'400': {
'description': 'Invalid input',
},
},
},
},
'/users/{id}': {
'get': {
'summary': 'Get user by ID',
'operationId': 'getUserById',
'tags': ['users'],
'parameters': [
{
'name': 'id',
'in': 'path',
'required': true,
'schema': {'type': 'integer'},
'description': 'User ID',
},
],
'responses': {
'200': {
'description': 'User found',
'content': {
'application/json': {
'schema': {
r'$ref': '#/components/schemas/User',
},
},
},
},
'404': {
'description': 'User not found',
},
},
},
},
'/files/upload': {
'post': {
'summary': 'Upload file',
'operationId': 'uploadFile',
'tags': ['files'],
'requestBody': {
'required': true,
'content': {
'multipart/form-data': {
'schema': {
'type': 'object',
'properties': {
'file': {
'type': 'string',
'format': 'binary',
},
'description': {
'type': 'string',
},
},
'required': ['file'],
},
},
},
},
'responses': {
'200': {
'description': 'File uploaded',
'content': {
'application/json': {
'schema': {
r'$ref': '#/components/schemas/FileUploadResult',
},
},
},
},
},
},
},
},
'components': {
'schemas': {
'User': {
'type': 'object',
'properties': {
'id': {
'type': 'integer',
'format': 'int64',
},
'name': {
'type': 'string',
'maxLength': 100,
},
'email': {
'type': 'string',
'format': 'email',
},
'createdAt': {
'type': 'string',
'format': 'date-time',
},
},
'required': ['id', 'name', 'email'],
},
'CreateUserRequest': {
'type': 'object',
'properties': {
'name': {
'type': 'string',
'maxLength': 100,
},
'email': {
'type': 'string',
'format': 'email',
},
},
'required': ['name', 'email'],
},
'FileUploadResult': {
'type': 'object',
'properties': {
'url': {
'type': 'string',
'format': 'uri',
},
'filename': {
'type': 'string',
},
'size': {
'type': 'integer',
},
'contentType': {
'type': 'string',
},
},
'required': ['url', 'filename', 'size'],
},
},
'securitySchemes': {
'bearerAuth': {
'type': 'http',
'scheme': 'bearer',
'bearerFormat': 'JWT',
},
},
},
'security': [
{
'bearerAuth': [],
},
],
};
final jsonString = jsonEncode(testApiJson);
// 2. 解析 JSON 为 SwaggerDocument
final parser = PerformanceParser(
config: const ParseConfig(
enablePerformanceStats: true,
enableParallelParsing: false, // 禁用并行解析避免类型转换问题
),
);
final document = await parser.parseDocument(jsonString);
// 验证解析结果
expect(document.title, equals('Integration Test API'));
expect(document.version, equals('1.0.0'));
expect(document.paths.length, equals(3));
expect(document.models.length, equals(3));
expect(document.servers.length, equals(1));
// 检查解析性能
final parseStats = parser.lastStats;
expect(parseStats, isNotNull);
expect(parseStats!.totalTime.inMilliseconds, lessThan(5000));
// 3. 验证文档
final validator = EnhancedValidator(
);
final isValid = validator.validateDocument(document);
expect(isValid, isTrue);
final errors = validator.errorReporter.errors;
final criticalErrors = errors
.where((e) =>
e.severity == ErrorSeverity.error ||
e.severity == ErrorSeverity.critical,)
.toList();
expect(criticalErrors, isEmpty,
reason:
'Document should not have critical errors: ${criticalErrors.map((e) => e.title).join(", ")}',);
// 4. 生成 Retrofit API 代码
final retrofitGenerator = RetrofitApiGenerator(
className: 'IntegrationTestApi',
);
final retrofitCode = retrofitGenerator.generateFromDocument(document);
// 验证生成的代码
expect(retrofitCode, isNotEmpty);
expect(retrofitCode, contains('IntegrationTestApi'));
expect(retrofitCode, contains("@GET('/users')"));
expect(retrofitCode, contains("@POST('/users')"));
expect(retrofitCode, contains("@GET('/users/{id}')"));
expect(retrofitCode, contains("@POST('/files/upload')"));
expect(retrofitCode, contains("@Path('id')"));
expect(retrofitCode, contains("@Query('page')"));
expect(retrofitCode, contains('@MultiPart()'));
// 5. 性能验证
print('Integration Test Performance Summary:');
print(' Parse Time: ${parseStats.totalTime.inMilliseconds}ms');
print(
' Document Size: ${(jsonString.length / 1024).toStringAsFixed(2)}KB',);
print(' Paths Parsed: ${parseStats.pathCount}');
print(' Schemas Parsed: ${parseStats.schemaCount}');
print(
' Retrofit Code Size: ${(retrofitCode.length / 1024).toStringAsFixed(2)}KB',);
// 验证性能指标
expect(
parseStats.totalTime.inMilliseconds, lessThan(2000),); // 解析应在2秒内完成
expect(retrofitCode.length, greaterThan(1000)); // 应生成足够的代码
});
test('handles real project swagger.json', () async {
final file = File('swagger.json');
if (!file.existsSync()) {
print(
'swagger.json not found, skipping real project integration test',);
return;
}
final jsonString = await file.readAsString();
print(
'Real project swagger.json size: ${(jsonString.length / 1024).toStringAsFixed(2)}KB',);
// 解析
final parser = PerformanceParser(
config: const ParseConfig(
enablePerformanceStats: true,
enableParallelParsing: false, // 禁用并行解析
),
);
final parseStopwatch = Stopwatch()..start();
final document = await parser.parseDocument(jsonString);
parseStopwatch.stop();
expect(document, isNotNull);
expect(document.paths.isNotEmpty, isTrue);
print('Real project parsing results:');
print(' Parse Time: ${parseStopwatch.elapsedMilliseconds}ms');
print(' Paths: ${document.paths.length}');
print(' Models: ${document.models.length}');
print(' Servers: ${document.servers.length}');
// 验证
final validator = EnhancedValidator(includeWarnings: false);
final isValid = validator.validateDocument(document);
final errors =
validator.errorReporter.getErrorsBySeverity(ErrorSeverity.error);
final criticalErrors =
validator.errorReporter.getErrorsBySeverity(ErrorSeverity.critical);
print('Validation results:');
print(' Valid: $isValid');
print(' Errors: ${errors.length}');
print(' Critical: ${criticalErrors.length}');
// 生成代码(使用 RetrofitApiGenerator
final generator = RetrofitApiGenerator(
className: 'OAMobileApiService',
);
final genStopwatch = Stopwatch()..start();
final generatedCode = generator.generateFromDocument(document);
genStopwatch.stop();
expect(generatedCode, isNotEmpty);
expect(generatedCode, contains('OAMobileApiService'));
print('Code generation results:');
print(' Generation Time: ${genStopwatch.elapsedMilliseconds}ms');
print(
' Generated Code Size: ${(generatedCode.length / 1024).toStringAsFixed(2)}KB',);
print(' Generated Lines: ${generatedCode.split('\n').length}');
// 性能要求
expect(parseStopwatch.elapsedMilliseconds, lessThan(15000)); // 15秒内解析完成
expect(genStopwatch.elapsedMilliseconds, lessThan(10000)); // 10秒内生成完成
expect(generatedCode.length, greaterThan(1000)); // 至少生成1KB代码
});
});
group('Error Handling Integration', () {
test('handles malformed JSON gracefully', () async {
const malformedJson =
'{"openapi": "3.0.3", "info": {"title": "Test"'; // 缺少闭合括号
final parser = PerformanceParser();
expect(() => parser.parseDocument(malformedJson),
throwsA(isA<FormatException>()),);
});
test('handles invalid OpenAPI document', () async {
final invalidDoc = {
'openapi': '3.0.3',
'info': {
'title': 'Invalid API',
// 缺少 version
},
'paths': {}, // 空路径
};
final jsonString = jsonEncode(invalidDoc);
final parser = PerformanceParser();
expect(() => parser.parseDocument(jsonString),
throwsA(isA<FormatException>()),);
});
test('validation catches common errors', () async {
final problematicDoc = {
'openapi': '3.0.3',
'info': {
'title': 'Problematic API',
'version': '1.0.0',
},
'paths': {
'/users/{id}': {
'get': {
'summary': 'Get user',
'responses': {
'200': {
'description': 'Success',
},
},
// 缺少路径参数声明
},
},
},
};
final jsonString = jsonEncode(problematicDoc);
final parser = PerformanceParser();
final document = await parser.parseDocument(jsonString);
final validator = EnhancedValidator();
final isValid = validator.validateDocument(document);
expect(isValid, isFalse);
final errors = validator.errorReporter.errors;
expect(errors.any((e) => e.id == 'UNDECLARED_PATH_PARAMETER'), isTrue);
});
});
group('Performance Integration', () {
test('handles large documents efficiently', () async {
// 创建大型文档
final paths = <String, dynamic>{};
final schemas = <String, dynamic>{};
for (var i = 0; i < 200; i++) {
paths['/resource$i'] = {
'get': {
'summary': 'Get resource $i',
'operationId': 'getResource$i',
'tags': ['resources'],
'responses': {
'200': {
'description': 'Success',
'content': {
'application/json': {
'schema': {
r'$ref': '#/components/schemas/Resource$i',
},
},
},
},
},
},
};
schemas['Resource$i'] = {
'type': 'object',
'properties': {
'id': {'type': 'integer'},
'name': {'type': 'string'},
'value$i': {'type': 'string'},
},
'required': ['id', 'name'],
};
}
final largeDoc = {
'openapi': '3.0.3',
'info': {
'title': 'Large API',
'version': '1.0.0',
},
'paths': paths,
'components': {
'schemas': schemas,
},
};
final jsonString = jsonEncode(largeDoc);
print(
'Large document size: ${(jsonString.length / 1024).toStringAsFixed(2)}KB',);
// 测试解析性能
final parser = PerformanceParser(
config: const ParseConfig(
enablePerformanceStats: true,
enableParallelParsing: false, // 禁用并行解析
),
);
final parseStopwatch = Stopwatch()..start();
final document = await parser.parseDocument(jsonString);
parseStopwatch.stop();
expect(document.paths.length, greaterThan(100));
expect(document.models.length, greaterThan(100));
expect(parseStopwatch.elapsedMilliseconds, lessThan(10000)); // 10秒内完成
// 测试生成性能(使用 RetrofitApiGenerator
final generator = RetrofitApiGenerator(
);
final genStopwatch = Stopwatch()..start();
final generatedCode = generator.generateFromDocument(document);
genStopwatch.stop();
expect(generatedCode.length, greaterThan(10000)); // 至少10KB代码
expect(genStopwatch.elapsedMilliseconds, lessThan(15000)); // 15秒内完成
print('Large document performance:');
print(' Parse Time: ${parseStopwatch.elapsedMilliseconds}ms');
print(' Generation Time: ${genStopwatch.elapsedMilliseconds}ms');
print(' Paths: ${document.paths.length}');
print(' Models: ${document.models.length}');
print(
' Generated Code: ${(generatedCode.length / 1024).toStringAsFixed(2)}KB',);
});
});
});
}