import 'dart:io'; import 'package:koi_swagger_parser/koi_swagger_parser.dart'; import 'package:path/path.dart' as p; import 'package:test/test.dart'; void main() { group('multipart generation', () { test('按 preset 生成 Retrofit multipart 参数', () async { final tempDir = await Directory.systemTemp.createTemp( 'koi_swagger_parser_multipart_', ); addTearDown(() => tempDir.delete(recursive: true)); final specFile = File(p.join(tempDir.path, 'openapi.json')); await specFile.writeAsString(r''' { "openapi": "3.1.0", "info": { "title": "Multipart Sample", "version": "1.0.0" }, "paths": { "/api/v1/uploads": { "post": { "tags": ["uploads"], "operationId": "create_upload_api_v1_uploads_post", "requestBody": { "required": true, "content": { "multipart/form-data": { "schema": { "type": "object", "required": ["file", "meta"], "properties": { "file": {"type": "string", "format": "binary"}, "files": { "type": "array", "items": {"type": "string", "format": "binary"} }, "meta": {"$ref": "#/components/schemas/UploadMeta"}, "note": {"type": "string"} } } } } }, "responses": { "200": { "description": "ok", "content": { "application/json": { "schema": {"$ref": "#/components/schemas/UploadResponse"} } } } } } } }, "components": { "schemas": { "UploadMeta": { "type": "object", "required": ["album_id"], "properties": { "album_id": {"type": "integer"}, "tags": { "type": "array", "items": {"type": "string"} } } }, "UploadResponse": { "type": "object", "required": ["id"], "properties": { "id": {"type": "integer"} } } } } } '''); final outputDir = p.join(tempDir.path, 'lib', 'src'); final configFile = File(p.join(tempDir.path, 'koi_swagger_parser.yaml')); await configFile.writeAsString(''' input: swagger_urls: - url: ${specFile.path} enabled: true output: base_dir: $outputDir api_dir: ${p.join(outputDir, 'api')} models_dir: ${p.join(outputDir, 'api_models')} split_by_tags: true generation: preset: name: koi_flutter api: enabled: true use_retrofit: true cancel_token_support: false client: class_name: SampleApiClient file_name: sample_api_client response_wrapper: mode: passthrough detect_standard_envelope: false success_data_field: data models: enabled: true class_prefix: Koi default_non_nullable_list_to_empty: true json_serializable: checked: true explicit_to_json: true include_if_null: false formatting: run_dart_format: false '''); await const KoiSwaggerParser().generate(configPath: configFile.path); final apiFile = File( p.join(outputDir, 'api', 'v1', 'uploads_api.dart'), ).readAsStringSync(); expect(apiFile, contains('@MultiPart()')); expect( apiFile, contains("@Part(name: 'file') required MultipartFile file"), ); expect( apiFile, contains("@Part(name: 'files') List? files"), ); expect( apiFile, contains( "@Part(name: 'meta', contentType: 'application/json') required KoiUploadMeta meta", ), ); expect(apiFile, contains("@Part(name: 'note') String? note")); final metaFile = File( p.join(outputDir, 'api_models', 'request', 'upload_meta.dart'), ).readAsStringSync(); expect(metaFile, contains('class KoiUploadMeta')); expect(metaFile, contains("@JsonKey(name: 'album_id',")); }); }); group('discriminator union generation', () { test('生成 sealed wrapper 并在结果模型中引用', () async { final tempDir = await Directory.systemTemp.createTemp( 'koi_swagger_parser_union_', ); addTearDown(() => tempDir.delete(recursive: true)); final specFile = File(p.join(tempDir.path, 'openapi.json')); await specFile.writeAsString(r''' { "openapi": "3.1.0", "info": { "title": "Union Sample", "version": "1.0.0" }, "paths": { "/api/v1/pets/{pet_id}": { "get": { "tags": ["pets"], "operationId": "get_pet_api_v1_pets__pet_id__get", "parameters": [ { "name": "pet_id", "in": "path", "required": true, "schema": {"type": "integer"} } ], "responses": { "200": { "description": "ok", "content": { "application/json": { "schema": {"$ref": "#/components/schemas/PetResponse"} } } } } } } }, "components": { "schemas": { "PetResponse": { "type": "object", "required": ["pet"], "properties": { "pet": {"$ref": "#/components/schemas/Pet"} } }, "Pet": { "oneOf": [ {"$ref": "#/components/schemas/Cat"}, {"$ref": "#/components/schemas/Dog"} ], "discriminator": { "propertyName": "pet_type", "mapping": { "cat": "#/components/schemas/Cat", "dog": "#/components/schemas/Dog" } } }, "Cat": { "type": "object", "required": ["pet_type", "lives"], "properties": { "pet_type": {"type": "string", "enum": ["cat"]}, "lives": {"type": "integer"} } }, "Dog": { "type": "object", "required": ["pet_type", "breed"], "properties": { "pet_type": {"type": "string", "enum": ["dog"]}, "breed": {"type": "string"} } } } } } '''); final outputDir = p.join(tempDir.path, 'lib', 'src'); final configFile = File(p.join(tempDir.path, 'koi_swagger_parser.yaml')); await configFile.writeAsString(''' input: swagger_urls: - url: ${specFile.path} enabled: true output: base_dir: $outputDir api_dir: ${p.join(outputDir, 'api')} models_dir: ${p.join(outputDir, 'api_models')} split_by_tags: true generation: api: enabled: true use_retrofit: true cancel_token_support: false client: class_name: SampleApiClient file_name: sample_api_client response_wrapper: mode: passthrough detect_standard_envelope: false success_data_field: data models: enabled: true class_prefix: Koi default_non_nullable_list_to_empty: true json_serializable: checked: true explicit_to_json: true include_if_null: false formatting: run_dart_format: false '''); await const KoiSwaggerParser().generate(configPath: configFile.path); final unionFile = File( p.join(outputDir, 'api_models', 'result', 'pet.dart'), ).readAsStringSync(); expect(unionFile, contains('sealed class KoiPet')); expect( unionFile, contains('factory KoiPet.fromJson(Map json)'), ); expect(unionFile, contains("case 'cat':")); expect(unionFile, contains('return KoiPetCat(KoiCat.fromJson(json));')); expect(unionFile, contains('final class KoiPetDog extends KoiPet')); final responseFile = File( p.join(outputDir, 'api_models', 'result', 'pet_response.dart'), ).readAsStringSync(); expect(responseFile, contains('required KoiPet pet')); }); }); }