Release 0.2.0
Complete OpenAPI 3.0/3.1 generation, diagnostics, lifecycle safety, enum and naming mappings, bilingual documentation, examples, and pub.dev release validation.
This commit is contained in:
@@ -0,0 +1,198 @@
|
||||
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('schema semantics', () {
|
||||
test(
|
||||
'区分 required/nullable/absent,按方向拆分 readOnly/writeOnly,并保留 additionalProperties',
|
||||
() async {
|
||||
final tempDir = await Directory.systemTemp.createTemp(
|
||||
'koi_swagger_parser_schema_semantics_',
|
||||
);
|
||||
addTearDown(() => tempDir.delete(recursive: true));
|
||||
final spec = File(p.join(tempDir.path, 'openapi.json'));
|
||||
await spec.writeAsString(r'''
|
||||
{
|
||||
"openapi": "3.1.0",
|
||||
"info": {"title": "schema semantics", "version": "1"},
|
||||
"paths": {
|
||||
"/api/v1/accounts": {
|
||||
"post": {
|
||||
"tags": ["accounts"],
|
||||
"operationId": "create_account",
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {"$ref": "#/components/schemas/Account"}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "created",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {"$ref": "#/components/schemas/Account"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"Account": {
|
||||
"type": "object",
|
||||
"required": ["id", "secret", "display_name", "settings"],
|
||||
"properties": {
|
||||
"id": {"type": "integer", "readOnly": true},
|
||||
"secret": {"type": "string", "writeOnly": true},
|
||||
"display_name": {"type": ["string", "null"]},
|
||||
"nickname": {"type": "string"},
|
||||
"settings": {"$ref": "#/components/schemas/FlexibleSettings"}
|
||||
}
|
||||
},
|
||||
"FlexibleSettings": {
|
||||
"type": "object",
|
||||
"required": ["theme"],
|
||||
"properties": {
|
||||
"theme": {"type": "string"}
|
||||
},
|
||||
"additionalProperties": {"type": "integer"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
''');
|
||||
final outputDir = p.join(tempDir.path, 'lib', 'src');
|
||||
final config = File(p.join(tempDir.path, 'koi_swagger_parser.yaml'));
|
||||
await config.writeAsString('''
|
||||
input:
|
||||
swagger_urls:
|
||||
- url: ${spec.path}
|
||||
output:
|
||||
base_dir: $outputDir
|
||||
api_dir: ${p.join(outputDir, 'api')}
|
||||
models_dir: ${p.join(outputDir, 'models')}
|
||||
generation:
|
||||
api:
|
||||
response_wrapper:
|
||||
mode: passthrough
|
||||
models:
|
||||
class_prefix: Koi
|
||||
formatting:
|
||||
run_dart_format: false
|
||||
''');
|
||||
|
||||
await const KoiSwaggerParser().generate(configPath: config.path);
|
||||
|
||||
final request = File(
|
||||
p.join(outputDir, 'models', 'request', 'account_request.dart'),
|
||||
).readAsStringSync();
|
||||
final response = File(
|
||||
p.join(outputDir, 'models', 'result', 'account_response.dart'),
|
||||
).readAsStringSync();
|
||||
expect(request, contains('class KoiAccountRequest'));
|
||||
expect(request, isNot(contains('required int id')));
|
||||
expect(request, contains('required String secret'));
|
||||
expect(request, contains('required String? displayName'));
|
||||
expect(request, contains('includeIfNull: true'));
|
||||
expect(request, contains('String? nickname'));
|
||||
expect(request, contains('disallowNullValue: true'));
|
||||
|
||||
expect(response, contains('class KoiAccountResponse'));
|
||||
expect(response, contains('required int id'));
|
||||
expect(response, isNot(contains('required String secret')));
|
||||
expect(response, contains('required String? displayName'));
|
||||
|
||||
final settings = File(
|
||||
p.join(outputDir, 'models', 'shared', 'flexible_settings.dart'),
|
||||
).readAsStringSync();
|
||||
expect(settings, contains('Map<String, int> additionalProperties'));
|
||||
expect(settings, contains('ReadAdditionalProperties'));
|
||||
expect(settings, contains('createToJson: false'));
|
||||
expect(settings, contains('result[entry.key]'));
|
||||
expect(settings, contains("result['theme']"));
|
||||
|
||||
final api = File(
|
||||
p.join(outputDir, 'api', 'v1', 'accounts_api.dart'),
|
||||
).readAsStringSync();
|
||||
expect(api, contains('required KoiAccountRequest body'));
|
||||
expect(api, contains('Future<KoiAccountResponse> createAccount'));
|
||||
},
|
||||
);
|
||||
|
||||
test('同一模型属性名冲突时确定性后缀并给出诊断', () async {
|
||||
final tempDir = await Directory.systemTemp.createTemp(
|
||||
'koi_swagger_parser_property_collision_',
|
||||
);
|
||||
addTearDown(() => tempDir.delete(recursive: true));
|
||||
final spec = File(p.join(tempDir.path, 'openapi.json'));
|
||||
await spec.writeAsString(r'''
|
||||
{
|
||||
"openapi": "3.1.0",
|
||||
"info": {"title": "collision", "version": "1"},
|
||||
"paths": {
|
||||
"/api/v1/value": {
|
||||
"get": {
|
||||
"tags": ["value"],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "ok",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"some-value": {"type": "string"},
|
||||
"some_value": {"type": "string"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
''');
|
||||
final outputDir = p.join(tempDir.path, 'lib', 'src');
|
||||
final config = File(p.join(tempDir.path, 'koi_swagger_parser.yaml'));
|
||||
await config.writeAsString('''
|
||||
input:
|
||||
swagger_urls:
|
||||
- url: ${spec.path}
|
||||
output:
|
||||
base_dir: $outputDir
|
||||
api_dir: ${p.join(outputDir, 'api')}
|
||||
models_dir: ${p.join(outputDir, 'models')}
|
||||
generation:
|
||||
api:
|
||||
response_wrapper:
|
||||
mode: passthrough
|
||||
formatting:
|
||||
run_dart_format: false
|
||||
''');
|
||||
|
||||
final result = await const KoiSwaggerParser().generate(
|
||||
configPath: config.path,
|
||||
);
|
||||
expect(
|
||||
result.diagnostics.map((diagnostic) => diagnostic.code),
|
||||
contains('openapi.property_name_collision'),
|
||||
);
|
||||
final modelPath = result.writtenFiles.singleWhere(
|
||||
(path) => path.endsWith('response.dart'),
|
||||
);
|
||||
final model = File(modelPath).readAsStringSync();
|
||||
expect(model, contains('String? someValue'));
|
||||
expect(model, contains('String? someValue2'));
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user