swagger_generator_flutter/tests/integration_test.dart

590 lines
20 KiB
Dart

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/optimized_retrofit_generator.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': {
'\$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': {
'\$ref': '#/components/schemas/CreateUserRequest',
},
},
},
},
'responses': {
'201': {
'description': 'User created',
'content': {
'application/json': {
'schema': {
'\$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': {
'\$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': {
'\$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(
includeWarnings: true,
);
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',
splitByTags: true,
);
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. 生成优化的 API 代码
final optimizedGenerator = OptimizedRetrofitGenerator(
className: 'OptimizedIntegrationApi',
generateModularApis: true,
generateBaseResult: true,
generatePagination: true,
generateFileUpload: true,
);
final optimizedCode = optimizedGenerator.generateFromDocument(document);
// 验证优化代码
expect(optimizedCode, isNotEmpty);
expect(optimizedCode, contains('class BaseResult'));
expect(optimizedCode, contains('class BasePageResult'));
expect(optimizedCode, contains('class FileUploadRequest'));
expect(optimizedCode, contains('class ApiUtils'));
expect(optimizedCode, contains('UsersApi'));
expect(optimizedCode, contains('FilesApi'));
// 6. 性能验证
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');
print(
' Optimized Code Size: ${(optimizedCode.length / 1024).toStringAsFixed(2)}KB');
// 验证性能指标
expect(
parseStats.totalTime.inMilliseconds, lessThan(2000)); // 解析应在2秒内完成
expect(retrofitCode.length, greaterThan(1000)); // 应生成足够的代码
expect(optimizedCode.length,
greaterThan(retrofitCode.length)); // 优化版本应该更丰富
});
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, // 禁用并行解析
maxConcurrency: 4,
),
);
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}');
// 生成代码
final generator = OptimizedRetrofitGenerator(
className: 'OAMobileApiService',
generateModularApis: true,
generateBaseResult: true,
generatePagination: true,
generateFileUpload: true,
);
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(5000)); // 至少生成5KB代码
});
});
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 (int 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': {
'\$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, // 禁用并行解析
maxConcurrency: 4,
),
);
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秒内完成
// 测试生成性能
final generator = OptimizedRetrofitGenerator(
generateModularApis: true,
);
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');
});
});
});
}