393 lines
14 KiB
Dart
393 lines
14 KiB
Dart
import 'dart:convert';
|
|
import 'dart:io';
|
|
|
|
import 'package:swagger_generator_flutter/core/models.dart';
|
|
import 'package:swagger_generator_flutter/generators/optimized_retrofit_generator.dart';
|
|
import 'package:test/test.dart';
|
|
|
|
void main() {
|
|
group('OptimizedRetrofitGenerator', () {
|
|
late OptimizedRetrofitGenerator generator;
|
|
late SwaggerDocument testDocument;
|
|
|
|
setUp(() {
|
|
generator = OptimizedRetrofitGenerator(
|
|
className: 'TestApiService',
|
|
generateModularApis: true,
|
|
generateBaseResult: true,
|
|
generatePagination: true,
|
|
generateFileUpload: true,
|
|
);
|
|
|
|
// 创建测试文档
|
|
testDocument = const SwaggerDocument(
|
|
title: 'Test API',
|
|
version: '1.0.0',
|
|
description: 'Test API for generator',
|
|
servers: [
|
|
ApiServer(
|
|
url: 'https://api.example.com',
|
|
description: 'Test server',
|
|
),
|
|
],
|
|
components: ApiComponents(
|
|
schemas: {},
|
|
securitySchemes: {},
|
|
),
|
|
paths: {
|
|
'/api/v1/users/{id}': const ApiPath(
|
|
path: '/api/v1/users/{id}',
|
|
method: HttpMethod.get,
|
|
summary: 'Get user by ID',
|
|
description: 'Retrieve user information by ID',
|
|
operationId: 'getUser',
|
|
tags: ['users'],
|
|
parameters: [
|
|
ApiParameter(
|
|
name: 'id',
|
|
location: ParameterLocation.path,
|
|
required: true,
|
|
type: PropertyType.integer,
|
|
description: 'User ID',
|
|
),
|
|
],
|
|
responses: {
|
|
'200': const ApiResponse(
|
|
code: '200',
|
|
description: 'Success',
|
|
content: {
|
|
'application/json': const ApiMediaType(
|
|
schema: {'type': 'object'},
|
|
),
|
|
},
|
|
),
|
|
},
|
|
),
|
|
'/api/v1/users': const ApiPath(
|
|
path: '/api/v1/users',
|
|
method: HttpMethod.post,
|
|
summary: 'Create user',
|
|
description: 'Create a new user',
|
|
operationId: 'createUser',
|
|
tags: ['users'],
|
|
parameters: [],
|
|
requestBody: ApiRequestBody(
|
|
description: 'User data',
|
|
required: true,
|
|
content: {
|
|
'application/json': const ApiMediaType(
|
|
schema: {'type': 'object'},
|
|
),
|
|
},
|
|
),
|
|
responses: {
|
|
'201': const ApiResponse(
|
|
code: '201',
|
|
description: 'Created',
|
|
content: {
|
|
'application/json': const ApiMediaType(
|
|
schema: {'type': 'object'},
|
|
),
|
|
},
|
|
),
|
|
},
|
|
),
|
|
'/api/v1/files/upload': const ApiPath(
|
|
path: '/api/v1/files/upload',
|
|
method: HttpMethod.post,
|
|
summary: 'Upload file',
|
|
description: 'Upload a file',
|
|
operationId: 'uploadFile',
|
|
tags: ['files'],
|
|
parameters: [],
|
|
requestBody: ApiRequestBody(
|
|
description: 'File to upload',
|
|
required: true,
|
|
content: {
|
|
'multipart/form-data': const ApiMediaType(
|
|
schema: {'type': 'object'},
|
|
),
|
|
},
|
|
),
|
|
responses: {
|
|
'200': const ApiResponse(
|
|
code: '200',
|
|
description: 'Success',
|
|
content: {
|
|
'application/json': const ApiMediaType(
|
|
schema: {'type': 'object'},
|
|
),
|
|
},
|
|
),
|
|
},
|
|
),
|
|
},
|
|
models: {},
|
|
controllers: {},
|
|
security: [],
|
|
);
|
|
});
|
|
|
|
test('generates optimized code successfully', () {
|
|
expect(
|
|
() => generator.generateFromDocument(testDocument), returnsNormally);
|
|
|
|
final generatedCode = generator.generateFromDocument(testDocument);
|
|
expect(generatedCode, isNotEmpty);
|
|
print('Generated code length: ${generatedCode.length} characters');
|
|
});
|
|
|
|
test('includes required imports', () {
|
|
final generatedCode = generator.generateFromDocument(testDocument);
|
|
|
|
expect(generatedCode, contains('import \'package:dio/dio.dart\';'));
|
|
expect(generatedCode,
|
|
contains('import \'package:retrofit/retrofit.dart\';'));
|
|
expect(generatedCode,
|
|
contains('import \'package:json_annotation/json_annotation.dart\';'));
|
|
expect(generatedCode, contains('part \'test_api_service_api.g.dart\';'));
|
|
});
|
|
|
|
test('generates BaseResult type', () {
|
|
final generatedCode = generator.generateFromDocument(testDocument);
|
|
|
|
expect(generatedCode, contains('class BaseResult<T>'));
|
|
expect(generatedCode, contains('final int code;'));
|
|
expect(generatedCode, contains('final String message;'));
|
|
expect(generatedCode, contains('final T? data;'));
|
|
expect(generatedCode, contains('bool get isSuccess => code == 200;'));
|
|
});
|
|
|
|
test('generates pagination types', () {
|
|
final generatedCode = generator.generateFromDocument(testDocument);
|
|
|
|
expect(generatedCode, contains('class BasePageParameter'));
|
|
expect(generatedCode, contains('class BasePageResult<T>'));
|
|
expect(generatedCode, contains('final int page;'));
|
|
expect(generatedCode, contains('final int size;'));
|
|
expect(generatedCode, contains('final int total;'));
|
|
expect(generatedCode, contains('int get totalPages'));
|
|
expect(generatedCode, contains('bool get hasNext'));
|
|
expect(generatedCode, contains('bool get hasPrevious'));
|
|
});
|
|
|
|
test('generates file upload types', () {
|
|
final generatedCode = generator.generateFromDocument(testDocument);
|
|
|
|
expect(generatedCode, contains('class FileUploadRequest'));
|
|
expect(generatedCode, contains('class FileUploadResult'));
|
|
expect(generatedCode, contains('final MultipartFile file;'));
|
|
expect(generatedCode, contains('final String url;'));
|
|
expect(generatedCode, contains('final String filename;'));
|
|
expect(generatedCode, contains('final int size;'));
|
|
});
|
|
|
|
test('generates modular APIs', () {
|
|
final generatedCode = generator.generateFromDocument(testDocument);
|
|
|
|
expect(generatedCode, contains('class UsersApi'));
|
|
expect(generatedCode, contains('class FilesApi'));
|
|
expect(generatedCode, contains('class TestApiService'));
|
|
expect(generatedCode, contains('late final UsersApi users;'));
|
|
expect(generatedCode, contains('late final FilesApi files;'));
|
|
});
|
|
|
|
test('generates API methods with correct annotations', () {
|
|
final generatedCode = generator.generateFromDocument(testDocument);
|
|
|
|
// GET 方法
|
|
expect(generatedCode, contains('@GET(\'/api/v1/users/{id}\')'));
|
|
expect(generatedCode, contains('@Path(\'id\') int id'));
|
|
|
|
// POST 方法
|
|
expect(generatedCode, contains('@POST(\'/api/v1/users\')'));
|
|
expect(generatedCode, contains('@Body() Map<String, dynamic> body'));
|
|
|
|
// 文件上传方法
|
|
expect(generatedCode, contains('@POST(\'/api/v1/files/upload\')'));
|
|
expect(generatedCode, contains('@MultiPart()'));
|
|
expect(generatedCode, contains('@Part() MultipartFile file'));
|
|
});
|
|
|
|
test('generates utility classes', () {
|
|
final generatedCode = generator.generateFromDocument(testDocument);
|
|
|
|
expect(generatedCode, contains('class ApiUtils'));
|
|
expect(generatedCode,
|
|
contains('static Future<MultipartFile> createFileUpload'));
|
|
expect(
|
|
generatedCode, contains('static BasePageParameter createPageParam'));
|
|
});
|
|
|
|
test('handles method name generation correctly', () {
|
|
final generatedCode = generator.generateFromDocument(testDocument);
|
|
|
|
expect(generatedCode, contains('Future<BaseResult<dynamic>> getUsers('));
|
|
expect(generatedCode, contains('Future<BaseResult<dynamic>> postUsers('));
|
|
expect(generatedCode,
|
|
contains('Future<BaseResult<dynamic>> postFilesUpload('));
|
|
});
|
|
|
|
test('generates single API when modular is disabled', () {
|
|
final singleApiGenerator = OptimizedRetrofitGenerator(
|
|
className: 'SingleApiService',
|
|
generateModularApis: false,
|
|
);
|
|
|
|
final generatedCode =
|
|
singleApiGenerator.generateFromDocument(testDocument);
|
|
|
|
expect(generatedCode, contains('abstract class SingleApiService'));
|
|
expect(generatedCode, contains('factory SingleApiService(Dio dio'));
|
|
expect(generatedCode, isNot(contains('class UsersApi')));
|
|
expect(generatedCode, isNot(contains('class FilesApi')));
|
|
});
|
|
|
|
test('handles custom base result type', () {
|
|
final customGenerator = OptimizedRetrofitGenerator(
|
|
baseResultType: 'CustomResult',
|
|
pageResultType: 'CustomPageResult',
|
|
);
|
|
|
|
final generatedCode = customGenerator.generateFromDocument(testDocument);
|
|
|
|
expect(generatedCode, contains('class CustomResult<T>'));
|
|
expect(generatedCode, contains('class CustomPageResult<T>'));
|
|
expect(generatedCode, contains('Future<CustomResult<dynamic>>'));
|
|
});
|
|
|
|
test('extracts module names correctly', () {
|
|
final generator = OptimizedRetrofitGenerator();
|
|
|
|
// 使用反射或创建测试方法来测试私有方法
|
|
// 这里我们通过生成的代码来验证模块名提取是否正确
|
|
final generatedCode = generator.generateFromDocument(testDocument);
|
|
|
|
expect(generatedCode, contains('UsersApi')); // /api/v1/users -> Users
|
|
expect(generatedCode, contains('FilesApi')); // /api/v1/files -> Files
|
|
});
|
|
|
|
test('handles different parameter types', () {
|
|
// 创建包含不同参数类型的测试文档
|
|
const complexDocument = SwaggerDocument(
|
|
title: 'Complex API',
|
|
version: '1.0.0',
|
|
description: 'API with complex parameters',
|
|
servers: [ApiServer(url: 'https://api.example.com')],
|
|
components: ApiComponents(schemas: {}, securitySchemes: {}),
|
|
paths: {
|
|
'/api/v1/search': const ApiPath(
|
|
path: '/api/v1/search',
|
|
method: HttpMethod.get,
|
|
summary: 'Search',
|
|
description: 'Search with various parameters',
|
|
operationId: 'search',
|
|
tags: ['search'],
|
|
parameters: [
|
|
ApiParameter(
|
|
name: 'query',
|
|
location: ParameterLocation.query,
|
|
required: true,
|
|
type: PropertyType.string,
|
|
description: 'Search query',
|
|
),
|
|
ApiParameter(
|
|
name: 'page',
|
|
location: ParameterLocation.query,
|
|
required: false,
|
|
type: PropertyType.integer,
|
|
description: 'Page number',
|
|
),
|
|
ApiParameter(
|
|
name: 'active',
|
|
location: ParameterLocation.query,
|
|
required: false,
|
|
type: PropertyType.boolean,
|
|
description: 'Filter active items',
|
|
),
|
|
],
|
|
responses: {
|
|
'200': const ApiResponse(
|
|
code: '200',
|
|
description: 'Success',
|
|
content: {
|
|
'application/json':
|
|
const ApiMediaType(schema: {'type': 'object'}),
|
|
},
|
|
),
|
|
},
|
|
),
|
|
},
|
|
models: {},
|
|
controllers: {},
|
|
security: [],
|
|
);
|
|
|
|
final generatedCode = generator.generateFromDocument(complexDocument);
|
|
|
|
expect(
|
|
generatedCode, contains('@Query(\'query\') required String query'));
|
|
expect(generatedCode, contains('@Query(\'page\') int? page'));
|
|
expect(generatedCode, contains('@Query(\'active\') bool? active'));
|
|
});
|
|
|
|
test('performance test with complex document', () {
|
|
final stopwatch = Stopwatch()..start();
|
|
|
|
final generatedCode = generator.generateFromDocument(testDocument);
|
|
|
|
final elapsedMs = stopwatch.elapsedMilliseconds;
|
|
stopwatch.stop();
|
|
|
|
print('Generation time: ${elapsedMs}ms');
|
|
print('Generated code size: ${generatedCode.length} characters');
|
|
|
|
expect(elapsedMs, lessThan(1000)); // 应该在1秒内完成
|
|
expect(generatedCode.length, greaterThan(1000)); // 应该生成足够的代码
|
|
});
|
|
});
|
|
|
|
group('Real Project Integration', () {
|
|
test('generates code for real swagger.json', () async {
|
|
// 读取实际的 swagger.json 文件
|
|
final file = File('swagger.json');
|
|
if (!file.existsSync()) {
|
|
print('swagger.json not found, skipping real project test');
|
|
return;
|
|
}
|
|
|
|
final jsonString = await file.readAsString();
|
|
final swaggerJson = jsonDecode(jsonString) as Map<String, dynamic>;
|
|
final document = SwaggerDocument.fromJson(swaggerJson);
|
|
|
|
final generator = OptimizedRetrofitGenerator(
|
|
className: 'OAMobileApiService',
|
|
generateModularApis: true,
|
|
generateBaseResult: true,
|
|
generatePagination: true,
|
|
generateFileUpload: true,
|
|
);
|
|
|
|
final stopwatch = Stopwatch()..start();
|
|
final generatedCode = generator.generateFromDocument(document);
|
|
final elapsedMs = stopwatch.elapsedMilliseconds;
|
|
|
|
print('Real project generation:');
|
|
print(' Time: ${elapsedMs}ms');
|
|
print(' Code size: ${generatedCode.length} characters');
|
|
print(' Lines: ${generatedCode.split('\n').length}');
|
|
|
|
expect(generatedCode, isNotEmpty);
|
|
expect(generatedCode, contains('class OAMobileApiService'));
|
|
expect(generatedCode, contains('FollowManagerApi'));
|
|
expect(generatedCode, contains('LoginApi'));
|
|
expect(generatedCode, contains('IndexApi'));
|
|
|
|
// 可选:将生成的代码写入文件以供检查
|
|
// final outputFile = File('generated_oa_mobile_api.dart');
|
|
// await outputFile.writeAsString(generatedCode);
|
|
// print('Generated code written to: ${outputFile.path}');
|
|
});
|
|
});
|
|
}
|