diff --git a/.github/workflows/code_quality.yml b/.github/workflows/code_quality.yml index 7f165a3..71d16f3 100644 --- a/.github/workflows/code_quality.yml +++ b/.github/workflows/code_quality.yml @@ -25,9 +25,6 @@ jobs: steps: - name: Checkout uses: actions/checkout@v3 - - name: Start Docker compose containers - if: ${{ matrix.work_dir == 'openapi-generator' }} - run: docker-compose -f "docker-compose.yaml" up -d --build - name: Setup Dart uses: dart-lang/setup-dart@v1.5.0 with: @@ -36,6 +33,9 @@ jobs: run: dart pub get - name: Validate formatting run: dart format ./ --set-exit-if-changed + - name: Generate Mocks + if: ${{ matrix.work_dir == 'openapi-generator' }} + run: dart run build_runner build --delete-conflicting-outputs - name: Run analyzer run: dart analyze --fatal-warnings - name: Run tests @@ -51,9 +51,6 @@ jobs: verbose: true flags: ${{ matrix.work_dir }} CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - - name: Stop Docker Container - if: ${{ matrix.work_dir == 'openapi-generator' && always() }} - run: docker-compose -f "docker-compose.yaml" down build: name: Build example project 🛠️ diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 16e3f52..2dc40d3 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -27,7 +27,11 @@ dependency_overrides: path: ../openapi-generator openapi_generator_cli: path: ../openapi-generator-cli - + source_gen: + git: + ref: reviver + url: https://github.com/Nexushunter/source_gen + path: source_gen # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions diff --git a/openapi-generator-annotations/lib/src/openapi_generator_annotations_base.dart b/openapi-generator-annotations/lib/src/openapi_generator_annotations_base.dart index 007f60e..883f766 100644 --- a/openapi-generator-annotations/lib/src/openapi_generator_annotations_base.dart +++ b/openapi-generator-annotations/lib/src/openapi_generator_annotations_base.dart @@ -1,8 +1,11 @@ +library openapi_generator_annotations; + import 'dart:convert'; import 'dart:io'; import 'package:crypto/crypto.dart'; import 'package:meta/meta.dart'; +import 'package:source_gen/source_gen.dart'; /// Config base class /// Your annotated class must extend this config class @@ -226,11 +229,6 @@ class RemoteSpec extends InputSpec { const RemoteSpec.empty() : this(path: 'http://localhost:8080/'); Uri get url => Uri.parse(path); - - RemoteSpec.fromMap(Map map) - : headerDelegate = - map['headerDelegate'] ?? const RemoteSpecHeaderDelegate(), - super.fromMap(map); } /// Default [RemoteSpecHeaderDelegate] used when retrieving a remote OAS spec. @@ -266,12 +264,6 @@ class AWSRemoteSpecHeaderDelegate extends RemoteSpecHeaderDelegate { this.accessKeyId = null, }) : super(); - AWSRemoteSpecHeaderDelegate.fromMap(Map map) - : bucket = map['bucket'], - accessKeyId = map['accessKeyId'], - secretAccessKey = map['secretAccessKey'], - super.fromMap(map); - /// Generates the [header] map used within the GET request. /// /// Assumes that the user's auth AWS credentials diff --git a/openapi-generator-annotations/pubspec.yaml b/openapi-generator-annotations/pubspec.yaml index 289d60f..a1a8b8e 100644 --- a/openapi-generator-annotations/pubspec.yaml +++ b/openapi-generator-annotations/pubspec.yaml @@ -10,8 +10,16 @@ environment: dependencies: crypto: '>=3.0.0 <4.0.0' meta: '>=1.3.0 <2.0.0' + source_gen: dev_dependencies: test: source_gen_test: lint: + +dependency_overrides: + source_gen: + git: + ref: reviver + url: https://github.com/Nexushunter/source_gen.git + path: source_gen \ No newline at end of file diff --git a/openapi-generator-annotations/test/openapi_generator_annotations_test.dart b/openapi-generator-annotations/test/openapi_generator_annotations_test.dart index a8ea25b..3011960 100644 --- a/openapi-generator-annotations/test/openapi_generator_annotations_test.dart +++ b/openapi-generator-annotations/test/openapi_generator_annotations_test.dart @@ -91,6 +91,7 @@ void main() { expect(remote.path, 'http://localhost:8080/'); expect(remote.headerDelegate, isA()); }); + test('uses path', () { final remote = RemoteSpec(path: 'https://example.com/path'); expect(remote.path, 'https://example.com/path'); @@ -112,7 +113,6 @@ void main() { accessKeyId: 'test', secretAccessKey: 'test', ); - test('signs the url correctly', () { final now = DateTime.now(); final actual = delegate.authHeaderContent( @@ -176,6 +176,7 @@ void main() { } }); test('uses the provided environment', () async { + print(Directory.current.path); final result = Process.runSync( 'dart', [ diff --git a/openapi-generator/.gitignore b/openapi-generator/.gitignore index a1cc285..1810161 100755 --- a/openapi-generator/.gitignore +++ b/openapi-generator/.gitignore @@ -169,4 +169,6 @@ example/.dart_tool # Generated test output test/specs/test-cached.json -test/specs/localstack/** \ No newline at end of file +test/specs/commands-cache.json +test/specs/localstack +test/mocks.mocks.dart \ No newline at end of file diff --git a/openapi-generator/Makefile b/openapi-generator/Makefile new file mode 100644 index 0000000..92e92a1 --- /dev/null +++ b/openapi-generator/Makefile @@ -0,0 +1,4 @@ +.PHONY: gen + +gen: + dart run build_runner build --delete-conflicting-outputs \ No newline at end of file diff --git a/openapi-generator/docker-compose.yaml b/openapi-generator/docker-compose.yaml deleted file mode 100644 index 64f3ca2..0000000 --- a/openapi-generator/docker-compose.yaml +++ /dev/null @@ -1,13 +0,0 @@ -version: '3' -services: - local-s3: - container_name: localstack - image: localstack/localstack:latest - ports: - - "4566:4566" - environment: - - SERVICES=S3 - - AWS_DEFAULT_REGION=us-east-1 - volumes: - - './test/specs/localstack:/var/lib/localstack' - - './test/specs/buckets.sh:/etc/localstack/init/ready.d/buckets.sh' diff --git a/openapi-generator/lib/src/extensions/type_methods.dart b/openapi-generator/lib/src/extensions/type_methods.dart index 6f0f422..72a7160 100644 --- a/openapi-generator/lib/src/extensions/type_methods.dart +++ b/openapi-generator/lib/src/extensions/type_methods.dart @@ -1,8 +1,6 @@ import 'dart:mirrors'; import 'package:analyzer/dart/element/type.dart'; -import 'package:openapi_generator/src/utils.dart'; -import 'package:openapi_generator_annotations/openapi_generator_annotations.dart'; import 'package:source_gen/source_gen.dart' show ConstantReader, TypeChecker; /// Extension adding the type methods to `ConstantReader`. @@ -73,100 +71,3 @@ extension TypeMethods on ConstantReader { return values[enumIndex]; } } - -extension ReadProperty on ConstantReader { - T readPropertyOrDefault(String name, T defaultValue) { - final v = peek(name); - if (v == null) { - return defaultValue; - } - - if (isA(v, InputSpec)) { - final revived = v.revive(); - - if (isA(v, RemoteSpec)) { - final map = revived.namedArguments; - final delegate = map['headerDelegate']; - final mapped = { - 'path': convertToPropertyValue(map['path']!), - }; - if (delegate?.isNull ?? true) { - return RemoteSpec.fromMap(mapped) as T; - } else { - final delegateReader = ConstantReader(delegate); - if (isA(delegateReader, AWSRemoteSpecHeaderDelegate)) { - mapped['headerDelegate'] = AWSRemoteSpecHeaderDelegate.fromMap( - delegateReader.revive().namedArguments.map( - (key, value) => MapEntry( - key, - convertToPropertyValue(value), - ), - ), - ); - } - return RemoteSpec.fromMap(mapped) as T; - } - } else { - final map = revived.namedArguments.map( - (key, value) => MapEntry( - key, - convertToPropertyValue(value), - ), - ); - return InputSpec.fromMap(map) as T; - } - } - - if (isA(v, AdditionalProperties)) { - final map = v.revive().namedArguments.map( - (key, value) => MapEntry( - key, - convertToPropertyValue(value), - ), - ); - if (isA(v, DioProperties)) { - return DioProperties.fromMap(map) as T; - } else if (isA(v, DioAltProperties)) { - return DioAltProperties.fromMap(map) as T; - } else { - return AdditionalProperties.fromMap(map) as T; - } - } - - if (isA(v, InlineSchemaOptions)) { - return InlineSchemaOptions.fromMap( - v.revive().namedArguments.map( - (key, value) => MapEntry( - key, - convertToPropertyValue(value), - ), - ), - ) as T; - } - - if (isA(v, Map)) { - return v.mapValue.map((key, value) => MapEntry( - convertToPropertyValue(key!) as String, - convertToPropertyValue(value!) as String)) as T; - } else if (isA(v, bool)) { - return v.boolValue as T; - } else if (isA(v, double)) { - return v.doubleValue as T; - } else if (isA(v, int)) { - return v.intValue as T; - } else if (isA(v, String)) { - return v.stringValue as T; - } else if (isA(v, Set)) { - return v.setValue.map(convertToPropertyValue) as T; - } else if (isA(v, List)) { - return v.listValue.map(convertToPropertyValue) as T; - } else if (isA(v, Enum)) { - return v.enumValue(); - } else { - return defaultValue; - } - } -} - -bool isA(ConstantReader? v, Type t) => - v?.instanceOf(TypeChecker.fromRuntime(t)) ?? false; diff --git a/openapi-generator/lib/src/gen_on_spec_changes.dart b/openapi-generator/lib/src/gen_on_spec_changes.dart index e221305..176c6de 100644 --- a/openapi-generator/lib/src/gen_on_spec_changes.dart +++ b/openapi-generator/lib/src/gen_on_spec_changes.dart @@ -59,23 +59,23 @@ FutureOr> loadSpec( } } else { Map? headers; - if (specConfig.headerDelegate is AWSRemoteSpecHeaderDelegate) { - try { - headers = (specConfig.headerDelegate as AWSRemoteSpecHeaderDelegate) - .header(path: specConfig.url.path); - } catch (e, st) { - return Future.error( - OutputMessage( - message: 'failed to generate AWS headers', - additionalContext: e, - stackTrace: st, - level: Level.SEVERE, - ), - ); - } - } else { - headers = specConfig.headerDelegate.header(); - } + // if (specConfig.headerDelegate is AWSRemoteSpecHeaderDelegate) { + // try { + // headers = (specConfig.headerDelegate as AWSRemoteSpecHeaderDelegate) + // .header(path: specConfig.url.path); + // } catch (e, st) { + // return Future.error( + // OutputMessage( + // message: 'failed to generate AWS headers', + // additionalContext: e, + // stackTrace: st, + // level: Level.SEVERE, + // ), + // ); + // } + // } else { + // headers = specConfig.headerDelegate.header(); + // } final resp = await http.get(specConfig.url, headers: headers); if (resp.statusCode == 200) { diff --git a/openapi-generator/lib/src/models/command.dart b/openapi-generator/lib/src/models/command.dart index 6e3e112..2f847e0 100644 --- a/openapi-generator/lib/src/models/command.dart +++ b/openapi-generator/lib/src/models/command.dart @@ -1,3 +1,7 @@ +import 'dart:io'; + +import 'package:openapi_generator/src/determine_flutter_project_status.dart'; +import 'package:openapi_generator/src/gen_on_spec_changes.dart'; import 'package:openapi_generator_annotations/openapi_generator_annotations.dart'; /// Creates a representation of a cli request for Flutter or Dart. @@ -29,3 +33,52 @@ class Command { arguments, ); } + +/// CommandRunner provides an abstraction layer to external functions / processes. +class CommandRunner { + const CommandRunner(); + + Future runCommand({ + required Command command, + required String workingDirectory, + }) async => + Process.run( + command.executable, + command.arguments, + workingDirectory: workingDirectory, + runInShell: Platform.isWindows, + ); + + Future> loadAnnotatedFile({required String path}) async { + final f = File(path); + return f.readAsLines(); + } + + Future writeAnnotatedFile( + {required String path, required List content}) async { + final f = File(path); + return f.writeAsStringSync(content.join('\n'), flush: true); + } + + Future cacheSpecFile({ + required Map updatedSpec, + required String cachedPath, + }) async => + cacheSpec(outputLocation: cachedPath, spec: updatedSpec); + + Future> loadSpecFile( + {required InputSpec specConfig, bool isCached = false}) async => + loadSpec(specConfig: specConfig); + + Future isSpecFileDirty({ + required Map cachedSpec, + required Map loadedSpec, + }) async => + isSpecDirty(cachedSpec: cachedSpec, loadedSpec: loadedSpec); + + Future checkForFlutterEnvironemt( + {Wrapper? wrapper = Wrapper.none, + String? providedPubspecPath}) async => + checkPubspecAndWrapperForFlutterSupport( + wrapper: wrapper, providedPubspecPath: providedPubspecPath); +} diff --git a/openapi-generator/lib/src/models/generator_arguments.dart b/openapi-generator/lib/src/models/generator_arguments.dart index c43c392..d82962c 100644 --- a/openapi-generator/lib/src/models/generator_arguments.dart +++ b/openapi-generator/lib/src/models/generator_arguments.dart @@ -2,10 +2,8 @@ import 'dart:async'; import 'dart:io'; import 'package:logging/logging.dart'; -import 'package:openapi_generator/src/extensions/type_methods.dart'; import 'package:openapi_generator/src/models/output_message.dart'; import 'package:openapi_generator_annotations/openapi_generator_annotations.dart'; -import 'package:source_gen/source_gen.dart' as src_gen; import '../utils.dart'; @@ -74,6 +72,7 @@ class GeneratorArguments { /// Use the provided spec instead of one located in [Directory.current]. /// /// Default: openapi.(ya?ml) | openapi.json + @Deprecated('Use inputSpec instead.') String _inputFile; /// Provides an OAS spec file. @@ -114,63 +113,61 @@ class GeneratorArguments { final InlineSchemaOptions? inlineSchemaOptions; GeneratorArguments({ - required src_gen.ConstantReader annotations, - bool alwaysRun = false, - String inputSpecFile = '', - InputSpec inputSpec = const InputSpec.empty(), - String templateDirectory = '', - Generator generator = Generator.dart, - Map typeMapping = const {}, - Map importMapping = const {}, - Map reservedWordsMapping = const {}, - Map inlineSchemaNameMapping = const {}, + required Openapi annotation, + bool? alwaysRun, + String? inputSpecFile, + InputSpec? inputSpec, + String? templateDirectory, + Generator? generator, + Map? typeMapping, + Map? importMapping, + Map? reservedWordsMapping, + Map? inlineSchemaNameMapping, AdditionalProperties? additionalProperties, InlineSchemaOptions? inlineSchemaOptions, - bool skipValidation = false, - bool runSourceGen = true, + bool? skipValidation, + bool? runSourceGen, String? outputDirectory, - bool fetchDependencies = true, - bool useNextGen = false, + bool? fetchDependencies, + bool? useNextGen, String? cachePath, String? pubspecPath, - bool isDebug = false, - }) : alwaysRun = annotations.readPropertyOrDefault('alwaysRun', alwaysRun), - _inputFile = - annotations.readPropertyOrDefault('inputSpecFile', inputSpecFile), - templateDirectory = annotations.readPropertyOrDefault( - 'templateDirectory', templateDirectory), - generator = - annotations.readPropertyOrDefault('generatorName', generator), + bool? isDebug, + }) : alwaysRun = alwaysRun ?? annotation.alwaysRun ?? true, + _inputFile = inputSpecFile ?? annotation.inputSpecFile, + templateDirectory = + templateDirectory ?? annotation.templateDirectory ?? '', + generator = generator ?? annotation.generatorName, typeMappings = - annotations.readPropertyOrDefault('typeMappings', typeMapping), + typeMapping ?? annotation.typeMappings ?? {}, importMappings = - annotations.readPropertyOrDefault('importMappings', importMapping), - reservedWordsMappings = annotations.readPropertyOrDefault( - 'reservedWordsMappings', reservedWordsMapping), - inlineSchemaNameMappings = annotations.readPropertyOrDefault( - 'inlineSchemaNameMappings', inlineSchemaNameMapping), - additionalProperties = annotations.readPropertyOrDefault( - 'additionalProperties', additionalProperties), - inlineSchemaOptions = annotations.readPropertyOrDefault( - 'inlineSchemaOptions', inlineSchemaOptions), - skipValidation = annotations.readPropertyOrDefault( - 'skipSpecValidation', skipValidation), - runSourceGen = annotations.readPropertyOrDefault( - 'runSourceGenOnOutput', runSourceGen), - shouldFetchDependencies = annotations.readPropertyOrDefault( - 'fetchDependencies', fetchDependencies), - outputDirectory = annotations.readPropertyOrDefault( - 'outputDirectory', outputDirectory ?? Directory.current.path), - useNextGen = - annotations.readPropertyOrDefault('useNextGen', useNextGen), - cachePath = annotations.readPropertyOrDefault( - 'cachePath', cachePath ?? defaultCachedPath), - pubspecPath = annotations.readPropertyOrDefault( - 'projectPubspecPath', + importMapping ?? annotation.importMappings ?? {}, + reservedWordsMappings = reservedWordsMapping ?? + annotation.reservedWordsMappings ?? + {}, + inlineSchemaNameMappings = inlineSchemaNameMapping ?? + annotation.inlineSchemaNameMappings ?? + {}, + additionalProperties = + additionalProperties ?? annotation.additionalProperties, + inlineSchemaOptions = inlineSchemaOptions, + // ?? annotations.readPropertyOrDefault( + // 'inlineSchemaOptions', inlineSchemaOptions), + skipValidation = + skipValidation ?? annotation.skipSpecValidation ?? false, + runSourceGen = runSourceGen ?? annotation.runSourceGenOnOutput ?? true, + shouldFetchDependencies = + fetchDependencies ?? annotation.fetchDependencies ?? true, + outputDirectory = annotation.outputDirectory ?? + outputDirectory ?? + Directory.current.path, + useNextGen = useNextGen ?? annotation.useNextGen, + cachePath = cachePath ?? annotation.cachePath ?? defaultCachedPath, + pubspecPath = annotation.projectPubspecPath ?? pubspecPath ?? - '${Directory.current.path}${Platform.pathSeparator}pubspec.yaml'), - isDebug = annotations.readPropertyOrDefault('debugLogging', isDebug), - inputSpec = annotations.readPropertyOrDefault('inputSpec', inputSpec); + '${Directory.current.path}${Platform.pathSeparator}pubspec.yaml', + isDebug = annotation.debugLogging, + inputSpec = inputSpec ?? annotation.inputSpec ?? InputSpec.empty(); /// The stringified name of the [Generator]. String get generatorName => generator == Generator.dart diff --git a/openapi-generator/lib/src/openapi_generator_runner.dart b/openapi-generator/lib/src/openapi_generator_runner.dart index 7ddc15d..4d06b22 100755 --- a/openapi-generator/lib/src/openapi_generator_runner.dart +++ b/openapi-generator/lib/src/openapi_generator_runner.dart @@ -6,8 +6,6 @@ import 'dart:isolate'; import 'package:analyzer/dart/element/element.dart'; import 'package:build/build.dart'; import 'package:logging/logging.dart'; -import 'package:openapi_generator/src/determine_flutter_project_status.dart'; -import 'package:openapi_generator/src/gen_on_spec_changes.dart'; import 'package:openapi_generator/src/models/output_message.dart'; import 'package:openapi_generator/src/utils.dart'; import 'package:openapi_generator_annotations/openapi_generator_annotations.dart' @@ -20,14 +18,20 @@ import 'models/generator_arguments.dart'; class OpenapiGenerator extends GeneratorForAnnotation { @Deprecated('To be removed in next major version') final bool testMode; + final CommandRunner runner; + final Logger _log; - OpenapiGenerator({this.testMode = false}); + OpenapiGenerator({ + this.testMode = false, + this.runner = const CommandRunner(), + Logger? logger, + }) : _log = logger ?? Logger('OpenApiGenerator'); @override FutureOr generateForAnnotatedElement( Element element, ConstantReader annotations, BuildStep buildStep) async { logOutputMessage( - log: log, + log: _log, communication: OutputMessage( message: [ '\n', @@ -38,51 +42,35 @@ class OpenapiGenerator extends GeneratorForAnnotation { ), ); - try { - if (element is! ClassElement) { - final friendlyName = element.displayName; + if (element is! ClassElement) { + final friendlyName = element.displayName; - throw InvalidGenerationSourceError( - 'Generator cannot target `$friendlyName`.', - todo: 'Remove the [Openapi] annotation from `$friendlyName`.', - ); - } else { - if (!(annotations.read('useNextGen').literalValue as bool)) { - if (annotations.read('cachePath').literalValue != null) { - throw InvalidGenerationSourceError( - 'useNextGen must be set when using cachePath', - todo: - 'Either set useNextGen: true on the annotation or remove the custom cachePath', - ); - } - } + throw InvalidGenerationSourceError( + 'Generator cannot target `$friendlyName`.', + todo: 'Remove the [Openapi] annotation from `$friendlyName`.', + ); + } else { + final apiAnnotation = Reviver(annotations).toInstance() as annots.Openapi; + if (!apiAnnotation.useNextGen && apiAnnotation.cachePath != null) { + throw AssertionError('useNextGen must be set when using cachePath'); + } + try { // Transform the annotations. - final args = GeneratorArguments(annotations: annotations); - + final args = GeneratorArguments(annotation: apiAnnotation); // Determine if the project has a dependency on the flutter sdk or not. - final baseCommand = await checkPubspecAndWrapperForFlutterSupport( + final baseCommand = await runner.checkForFlutterEnvironemt( wrapper: args.wrapper, providedPubspecPath: args.pubspecPath) ? 'flutter' : 'dart'; + logOutputMessage( + log: _log, + communication: OutputMessage( + message: 'Using $baseCommand environemnt', + ), + ); - if (!args.useNextGen) { - final path = - '${args.outputDirectory}${Platform.pathSeparator}lib${Platform.pathSeparator}api.dart'; - if (await File(path).exists()) { - if (!args.alwaysRun) { - logOutputMessage( - log: log, - communication: OutputMessage( - message: - 'Generated client already exists at [$path] and configuration is annotated with alwaysRun: [${args.alwaysRun}]. Therefore, skipping this build. Note that the "alwaysRun" config will be removed in future versions.', - level: Level.INFO, - ), - ); - return ''; - } - } - } else { + if (args.useNextGen) { // If the flag to use the next generation of the generator is applied // use the new functionality. return generatorV2( @@ -91,33 +79,49 @@ class OpenapiGenerator extends GeneratorForAnnotation { annotatedPath: buildStep.inputId.path); } + final path = + '${args.outputDirectory}${Platform.pathSeparator}lib${Platform.pathSeparator}api.dart'; + if (await File(path).exists()) { + if (!args.alwaysRun) { + logOutputMessage( + log: _log, + communication: OutputMessage( + message: + 'Generated client already exists at [$path] and configuration is annotated with alwaysRun: [${args.alwaysRun}]. Therefore, skipping this build. Note that the "alwaysRun" config will be removed in future versions.', + level: Level.INFO, + ), + ); + return ''; + } + } + await runOpenApiJar(arguments: args); await fetchDependencies(baseCommand: baseCommand, args: args); await generateSources(baseCommand: baseCommand, args: args); - } - } catch (e, st) { - late OutputMessage communication; - if (e is! OutputMessage) { - communication = OutputMessage( - message: '- There was an error generating the spec.', - level: Level.SEVERE, - additionalContext: e, - stackTrace: st, - ); - } else { - communication = e; - } + } catch (e, st) { + late OutputMessage communication; + if (e is! OutputMessage) { + communication = OutputMessage( + message: '- There was an error generating the spec.', + level: Level.SEVERE, + additionalContext: e, + stackTrace: st, + ); + } else { + communication = e; + } - logOutputMessage(log: log, communication: communication); + logOutputMessage(log: _log, communication: communication); + } + return ''; } - return ''; } /// Runs the OpenAPI compiler with the given [args]. Future runOpenApiJar({required GeneratorArguments arguments}) async { final args = await arguments.jarArgs; logOutputMessage( - log: log, + log: _log, communication: OutputMessage( message: 'Running following command to generate openapi client - [ ${args.join(' ')} ]', @@ -131,23 +135,18 @@ class OpenapiGenerator extends GeneratorForAnnotation { // Include java environment variables in openApiCliCommand var javaOpts = Platform.environment['JAVA_OPTS'] ?? ''; - ProcessResult result; - if (!testMode) { - result = await Process.run( - 'java', - [ + final result = await runner.runCommand( + command: Command( + executable: 'java', + arguments: [ if (javaOpts.isNotEmpty) javaOpts, '-jar', binPath, ...args, ], - workingDirectory: Directory.current.path, - runInShell: Platform.isWindows, - ); - } else { - result = ProcessResult(999999, 0, null, null); - } - + ), + workingDirectory: Directory.current.path, + ); if (result.exitCode != 0) { return Future.error( OutputMessage( @@ -159,7 +158,7 @@ class OpenapiGenerator extends GeneratorForAnnotation { ); } else { logOutputMessage( - log: log, + log: _log, communication: OutputMessage( message: [ if (arguments.isDebug) result.stdout, @@ -182,7 +181,7 @@ class OpenapiGenerator extends GeneratorForAnnotation { required String annotatedPath}) async { if (args.isRemote) { logOutputMessage( - log: log, + log: _log, communication: OutputMessage( message: 'Using a remote specification, a cache will still be create but may be outdated.', @@ -193,14 +192,14 @@ class OpenapiGenerator extends GeneratorForAnnotation { try { if (!await hasDiff(args: args)) { logOutputMessage( - log: log, + log: _log, communication: OutputMessage( message: 'No diff between versions, not running generator.', ), ); } else { logOutputMessage( - log: log, + log: _log, communication: OutputMessage( message: 'Dirty Spec found. Running generation.', ), @@ -210,26 +209,26 @@ class OpenapiGenerator extends GeneratorForAnnotation { await generateSources(baseCommand: baseCommand, args: args); if (!args.hasLocalCache) { logOutputMessage( - log: log, + log: _log, communication: OutputMessage( message: 'No local cache found. Creating one.', - level: Level.CONFIG, + level: Level.INFO, ), ); } else { logOutputMessage( - log: log, + log: _log, communication: OutputMessage( message: 'Local cache found. Overwriting existing one.', - level: Level.CONFIG, + level: Level.INFO, ), ); } - await cacheSpec( - outputLocation: args.cachePath, - spec: await loadSpec(specConfig: args.inputSpec)); + await runner.cacheSpecFile( + cachedPath: args.cachePath, + updatedSpec: await runner.loadSpecFile(specConfig: args.inputSpec)); logOutputMessage( - log: log, + log: _log, communication: OutputMessage( message: 'Successfully cached spec changes.', ), @@ -237,7 +236,7 @@ class OpenapiGenerator extends GeneratorForAnnotation { } } catch (e, st) { logOutputMessage( - log: log, + log: _log, communication: OutputMessage( message: 'Failed to generate content.', additionalContext: e, @@ -249,7 +248,7 @@ class OpenapiGenerator extends GeneratorForAnnotation { await formatCode(args: args).then( (_) {}, onError: (e, st) => logOutputMessage( - log: log, + log: _log, communication: OutputMessage( message: 'Failed to format generated code.', additionalContext: e, @@ -260,14 +259,14 @@ class OpenapiGenerator extends GeneratorForAnnotation { ); await updateAnnotatedFile(annotatedPath: annotatedPath).then( (_) => logOutputMessage( - log: log, + log: _log, communication: OutputMessage( message: 'Successfully updated annotated file.', - level: Level.CONFIG, + level: Level.INFO, ), ), onError: (e, st) => logOutputMessage( - log: log, + log: _log, communication: OutputMessage( message: 'Failed to update annotated class file.', level: Level.WARNING, @@ -283,12 +282,12 @@ class OpenapiGenerator extends GeneratorForAnnotation { /// Load both specs into memory and verify if there is a diff between them. FutureOr hasDiff({required GeneratorArguments args}) async { try { - final cachedSpec = await loadSpec( + final cachedSpec = await runner.loadSpecFile( specConfig: annots.InputSpec(path: args.cachePath), isCached: true); - final loadedSpec = await loadSpec(specConfig: args.inputSpec); + final loadedSpec = await runner.loadSpecFile(specConfig: args.inputSpec); logOutputMessage( - log: log, + log: _log, communication: OutputMessage( message: [ 'Loaded cached and current spec files.', @@ -300,7 +299,8 @@ class OpenapiGenerator extends GeneratorForAnnotation { ), ); - return isSpecDirty(cachedSpec: cachedSpec, loadedSpec: loadedSpec); + return await runner.isSpecFileDirty( + cachedSpec: cachedSpec, loadedSpec: loadedSpec); } catch (e, st) { return Future.error( OutputMessage( @@ -319,7 +319,7 @@ class OpenapiGenerator extends GeneratorForAnnotation { {required String baseCommand, required GeneratorArguments args}) async { if (!args.runSourceGen) { logOutputMessage( - log: log, + log: _log, communication: OutputMessage( message: 'Skipping source gen step due to flag being set.', level: Level.WARNING, @@ -327,7 +327,7 @@ class OpenapiGenerator extends GeneratorForAnnotation { ); } else if (!args.shouldGenerateSources) { logOutputMessage( - log: log, + log: _log, communication: OutputMessage( message: 'Skipping source gen because generator does not need it.', ), @@ -335,7 +335,7 @@ class OpenapiGenerator extends GeneratorForAnnotation { } else { return await runSourceGen(baseCommand: baseCommand, args: args).then( (_) => logOutputMessage( - log: log, + log: _log, communication: OutputMessage( message: 'Sources generated successfully.', ), @@ -356,7 +356,7 @@ class OpenapiGenerator extends GeneratorForAnnotation { Future runSourceGen( {required String baseCommand, required GeneratorArguments args}) async { logOutputMessage( - log: log, + log: _log, communication: OutputMessage( message: 'Running source code generation.', ), @@ -369,23 +369,14 @@ class OpenapiGenerator extends GeneratorForAnnotation { wrapper: args.wrapper); logOutputMessage( - log: log, + log: _log, communication: OutputMessage( message: '${command.executable} ${command.arguments.join(' ')}', ), ); - ProcessResult results; - if (!testMode) { - results = await Process.run( - command.executable, - command.arguments, - runInShell: Platform.isWindows, - workingDirectory: args.outputDirectory, - ); - } else { - results = ProcessResult(99999, 0, null, null); - } + final results = await runner.runCommand( + command: command, workingDirectory: args.outputDirectory); if (results.exitCode != 0) { return Future.error( @@ -398,7 +389,7 @@ class OpenapiGenerator extends GeneratorForAnnotation { ); } else { logOutputMessage( - log: log, + log: _log, communication: OutputMessage( message: 'Codegen completed successfully.', ), @@ -411,7 +402,7 @@ class OpenapiGenerator extends GeneratorForAnnotation { {required String baseCommand, required GeneratorArguments args}) async { if (!args.shouldFetchDependencies) { logOutputMessage( - log: log, + log: _log, communication: OutputMessage( message: 'Skipping install step because flag was set.', level: Level.WARNING, @@ -424,25 +415,15 @@ class OpenapiGenerator extends GeneratorForAnnotation { wrapper: args.wrapper); logOutputMessage( - log: log, + log: _log, communication: OutputMessage( message: 'Installing dependencies with generated source. ${command.executable} ${command.arguments.join(' ')}', ), ); - ProcessResult results; - if (!testMode) { - results = await Process.run( - command.executable, - command.arguments, - runInShell: Platform.isWindows, - workingDirectory: args.outputDirectory, - ); - } else { - results = ProcessResult(999999, 0, null, null); - } - + final results = await runner.runCommand( + command: command, workingDirectory: args.outputDirectory); if (results.exitCode != 0) { return Future.error( OutputMessage( @@ -454,7 +435,7 @@ class OpenapiGenerator extends GeneratorForAnnotation { ); } else { logOutputMessage( - log: log, + log: _log, communication: OutputMessage( message: [ if (args.isDebug) results.stdout, @@ -471,35 +452,35 @@ class OpenapiGenerator extends GeneratorForAnnotation { required Map updatedSpec, required String cachedPath, }) async => - cacheSpec(spec: updatedSpec, outputLocation: cachedPath); + runner.cacheSpecFile(updatedSpec: updatedSpec, cachedPath: cachedPath); Future updateAnnotatedFile({required annotatedPath}) async { - // The should exist since that is what triggered the build to begin with so - // there is no point in verifying it exists. It is also a relative file since - // it exists within the project. - final f = File(annotatedPath); - var content = f.readAsLinesSync(); - final now = DateTime.now().toIso8601String(); - final generated = '$lastRunPlaceHolder: $now'; - if (content.first.contains(lastRunPlaceHolder)) { - content = content.sublist(1); - logOutputMessage( - log: log, - communication: OutputMessage( - message: 'Found generated timestamp. Updating with $now', - ), - ); - } else { - logOutputMessage( - log: log, - communication: OutputMessage( - message: 'Creating generated timestamp with $now', - ), - ); - } try { - content.insert(0, generated); - f.writeAsStringSync(content.join('\n'), flush: true); + // The should exist since that is what triggered the build to begin with so + // there is no point in verifying it exists. It is also a relative file since + // it exists within the project. + var content = await runner.loadAnnotatedFile(path: annotatedPath); + final now = DateTime.now().toIso8601String(); + final generated = '$lastRunPlaceHolder: $now'; + if (content.first.contains(lastRunPlaceHolder)) { + content = content.sublist(1); + logOutputMessage( + log: _log, + communication: OutputMessage( + message: 'Found generated timestamp. Updating with $now', + ), + ); + } else { + logOutputMessage( + log: _log, + communication: OutputMessage( + message: 'Creating generated timestamp with $now', + ), + ); + } + + await runner.writeAnnotatedFile( + path: annotatedPath, content: content..insert(0, generated)); } catch (e, st) { return Future.error( OutputMessage( @@ -515,17 +496,8 @@ class OpenapiGenerator extends GeneratorForAnnotation { /// Format the generated code in the output directory. Future formatCode({required GeneratorArguments args}) async { final command = Command(executable: 'dart', arguments: ['format', './']); - ProcessResult result; - if (!testMode) { - result = await Process.run( - command.executable, - command.arguments, - workingDirectory: args.outputDirectory, - runInShell: Platform.isWindows, - ); - } else { - result = ProcessResult(99999, 0, null, null); - } + final result = await runner.runCommand( + command: command, workingDirectory: args.outputDirectory); if (result.exitCode != 0) { return Future.error( @@ -538,9 +510,11 @@ class OpenapiGenerator extends GeneratorForAnnotation { ); } else { logOutputMessage( - log: log, - communication: - OutputMessage(message: 'Successfully formatted code.')); + log: _log, + communication: OutputMessage( + message: 'Successfully formatted code.', + ), + ); } } } diff --git a/openapi-generator/lib/src/utils.dart b/openapi-generator/lib/src/utils.dart index 40f344a..9620b36 100644 --- a/openapi-generator/lib/src/utils.dart +++ b/openapi-generator/lib/src/utils.dart @@ -6,7 +6,7 @@ import 'models/output_message.dart'; /// A utility function that prints out a log meant for the end user. void logOutputMessage( {required Logger log, required OutputMessage communication}) => - log.log(communication.level, communication.message + '\n', + log.log(communication.level, communication.message, communication.additionalContext, communication.stackTrace); /// Transforms a [Map] into a string. diff --git a/openapi-generator/pubspec.yaml b/openapi-generator/pubspec.yaml index 60ed7e7..83c1451 100755 --- a/openapi-generator/pubspec.yaml +++ b/openapi-generator/pubspec.yaml @@ -24,7 +24,13 @@ dev_dependencies: source_gen_test: pedantic: coverage: ^1.6.3 + mockito: -#dependency_overrides: -# openapi_generator_annotations: -# path: ../openapi-generator-annotations +dependency_overrides: + openapi_generator_annotations: + path: ../openapi-generator-annotations + source_gen: + git: + ref: reviver + url: https://github.com/Nexushunter/source_gen.git + path: source_gen \ No newline at end of file diff --git a/openapi-generator/test/builder_test.dart b/openapi-generator/test/builder_test.dart deleted file mode 100644 index c97b621..0000000 --- a/openapi-generator/test/builder_test.dart +++ /dev/null @@ -1,588 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; - -import 'package:build_test/build_test.dart'; -import 'package:openapi_generator/src/gen_on_spec_changes.dart'; -import 'package:openapi_generator/src/models/generator_arguments.dart'; -import 'package:openapi_generator/src/utils.dart'; -import 'package:openapi_generator_annotations/openapi_generator_annotations.dart'; -import 'package:source_gen/source_gen.dart'; -import 'package:test/expect.dart'; -import 'package:test/scaffolding.dart'; - -import 'utils.dart'; - -/// We test the build runner by mocking the specs and then checking the output -/// content for the expected generate command. -void main() { - group('generator dio', () { - test('to generate appropriate openapi cli command', () async { - expect( - await generate(''' - @Openapi( - additionalProperties: - DioProperties(pubName: 'petstore_api', pubAuthor: 'Johnny dep...'), - inputSpecFile: '../openapi-spec.yaml', - typeMappings: {'Pet': 'ExamplePet'}, - generatorName: Generator.dio, - runSourceGenOnOutput: true, - alwaysRun: true, - outputDirectory: 'api/petstore_api') - '''), - contains( - 'generate -o=api/petstore_api -i=../openapi-spec.yaml -g=dart-dio --type-mappings=Pet=ExamplePet --additional-properties=allowUnicodeIdentifiers=false,ensureUniqueParams=true,useEnumExtension=true,prependFormOrBodyParameters=false,pubAuthor=Johnny dep...,pubName=petstore_api,legacyDiscriminatorBehavior=true,sortModelPropertiesByRequiredFlag=true,sortParamsByRequiredFlag=true,wrapper=none')); - }); - - test('to generate command with import and type mappings', () async { - expect( - await generate(''' - @Openapi( - inputSpecFile: '../openapi-spec.yaml', - typeMappings: {'int-or-string':'IntOrString'}, - importMappings: {'IntOrString':'./int_or_string.dart'}, - generatorName: Generator.dio, - outputDirectory: '${testSpecPath}output', - ) - '''), - contains( - 'generate -o=${testSpecPath}output -i=../openapi-spec.yaml -g=dart-dio --import-mappings=IntOrString=./int_or_string.dart --type-mappings=int-or-string=IntOrString')); - }); - - test('to generate command with inline schema mappings', () async { - expect( - await generate(''' - @Openapi( - inputSpecFile: '../openapi-spec.yaml', - typeMappings: {'int-or-string':'IntOrString'}, - inlineSchemaNameMappings: {'inline_object_2':'SomethingMapped','inline_object_4':'nothing_new'}, - generatorName: Generator.dio, - outputDirectory: '${testSpecPath}output', - ) - '''), - contains(''' - generate -o=${testSpecPath}output -i=../openapi-spec.yaml -g=dart-dio --inline-schema-name-mappings=inline_object_2=SomethingMapped,inline_object_4=nothing_new --type-mappings=int-or-string=IntOrString - ''' - .trim())); - }); - - // test('to generate command with inline schema options', () async { - // expect(await generate(''' - // @Openapi( - // inputSpecFile: '../openapi-spec.yaml', - // inlineSchemaOptions: InlineSchemaOptions(skipSchemaReuse: true,refactorAllofInlineSchemas: true,resolveInlineEnums: true), - // generatorName: Generator.dio) - // '''), contains(''' - // generate -i ../openapi-spec.yaml -g dart-dio --type-mappings=int-or-string=IntOrString --inline-schema-name-mappings=inline_object_2=SomethingMapped,inline_object_4=nothing_new - // '''.trim())); - // }); - }); - - group('generator dioAlt', () { - test('to generate appropriate openapi cli command', () async { - expect( - await generate(''' - @Openapi( - additionalProperties: - DioProperties(pubName: 'petstore_api', pubAuthor: 'Johnny dep...'), - inputSpecFile: '../openapi-spec.yaml', - typeMappings: {'Pet': 'ExamplePet'}, - generatorName: Generator.dio, - runSourceGenOnOutput: true, - alwaysRun: true, - outputDirectory: 'api/petstore_api') - '''), - contains(''' - generate -o=api/petstore_api -i=../openapi-spec.yaml -g=dart-dio --type-mappings=Pet=ExamplePet --additional-properties=allowUnicodeIdentifiers=false,ensureUniqueParams=true,useEnumExtension=true,prependFormOrBodyParameters=false,pubAuthor=Johnny dep...,pubName=petstore_api,legacyDiscriminatorBehavior=true,sortModelPropertiesByRequiredFlag=true,sortParamsByRequiredFlag=true,wrapper=none - ''' - .trim())); - }); - - test('to generate command with import and type mappings for dioAlt', - () async { - expect( - await generate(''' - @Openapi( - inputSpecFile: '../openapi-spec.yaml', - typeMappings: {'int-or-string':'IntOrString'}, - importMappings: {'IntOrString':'./int_or_string.dart'}, - generatorName: Generator.dioAlt, - outputDirectory: '${testSpecPath}output', - ) - '''), - contains( - 'generate -o=${testSpecPath}output -i=../openapi-spec.yaml -g=dart2-api --import-mappings=IntOrString=./int_or_string.dart --type-mappings=int-or-string=IntOrString')); - }); - }); - - group('NextGen', () { - late String generatedOutput; - final specPath = - 'https://raw.githubusercontent.com/Nexushunter/tagmine-api/main/openapi.yaml'; - final basePath = '${testSpecPath}output-nextgen/'; - final f = File('${basePath}cache.json'); - tearDown(() { - final b = File(basePath); - if (b.existsSync()) b.deleteSync(recursive: true); - }); - - group('runs', () { - setUpAll(() { - if (!f.existsSync()) { - f.createSync(recursive: true); - } - f.writeAsStringSync('{}'); - }); - tearDown(() { - if (f.existsSync()) { - f.deleteSync(); - } - }); - test('fails with invalid configuration', () async { - generatedOutput = await generate(''' - @Openapi( - inputSpecFile: '$specPath', - typeMappings: {'int-or-string':'IntOrString'}, - importMappings: {'IntOrString':'./int_or_string.dart'}, - generatorName: Generator.dioAlt, - useNextGen: false, - cachePath: '${f.path}', - outputDirectory: '${f.parent.path}/invalid_config/' - ) - '''); - expect(generatedOutput, - contains('useNextGen must be set when using cachePath')); - }); - test('Logs warning when using remote spec', () async { - generatedOutput = await generate(''' - @Openapi( - inputSpecFile: '$specPath', - inputSpec: RemoteSpec(path: '$specPath'), - typeMappings: {'int-or-string':'IntOrString'}, - importMappings: {'IntOrString':'./int_or_string.dart'}, - generatorName: Generator.dioAlt, - useNextGen: true, - outputDirectory: '${f.parent.path}/logs-when-remote' - ) - '''); - expect( - generatedOutput, - contains( - 'Using a remote specification, a cache will still be create but may be outdated.')); - }); - test('when the spec is dirty', () async { - final src = ''' - @Openapi( - inputSpecFile: '$specPath', - inputSpec: RemoteSpec(path: '$specPath'), - useNextGen: true, - cachePath: '${f.path}', - outputDirectory: '${f.parent.path}/when-spec-is-dirty' - ) - '''; - generatedOutput = await generate(src); - expect( - generatedOutput, contains('Dirty Spec found. Running generation.')); - }); - test('and terminates early when there is no diff', () async { - f.writeAsStringSync( - jsonEncode(await loadSpec(specConfig: RemoteSpec(path: specPath)))); - final src = ''' - @Openapi( - inputSpecFile: '$specPath', - inputSpec: RemoteSpec(path: '$specPath'), - useNextGen: true, - cachePath: '${f.path}', - outputDirectory: '${f.parent.path}/early-term' - ) - '''; - generatedOutput = await generate(src); - expect(generatedOutput, - contains('No diff between versions, not running generator.')); - }); - test('openApiJar with expected args', () async { - f.writeAsStringSync(jsonEncode({'someKey': 'someValue'})); - final annotations = (await resolveSource( - File('$testSpecPath/next_gen_builder_test_config.dart') - .readAsStringSync(), - (resolver) async => - (await resolver.findLibraryByName('test_lib'))!)) - .getClass('TestClassConfig')! - .metadata - .map((e) => ConstantReader(e.computeConstantValue()!)) - .first; - final args = GeneratorArguments(annotations: annotations); - generatedOutput = await generate(''' - @Openapi( - inputSpecFile: - 'https://raw.githubusercontent.com/Nexushunter/tagmine-api/main/openapi.yaml', - inputSpec: RemoteSpec(path: '$specPath'), - generatorName: Generator.dio, - useNextGen: true, - cachePath: '${f.path}', - outputDirectory: './test/specs/output-nextgen/expected-args' -) - '''); - expect( - generatedOutput, contains('[ ${(await args.jarArgs).join(' ')} ]')); - }); - test('adds generated comment', () async { - f.writeAsStringSync(jsonEncode({'someKey': 'someValue'})); - final contents = File('$testSpecPath/next_gen_builder_test_config.dart') - .readAsStringSync(); - final copy = - File('./test/specs/next_gen_builder_test_config_copy.dart'); - copy.writeAsStringSync(contents, flush: true); - generatedOutput = await generate(''' - @Openapi( - inputSpecFile: - 'https://raw.githubusercontent.com/Nexushunter/tagmine-api/main/openapi.yaml', - inputSpec: RemoteSpec(path: '$specPath'), - generatorName: Generator.dio, - useNextGen: true, - cachePath: '${f.path}', - outputDirectory: './test/specs/output-nextgen/add-generated-comment' -) - ''', path: copy.path); - - var hasOutput = copy.readAsStringSync().contains(lastRunPlaceHolder); - expect(generatedOutput, contains('Creating generated timestamp with ')); - - generatedOutput = await generate(''' - @Openapi( - inputSpecFile: - 'https://raw.githubusercontent.com/Nexushunter/tagmine-api/main/openapi.yaml', - inputSpec: RemoteSpec(path: '$specPath'), - generatorName: Generator.dio, - useNextGen: true, - cachePath: '${f.path}', - outputDirectory: './test/specs/output-nextgen/add-generated-comment' -) - ''', path: copy.path); - - hasOutput = copy.readAsStringSync().contains(lastRunPlaceHolder); - expect(generatedOutput, - contains('Found generated timestamp. Updating with')); - - copy.deleteSync(); - expect(hasOutput, isTrue); - }); - group('source gen', () { - group('uses Flutter', () { - group('with wrapper', () { - test('fvm', () async { - generatedOutput = await generate(''' -@Openapi( - inputSpecFile: - 'https://raw.githubusercontent.com/Nexushunter/tagmine-api/main/openapi.yaml', - inputSpec: RemoteSpec(path: '$specPath'), - generatorName: Generator.dio, - useNextGen: true, - cachePath: '${f.path}', - outputDirectory: '${f.parent.path}/fvm', - additionalProperties: AdditionalProperties( - wrapper: Wrapper.fvm, - ), -) - '''); - expect( - generatedOutput, contains('Running source code generation.')); - expect( - generatedOutput, - contains( - 'fvm pub run build_runner build --delete-conflicting-outputs')); - }); - test('flutterw', () async { - generatedOutput = await generate(''' -@Openapi( - inputSpecFile: - 'https://raw.githubusercontent.com/Nexushunter/tagmine-api/main/openapi.yaml', - inputSpec: RemoteSpec(path: '$specPath'), - generatorName: Generator.dio, - useNextGen: true, - cachePath: '${f.path}', - outputDirectory: '${f.parent.path}/flutterw', - additionalProperties: AdditionalProperties( - wrapper: Wrapper.flutterw, - ), -) - '''); - expect( - generatedOutput, contains('Running source code generation.')); - expect( - generatedOutput, - contains( - './flutterw pub run build_runner build --delete-conflicting-outputs')); - }); - }); - test('without wrapper', () async { - final annotations = (await resolveSource( - File('$testSpecPath/next_gen_builder_flutter_test_config.dart') - .readAsStringSync(), - (resolver) async => - (await resolver.findLibraryByName('test_lib'))!)) - .getClass('TestClassConfig')! - .metadata - .map((e) => ConstantReader(e.computeConstantValue()!)) - .first; - final args = GeneratorArguments(annotations: annotations); - generatedOutput = await generate(''' -@Openapi( - inputSpecFile: - 'https://raw.githubusercontent.com/Nexushunter/tagmine-api/main/openapi.yaml', - inputSpec: RemoteSpec(path: '$specPath'), - generatorName: Generator.dio, - useNextGen: true, - cachePath: '${f.path}', - outputDirectory: '${f.parent.path}/flutter', - projectPubspecPath: './test/specs/flutter_pubspec.test.yaml', -) - '''); - - expect(args.wrapper, Wrapper.none); - expect( - generatedOutput, contains('Running source code generation.')); - expect( - generatedOutput, - contains( - 'flutter pub run build_runner build --delete-conflicting-outputs')); - }); - }); - test('uses dart', () async { - final annotations = (await resolveSource( - File('$testSpecPath/next_gen_builder_test_config.dart') - .readAsStringSync(), - (resolver) async => - (await resolver.findLibraryByName('test_lib'))!)) - .getClass('TestClassConfig')! - .metadata - .map((e) => ConstantReader(e.computeConstantValue()!)) - .first; - final args = GeneratorArguments(annotations: annotations); - generatedOutput = await generate(''' -@Openapi( - inputSpecFile: - 'https://raw.githubusercontent.com/Nexushunter/tagmine-api/main/openapi.yaml', - inputSpec: RemoteSpec(path: '$specPath'), - generatorName: Generator.dio, - useNextGen: true, - cachePath: '${f.path}', - outputDirectory: '${f.parent.path}/dart', - projectPubspecPath: './test/specs/dart_pubspec.test.yaml', -) - '''); - - expect(args.wrapper, Wrapper.none); - expect(generatedOutput, contains('Running source code generation.')); - expect( - generatedOutput, - contains( - 'dart pub run build_runner build --delete-conflicting-outputs')); - }); - group('except when', () { - test('flag is set', () async { - final annotations = (await resolveSource( - ''' -library test_lib; - -import 'package:openapi_generator_annotations/openapi_generator_annotations.dart'; - -@Openapi( - inputSpecFile: - 'https://raw.githubusercontent.com/Nexushunter/tagmine-api/main/openapi.yaml', - inputSpec: RemoteSpec(path: '$specPath'), - generatorName: Generator.dio, - useNextGen: true, - cachePath: '${f.path}', - outputDirectory: '${f.parent.path}/no-src', - runSourceGenOnOutput: false, -) -class TestClassConfig extends OpenapiGeneratorConfig {} - ''', - (resolver) async => - (await resolver.findLibraryByName('test_lib'))!)) - .getClass('TestClassConfig')! - .metadata - .map((e) => ConstantReader(e.computeConstantValue()!)) - .first; - final args = GeneratorArguments(annotations: annotations); - - expect(args.runSourceGen, isFalse); - generatedOutput = await generate(''' -@Openapi( - inputSpecFile: - 'https://raw.githubusercontent.com/Nexushunter/tagmine-api/main/openapi.yaml', - inputSpec: RemoteSpec(path: '$specPath'), - generatorName: Generator.dio, - useNextGen: true, - cachePath: '${f.path}', - outputDirectory: '${f.parent.path}/no-src', - runSourceGenOnOutput: false, -) - '''); - expect(generatedOutput, - contains('Skipping source gen step due to flag being set.')); - }); - test('generator is dart', () async { - final annotations = (await resolveSource( - ''' -library test_lib; - -import 'package:openapi_generator_annotations/openapi_generator_annotations.dart'; - -@Openapi( - inputSpecFile: - 'https://raw.githubusercontent.com/Nexushunter/tagmine-api/main/openapi.yaml', - inputSpec: RemoteSpec(path: '$specPath'), - generatorName: Generator.dart, - useNextGen: true, - cachePath: '${f.path}', - outputDirectory: '${f.parent.path}/dart-gen' -) -class TestClassConfig extends OpenapiGeneratorConfig {} - ''', - (resolver) async => - (await resolver.findLibraryByName('test_lib'))!)) - .getClass('TestClassConfig')! - .metadata - .map((e) => ConstantReader(e.computeConstantValue()!)) - .first; - final args = GeneratorArguments(annotations: annotations); - expect(args.runSourceGen, isTrue); - generatedOutput = await generate(''' -@Openapi( - inputSpecFile: - 'https://raw.githubusercontent.com/Nexushunter/tagmine-api/main/openapi.yaml', - inputSpec: RemoteSpec(path: '$specPath'), - generatorName: Generator.dart, - useNextGen: true, - cachePath: '${f.path}', - outputDirectory: '${f.parent.path}/dart-gen' -) - '''); - expect( - generatedOutput, - contains( - 'Skipping source gen because generator does not need it.')); - }); - }); - test('logs when successful', () async { - generatedOutput = await generate(''' -@Openapi( - inputSpecFile: - 'https://raw.githubusercontent.com/Nexushunter/tagmine-api/main/openapi.yaml', - inputSpec: RemoteSpec(path: '$specPath'), - generatorName: Generator.dio, - useNextGen: true, - cachePath: '${f.path}', - outputDirectory: '${f.parent.path}/success', - projectPubspecPath: './test/specs/dart_pubspec.test.yaml', -) - '''); - expect(generatedOutput, contains('Codegen completed successfully.')); - expect(generatedOutput, contains('Sources generated successfully.')); - }); - }); - group('fetch dependencies', () { - test('except when flag is present', () async { - generatedOutput = await generate(''' -@Openapi( - inputSpecFile: - 'https://raw.githubusercontent.com/Nexushunter/tagmine-api/main/openapi.yaml', - inputSpec: RemoteSpec(path: '$specPath'), - generatorName: Generator.dio, - useNextGen: true, - cachePath: '${f.path}', - outputDirectory: '${f.parent.path}/no-fetch', - projectPubspecPath: './test/specs/dart_pubspec.test.yaml', - fetchDependencies: false, -) - '''); - expect(generatedOutput, - contains('Skipping install step because flag was set.')); - }); - test('succeeds', () async { - generatedOutput = await generate(''' -@Openapi( - inputSpecFile: - 'https://raw.githubusercontent.com/Nexushunter/tagmine-api/main/openapi.yaml', - inputSpec: RemoteSpec(path: '$specPath'), - generatorName: Generator.dio, - useNextGen: true, - cachePath: '${f.path}', - outputDirectory: '${f.parent.path}/no-fetch', - projectPubspecPath: './test/specs/dart_pubspec.test.yaml', -) - '''); - expect(generatedOutput, - contains('Installing dependencies with generated source.')); - expect(generatedOutput, contains('Install completed successfully.')); - }); - }); - group('update cache', () { - final src = ''' - @Openapi( - inputSpecFile: '$specPath', - inputSpec: RemoteSpec(path: '$specPath'), - useNextGen: true, - cachePath: '${f.path}', - outputDirectory: '${f.parent.path}/update-cache', - ) - '''; - - test('creating a cache file when not found', () async { - // Ensure that other tests don't make this available; - if (f.existsSync()) { - f.deleteSync(); - } - expect(f.existsSync(), isFalse); - generatedOutput = await generate(src); - expect(f.existsSync(), isTrue); - expect(jsonDecode(f.readAsStringSync()), - await loadSpec(specConfig: RemoteSpec(path: specPath))); - }); - test('updates the cache file when found', () async { - f.writeAsStringSync(jsonEncode({'someKey': 'someValue'})); - expect(f.existsSync(), isTrue); - generatedOutput = await generate(src); - final expectedSpec = - await loadSpec(specConfig: RemoteSpec(path: specPath)); - final actualSpec = jsonDecode(f.readAsStringSync()); - expect(actualSpec, expectedSpec); - }); - test('logs when successful', () async { - f.writeAsStringSync(jsonEncode({'someKey': 'someValue'})); - generatedOutput = await generate(src); - expect( - generatedOutput, contains('Successfully cached spec changes.')); - }); - }); - test('uses AWS', () async { - generatedOutput = await generate(''' -@Openapi( - inputSpecFile: '', - inputSpec: RemoteSpec( - path: - 'http://bucket.s3.us-east-1.localhost.localstack.cloud:4566/openapi.yaml', - headerDelegate: AWSRemoteSpecHeaderDelegate( - bucket: 'bucket', - accessKeyId: 'test', - secretAccessKey: 'test', - ), - ), - generatorName: Generator.dio, - useNextGen: true, - cachePath: '${f.path}', - outputDirectory: '${f.parent.path}/uses-aws-spec', - projectPubspecPath: './test/specs/dart_pubspec.test.yaml', -) - '''); - - expect(generatedOutput, contains('Running source code generation.')); - expect( - generatedOutput, - contains( - 'dart pub run build_runner build --delete-conflicting-outputs')); - }); - }); - }); -} diff --git a/openapi-generator/test/command_test.dart b/openapi-generator/test/command_test.dart index be502a7..23967fa 100644 --- a/openapi-generator/test/command_test.dart +++ b/openapi-generator/test/command_test.dart @@ -1,7 +1,11 @@ +import 'dart:io'; + import 'package:openapi_generator/src/models/command.dart'; import 'package:openapi_generator_annotations/openapi_generator_annotations.dart'; import 'package:test/test.dart'; +import 'utils.dart'; + void main() { group('Command', () { final testArgs = ['pub', 'get']; @@ -32,4 +36,80 @@ void main() { expect(command.executable, 'dart'); }); }); + + group('CommandRunner', () { + final runner = CommandRunner(); + test( + 'runCommand returns a process', + () async => expect( + await runner.runCommand( + command: Command(executable: 'dart', arguments: ['--version']), + workingDirectory: './'), + isA(), + ), + ); + test('loads an annotated file', () async { + expect( + await runner.loadAnnotatedFile( + path: '$testSpecPath/next_gen_builder_test_config.dart'), + '''library test_lib; + +import 'package:openapi_generator_annotations/openapi_generator_annotations.dart'; + +@Openapi( + inputSpecFile: + 'https://raw.githubusercontent.com/Nexushunter/tagmine-api/main/openapi.yaml', + inputSpec: RemoteSpec( + path: + 'https://raw.githubusercontent.com/Nexushunter/tagmine-api/main/openapi.yaml', + ), + generatorName: Generator.dio, + useNextGen: true, + cachePath: './test/specs/output-nextgen/expected-args/cache.json', + outputDirectory: './test/specs/output-nextgen/expected-args') +class TestClassConfig {}''' + .split('\n'), + ); + }); + test( + 'writes annotation file', + () async => expect( + runner.writeAnnotatedFile( + path: '$testSpecPath/next_gen_builder_test_config.dart', + content: '''library test_lib; + +import 'package:openapi_generator_annotations/openapi_generator_annotations.dart'; + +@Openapi( + inputSpecFile: + 'https://raw.githubusercontent.com/Nexushunter/tagmine-api/main/openapi.yaml', + inputSpec: RemoteSpec( + path: + 'https://raw.githubusercontent.com/Nexushunter/tagmine-api/main/openapi.yaml', + ), + generatorName: Generator.dio, + useNextGen: true, + cachePath: './test/specs/output-nextgen/expected-args/cache.json', + outputDirectory: './test/specs/output-nextgen/expected-args') +class TestClassConfig {}''' + .split('\n')), + completes)); + test( + 'writes cache spec file', + () async => expect( + runner.cacheSpecFile( + updatedSpec: {}, + cachedPath: '$testSpecPath/commands-cache.json'), + completes)); + test( + 'checks for flutter environment', + () async => expect( + await runner.checkForFlutterEnvironemt(wrapper: Wrapper.fvm), + isTrue)); + test( + 'checks spec dirty status', + () async => expect( + await runner.isSpecFileDirty(cachedSpec: {}, loadedSpec: {}), + isFalse)); + }); } diff --git a/openapi-generator/test/generator_arguments_test.dart b/openapi-generator/test/generator_arguments_test.dart index 53712e9..622b618 100644 --- a/openapi-generator/test/generator_arguments_test.dart +++ b/openapi-generator/test/generator_arguments_test.dart @@ -1,19 +1,18 @@ import 'dart:io'; -import 'package:build_test/build_test.dart'; import 'package:openapi_generator/src/models/generator_arguments.dart'; import 'package:openapi_generator/src/models/output_message.dart'; import 'package:openapi_generator/src/utils.dart'; import 'package:openapi_generator_annotations/openapi_generator_annotations.dart'; -import 'package:source_gen/source_gen.dart' as src_gen; import 'package:test/test.dart'; void main() { group('GeneratorArguments', () { group('defaults', () { late GeneratorArguments args; - setUpAll(() => - args = GeneratorArguments(annotations: src_gen.ConstantReader(null))); + setUpAll(() => args = GeneratorArguments( + annotation: + Openapi(inputSpecFile: '', generatorName: Generator.dart))); test('alwaysRun', () => expect(args.alwaysRun, isFalse)); test('useNextGen', () => expect(args.useNextGen, isFalse)); test('cachePath', () => expect(args.cachePath, defaultCachedPath)); @@ -82,9 +81,11 @@ void main() { }); }); group('accepts overrides', () { - final annos = src_gen.ConstantReader(null); final args = GeneratorArguments( - annotations: annos, + annotation: Openapi( + inputSpecFile: '', + generatorName: Generator.dart, + ), alwaysRun: true, useNextGen: true, cachePath: 'test', @@ -100,7 +101,9 @@ void main() { typeMapping: {'package': 'type'}, reservedWordsMapping: {'const': 'final'}, inlineSchemaNameMapping: {'L': 'R'}, - additionalProperties: AdditionalProperties(wrapper: Wrapper.fvm), + additionalProperties: AdditionalProperties( + wrapper: Wrapper.fvm, + ), pubspecPath: 'testing/pubspec.yaml', ); test('alwaysRun', () => expect(args.alwaysRun, isTrue)); @@ -153,155 +156,59 @@ void main() { ), ); }); - group('annotation specification', () { - // https://github.com/gibahjoe/openapi-generator-dart/issues/110 - test('Processes annotations correctly', () async { - final config = File( - '${Directory.current.path}${Platform.pathSeparator}test${Platform.pathSeparator}specs${Platform.pathSeparator}test_config.dart') - .readAsStringSync(); - final annotations = (await resolveSource( - config, - (resolver) async => - (await resolver.findLibraryByName('test_lib'))!)) - .getClass('TestClassConfig')! - .metadata - .map((e) => src_gen.ConstantReader(e.computeConstantValue()!)) - .first; - final args = GeneratorArguments(annotations: annotations); - expect(args.alwaysRun, isTrue); - expect(args.useNextGen, isTrue); - expect(args.cachePath, './test/specs/output/cache.json'); - expect(args.outputDirectory, './test/specs/output'); - expect(args.runSourceGen, isTrue); - expect(args.shouldFetchDependencies, isTrue); - expect(args.skipValidation, isFalse); - expect(await args.inputFileOrFetch, './test/specs/openapi.test.yaml'); - expect(args.templateDirectory, 'template'); - expect(args.generator, Generator.dio); - expect(args.wrapper, Wrapper.fvm); - expect(args.importMappings, {'package': 'test'}); - expect(args.typeMappings, {'key': 'value'}); - expect(args.reservedWordsMappings, {'const': 'final'}); - expect(args.inlineSchemaNameMappings, {'200resp': 'OkResp'}); - expect(args.pubspecPath, './test/specs/dart_pubspec.test.yaml'); - expect(args.isRemote, isFalse); - expect(args.generatorName, 'dart-dio'); - expect(args.shouldGenerateSources, isTrue); - expect(args.additionalProperties?.useEnumExtension, isTrue); - expect(args.additionalProperties?.pubAuthor, 'test author'); - expect(await args.jarArgs, [ - 'generate', - '-o=${args.outputDirectory}', - '-i=${await args.inputFileOrFetch}', - '-t=${args.templateDirectory}', - '-g=${args.generatorName}', - '--reserved-words-mappings=${args.reservedWordsMappings.entries.fold('', foldStringMap())}', - '--inline-schema-name-mappings=${args.inlineSchemaNameMappings.entries.fold('', foldStringMap())}', - '--import-mappings=${args.importMappings.entries.fold('', foldStringMap())}', - '--type-mappings=${args.typeMappings.entries.fold('', foldStringMap())}', - '--additional-properties=${args.additionalProperties!.toMap().entries.fold('', foldStringMap(keyModifier: convertToPropertyKey))}' - ]); - }); - test('Processes annotation with DioProperties correctly', () async { - final config = File( - '${Directory.current.path}${Platform.pathSeparator}test${Platform.pathSeparator}specs${Platform.pathSeparator}dio_properties_test_config.dart') - .readAsStringSync(); - final annotations = (await resolveSource( - config, - (resolver) async => - (await resolver.findLibraryByName('test_lib'))!)) - .getClass('DioPropertiesTestConfig')! - .metadata - .map((e) => src_gen.ConstantReader(e.computeConstantValue()!)) - .first; - final args = GeneratorArguments(annotations: annotations); - expect(args.alwaysRun, isTrue); - expect(args.useNextGen, isTrue); - expect(args.cachePath, './test/specs/output/cache.json'); - expect(args.outputDirectory, './test/specs/output'); - expect(args.runSourceGen, isTrue); - expect(args.shouldFetchDependencies, isTrue); - expect(args.skipValidation, isFalse); - expect(await args.inputFileOrFetch, './test/specs/openapi.test.yaml'); - expect(args.templateDirectory, 'template'); - expect(args.generator, Generator.dio); - expect(args.wrapper, Wrapper.fvm); - expect(args.importMappings, {'package': 'test'}); - expect(args.typeMappings, {'key': 'value'}); - expect(args.reservedWordsMappings, {'const': 'final'}); - expect(args.inlineSchemaNameMappings, {'200resp': 'OkResp'}); - expect(args.pubspecPath, './test/specs/dart_pubspec.test.yaml'); - expect(args.isRemote, isFalse); - expect(args.generatorName, 'dart-dio'); - expect(args.shouldGenerateSources, isTrue); - expect(args.additionalProperties?.useEnumExtension, isTrue); - expect((args.additionalProperties as DioProperties?)?.nullableFields, - isTrue); - expect(await args.jarArgs, [ - 'generate', - '-o=${args.outputDirectory}', - '-i=${await args.inputFileOrFetch}', - '-t=${args.templateDirectory}', - '-g=${args.generatorName}', - '--reserved-words-mappings=${args.reservedWordsMappings.entries.fold('', foldStringMap())}', - '--inline-schema-name-mappings=${args.inlineSchemaNameMappings.entries.fold('', foldStringMap())}', - '--import-mappings=${args.importMappings.entries.fold('', foldStringMap())}', - '--type-mappings=${args.typeMappings.entries.fold('', foldStringMap())}', - '--additional-properties=${args.additionalProperties!.toMap().entries.fold('', foldStringMap(keyModifier: convertToPropertyKey))}' - ]); - }); - test('Processes annotation with DioAltProperties correctly', () async { - final config = File( - '${Directory.current.path}${Platform.pathSeparator}test${Platform.pathSeparator}specs${Platform.pathSeparator}dio_alt_properties_test_config.dart') - .readAsStringSync(); - final annotations = (await resolveSource( - config, - (resolver) async => - (await resolver.findLibraryByName('test_lib'))!)) - .getClass('DioAltPropertiesTestConfig')! - .metadata - .map((e) => src_gen.ConstantReader(e.computeConstantValue()!)) - .first; - final args = GeneratorArguments(annotations: annotations); - expect(args.alwaysRun, isTrue); - expect(args.useNextGen, isTrue); - expect(args.cachePath, './test/specs/output/cache.json'); - expect(args.outputDirectory, './test/specs/output'); - expect(args.runSourceGen, isTrue); - expect(args.shouldFetchDependencies, isTrue); - expect(args.skipValidation, isFalse); - expect(await args.inputFileOrFetch, './test/specs/openapi.test.yaml'); - expect(args.templateDirectory, 'template'); - expect(args.generator, Generator.dio); - expect(args.wrapper, Wrapper.fvm); - expect(args.importMappings, {'package': 'test'}); - expect(args.typeMappings, {'key': 'value'}); - expect(args.reservedWordsMappings, {'const': 'final'}); - expect(args.inlineSchemaNameMappings, {'200resp': 'OkResp'}); - expect(args.pubspecPath, './test/specs/dart_pubspec.test.yaml'); - expect(args.isRemote, isFalse); - expect(args.generatorName, 'dart-dio'); - expect(args.shouldGenerateSources, isTrue); - expect(args.additionalProperties?.useEnumExtension, isTrue); - expect( - (args.additionalProperties as DioAltProperties?)?.nullSafe, isTrue); - expect( - (args.additionalProperties as DioAltProperties?) - ?.nullSafeArrayDefault, - isTrue); - expect(await args.jarArgs, [ - 'generate', - '-o=${args.outputDirectory}', - '-i=${await args.inputFileOrFetch}', - '-t=${args.templateDirectory}', - '-g=${args.generatorName}', - '--reserved-words-mappings=${args.reservedWordsMappings.entries.fold('', foldStringMap())}', - '--inline-schema-name-mappings=${args.inlineSchemaNameMappings.entries.fold('', foldStringMap())}', - '--import-mappings=${args.importMappings.entries.fold('', foldStringMap())}', - '--type-mappings=${args.typeMappings.entries.fold('', foldStringMap())}', - '--additional-properties=${args.additionalProperties!.toMap().entries.fold('', foldStringMap(keyModifier: convertToPropertyKey))}' - ]); - }); + test('uses config', () async { + final annotation = Openapi( + inputSpecFile: './openapi.test.yaml', + inputSpec: InputSpec(path: './test/specs/openapi.test.yaml'), + generatorName: Generator.dio, + useNextGen: true, + cachePath: './test/specs/output/cache.json', + typeMappings: {'key': 'value'}, + templateDirectory: 'template', + alwaysRun: true, + outputDirectory: './test/specs/output', + runSourceGenOnOutput: true, + apiPackage: 'test', + skipSpecValidation: false, + importMappings: {'package': 'test'}, + reservedWordsMappings: {'const': 'final'}, + additionalProperties: AdditionalProperties(wrapper: Wrapper.fvm), + inlineSchemaNameMappings: {'200resp': 'OkResp'}, + overwriteExistingFiles: true, + projectPubspecPath: './test/specs/dart_pubspec.test.yaml', + ); + final args = GeneratorArguments(annotation: annotation); + expect(args.alwaysRun, isTrue); + expect(args.useNextGen, isTrue); + expect(args.cachePath, './test/specs/output/cache.json'); + expect(args.outputDirectory, './test/specs/output'); + expect(args.runSourceGen, isTrue); + expect(args.shouldFetchDependencies, isTrue); + expect(args.skipValidation, isFalse); + expect(await args.inputFileOrFetch, './test/specs/openapi.test.yaml'); + expect(args.templateDirectory, 'template'); + expect(args.generator, Generator.dio); + expect(args.wrapper, Wrapper.fvm); + expect(args.importMappings, {'package': 'test'}); + expect(args.typeMappings, {'key': 'value'}); + expect(args.reservedWordsMappings, {'const': 'final'}); + expect(args.inlineSchemaNameMappings, {'200resp': 'OkResp'}); + expect(args.pubspecPath, './test/specs/dart_pubspec.test.yaml'); + expect(args.isRemote, isFalse); + expect(args.generatorName, 'dart-dio'); + expect(args.shouldGenerateSources, isTrue); + expect(await args.jarArgs, [ + 'generate', + '-o=${args.outputDirectory}', + '-i=${await args.inputFileOrFetch}', + '-t=${args.templateDirectory}', + '-g=${args.generatorName}', + '--reserved-words-mappings=${args.reservedWordsMappings.entries.fold('', foldStringMap())}', + '--inline-schema-name-mappings=${args.inlineSchemaNameMappings.entries.fold('', foldStringMap())}', + '--import-mappings=${args.importMappings.entries.fold('', foldStringMap())}', + '--type-mappings=${args.typeMappings.entries.fold('', foldStringMap())}', + '--additional-properties=${args.additionalProperties!.toMap().entries.fold('', foldStringMap(keyModifier: convertToPropertyKey))}' + ]); }); }); } diff --git a/openapi-generator/test/generator_test.dart b/openapi-generator/test/generator_test.dart new file mode 100644 index 0000000..efa9937 --- /dev/null +++ b/openapi-generator/test/generator_test.dart @@ -0,0 +1,1100 @@ +import 'dart:io'; + +import 'package:analyzer/dart/element/type.dart'; +import 'package:logging/logging.dart'; +import 'package:mockito/mockito.dart'; +import 'package:openapi_generator/src/models/command.dart'; +import 'package:openapi_generator/src/models/generator_arguments.dart'; +import 'package:openapi_generator/src/models/output_message.dart'; +import 'package:openapi_generator/src/openapi_generator_runner.dart'; +import 'package:openapi_generator/src/utils.dart'; +import 'package:openapi_generator_annotations/openapi_generator_annotations.dart'; +import 'package:source_gen/source_gen.dart' as src_gen; +import 'package:test/test.dart'; + +import 'mocks.mocks.dart'; +import 'utils.dart'; + +void main() { + group('OpenApiGenerator', () { + group('NextGen', () { + late src_gen.ConstantReader defaultAnnotations; + late Openapi annotation; + late GeneratorArguments realArguments; + late MockGeneratorArguments mockedArgs; + late MockCommandRunner mockRunner; + final logger = Logger('TestOpenApiGenerator'); + setUpAll(() async { + resetMockitoState(); + mockedArgs = MockGeneratorArguments(); + mockRunner = MockCommandRunner(); + defaultAnnotations = + await loadAnnoation('next_gen_builder_test_config.dart'); + annotation = src_gen.Reviver(defaultAnnotations).toInstance(); + realArguments = GeneratorArguments(annotation: annotation); + }); + + test('should have banner logger', () async { + final logs = []; + logger.onRecord.listen(logs.add); + try { + await OpenapiGenerator(logger: logger).generateForAnnotatedElement( + MockMethodElement(), defaultAnnotations, MockBuildStep()); + fail('Should throw when not ClassElement'); + } catch (_, __) { + expect(logs.length, 1); + expect( + logs[0].message, + contains([ + ':::::::::::::::::::::::::::::::::::::::::::', + ':: Openapi generator for dart ::', + ':::::::::::::::::::::::::::::::::::::::::::', + ].join('\n'))); + expect(logs[0].level, Level.INFO); + } + }); + + test('throws InvalidGenerationSourceError when not a class', () async { + try { + await OpenapiGenerator().generateForAnnotatedElement( + MockMethodElement(), defaultAnnotations, MockBuildStep()); + fail('Should throw when not ClassElement'); + } catch (e, _) { + expect(e, isA()); + e as src_gen.InvalidGenerationSourceError; + expect(e.message, 'Generator cannot target ``.'); + expect(e.todo, 'Remove the [Openapi] annotation from ``.'); + } + }); + + test('throws AssertionError when useCache is set but useNextGen is not', + () async { + try { + await OpenapiGenerator().generateForAnnotatedElement( + MockClassElement(), + await loadAnnoation('next_gen_builder_test_invalid_config.dart'), + MockBuildStep()); + fail('Should throw when useNextGen is false and cache path is set.'); + } catch (e, _) { + expect(e, isA()); + e as AssertionError; + expect(e.message, 'useNextGen must be set when using cachePath'); + } + }); + + group('logs which enviroment being used', () { + setUpAll(() => resetMockitoState()); + test('dart when wrapper is none', () async { + final logs = []; + logger.onRecord.listen(logs.add); + when(mockRunner.checkForFlutterEnvironemt( + wrapper: anyNamed('wrapper'), + providedPubspecPath: anyNamed('providedPubspecPath'))) + .thenAnswer((_) async => false); + + await OpenapiGenerator(logger: logger, runner: mockRunner) + .generateForAnnotatedElement( + MockClassElement(), defaultAnnotations, MockBuildStep()); + + expect(logs[1].message, 'Using dart environemnt'); + }); + test('flutter when wrapper is fvm', () async { + final logs = []; + logger.onRecord.listen(logs.add); + final annotations = + await loadAnnoation('next_gen_builder_fvm_test_config.dart'); + when(mockRunner.checkForFlutterEnvironemt( + wrapper: argThat( + TypeMatcher() + .having((e) => e, 'name', Wrapper.fvm), + named: 'wrapper', + ), + providedPubspecPath: anyNamed('providedPubspecPath'))) + .thenAnswer((_) async => true); + + await OpenapiGenerator(logger: logger, runner: mockRunner) + .generateForAnnotatedElement( + MockClassElement(), annotations, MockBuildStep()); + + expect(logs[1].message, 'Using flutter environemnt'); + }); + test('flutter when wrapper is ./flutter', () async { + final logs = []; + logger.onRecord.listen(logs.add); + final annotations = + await loadAnnoation('next_gen_builder_flutterw_test_config.dart'); + + when(mockRunner.checkForFlutterEnvironemt( + wrapper: argThat( + TypeMatcher() + .having((e) => e, 'name', Wrapper.flutterw), + named: 'wrapper', + ), + providedPubspecPath: anyNamed('providedPubspecPath'))) + .thenAnswer((_) async => true); + + await OpenapiGenerator(logger: logger, runner: mockRunner) + .generateForAnnotatedElement( + MockClassElement(), annotations, MockBuildStep()); + + expect(logs[1].message, 'Using flutter environemnt'); + }); + test('when defined in pubspec', () async { + final logs = []; + logger.onRecord.listen(logs.add); + final annotations = + await loadAnnoation('next_gen_builder_flutter_test_config.dart'); + + when( + mockRunner.checkForFlutterEnvironemt( + wrapper: anyNamed('wrapper'), + providedPubspecPath: argThat( + contains('./test/specs/flutter_pubspec.test.yaml'), + named: 'providedPubspecPath', + ), + ), + ).thenAnswer((_) async => true); + + await OpenapiGenerator(logger: logger, runner: mockRunner) + .generateForAnnotatedElement( + MockClassElement(), annotations, MockBuildStep()); + + expect(logs[1].message, 'Using flutter environemnt'); + }); + }); + + group('uses correct generator', () { + test('dart', () async { + final logs = []; + logger.onRecord.listen(logs.add); + + when(mockRunner.runCommand( + command: anyNamed('command'), + workingDirectory: anyNamed('workingDirectory'))) + .thenAnswer( + (_) async => ProcessResult(999, 0, 'stdout', 'stderr')); + + when(mockRunner.isSpecFileDirty( + cachedSpec: anyNamed('cachedSpec'), + loadedSpec: anyNamed('loadedSpec'))) + .thenAnswer((_) async => true); + final args = GeneratorArguments( + annotation: annotation, generator: Generator.dart); + await OpenapiGenerator(logger: logger, runner: mockRunner) + .generatorV2( + args: args, + baseCommand: 'dart', + annotatedPath: 'annotatedPath'); + final generationLogIndex = logs.indexWhere((element) => + element.message.contains( + 'Running following command to generate openapi client - ')); + logs.forEach((element) { + print(element.message); + }); + final log = logs[generationLogIndex]; + expect(log.message, contains('-g=dart')); + final sourceGenLogIndex = logs + .indexWhere((element) => element.message.contains('source gen')); + expect(logs[sourceGenLogIndex].message, + 'Skipping source gen because generator does not need it.'); + }); + test('dio', () async { + final logs = []; + logger.onRecord.listen(logs.add); + + when(mockRunner.runCommand( + command: anyNamed('command'), + workingDirectory: anyNamed('workingDirectory'))) + .thenAnswer( + (_) async => ProcessResult(999, 0, 'stdout', 'stderr')); + + when(mockRunner.isSpecFileDirty( + cachedSpec: anyNamed('cachedSpec'), + loadedSpec: anyNamed('loadedSpec'))) + .thenAnswer((_) async => true); + final args = GeneratorArguments(annotation: annotation); + await OpenapiGenerator(logger: logger, runner: mockRunner) + .generatorV2( + args: args, + baseCommand: 'dart', + annotatedPath: 'annotatedPath'); + final generationLogIndex = logs.indexWhere((element) => + element.message.contains( + 'Running following command to generate openapi client - ')); + logs.forEach((element) { + print(element.message); + }); + final log = logs[generationLogIndex]; + expect(log.message, contains('-g=dart-dio')); + }); + test('dioAlt', () async { + final logs = []; + logger.onRecord.listen(logs.add); + + when(mockRunner.runCommand( + command: anyNamed('command'), + workingDirectory: anyNamed('workingDirectory'))) + .thenAnswer( + (_) async => ProcessResult(999, 0, 'stdout', 'stderr')); + + when(mockRunner.isSpecFileDirty( + cachedSpec: anyNamed('cachedSpec'), + loadedSpec: anyNamed('loadedSpec'))) + .thenAnswer((_) async => true); + final args = GeneratorArguments( + annotation: Openapi( + inputSpecFile: + 'https://raw.githubusercontent.com/Nexushunter/tagmine-api/main/openapi.yaml', + inputSpec: RemoteSpec( + path: + 'https://raw.githubusercontent.com/Nexushunter/tagmine-api/main/openapi.yaml', + ), + generatorName: Generator.dioAlt, + useNextGen: true, + cachePath: + './test/specs/output-nextgen/expected-args/cache.json', + outputDirectory: './test/specs/output-nextgen/expected-args'), + ); + await OpenapiGenerator(logger: logger, runner: mockRunner) + .generatorV2( + args: args, + baseCommand: 'dart', + annotatedPath: 'annotatedPath'); + final generationLogIndex = logs.indexWhere((element) => + element.message.contains( + 'Running following command to generate openapi client - ')); + logs.forEach((element) { + print(element.message); + }); + final log = logs[generationLogIndex]; + expect(log.message, contains('-g=dart2-api')); + }); + }); + + group('generatorV2', () { + group('completes successfully', () { + late OpenapiGenerator generator; + setUpAll(() { + resetMockitoState(); + generator = OpenapiGenerator(logger: logger, runner: mockRunner); + when(mockRunner.runCommand( + command: anyNamed('command'), + workingDirectory: anyNamed('workingDirectory'))) + .thenAnswer((realInvocation) => + Future.value(ProcessResult(999, 0, 'success', ''))); + when(mockRunner.writeAnnotatedFile( + path: anyNamed('path'), content: anyNamed('content'))) + .thenAnswer((realInvocation) => Future.value()); + }); + + test('no diff', () async { + final logs = []; + logger.onRecord.listen(logs.add); + when(mockRunner.loadSpecFile( + specConfig: anyNamed('specConfig'), isCached: true)) + .thenAnswer((realInvocation) => Future.value({})); + when(mockRunner.loadSpecFile(specConfig: anyNamed('specConfig'))) + .thenAnswer((realInvocation) => Future.value({})); + when(mockRunner.isSpecFileDirty( + cachedSpec: anyNamed('cachedSpec'), + loadedSpec: anyNamed('loadedSpec'))) + .thenAnswer((realInvocation) => Future.value(false)); + when(mockedArgs.isRemote).thenReturn(false); + try { + await generator.generatorV2( + args: mockedArgs, + baseCommand: 'dart', + annotatedPath: 'annotatedPath'); + expect(logs.length, 4); + expect(logs[1].message, + 'No diff between versions, not running generator.'); + expect(logs[1].level, Level.INFO); + } catch (e, _) { + fail('should not have thrown.'); + } + }); + test('has diff', () async { + // setup + final logs = []; + logger.onRecord.listen(logs.add); + when(mockRunner.loadSpecFile( + specConfig: anyNamed('specConfig'), isCached: true)) + .thenAnswer((realInvocation) => Future.value({})); + when(mockRunner.loadSpecFile(specConfig: anyNamed('specConfig'))) + .thenAnswer((realInvocation) => Future.value({})); + when(mockRunner.isSpecFileDirty( + cachedSpec: anyNamed('cachedSpec'), + loadedSpec: anyNamed('loadedSpec'))) + .thenAnswer((realInvocation) => Future.value(true)); + when(mockedArgs.isRemote).thenReturn(false); + when(mockedArgs.runSourceGen).thenReturn(true); + when(mockedArgs.shouldGenerateSources).thenReturn(true); + when(mockedArgs.shouldFetchDependencies).thenReturn(true); + when(mockRunner.cacheSpecFile( + updatedSpec: anyNamed('updatedSpec'), + cachedPath: anyNamed('cachedPath'))) + .thenAnswer((_) => Future.value(VoidType)); + when(mockRunner.runCommand( + command: anyNamed('command'), + workingDirectory: anyNamed('workingDirectory'))) + .thenAnswer((realInvocation) => + Future.value(ProcessResult(999, 0, 'success', ''))); + + when(mockRunner.loadAnnotatedFile(path: anyNamed('path'))) + .thenAnswer((realInvocation) => + Future.value(['cant be empty or throws'])); + when(mockRunner.writeAnnotatedFile( + path: anyNamed('path'), content: anyNamed('content'))) + .thenAnswer((realInvocation) => Future.value(VoidType)); + + // execution + await generator.generatorV2( + args: mockedArgs, + baseCommand: 'dart', + annotatedPath: 'annotatedPath'); + expect(logs.length, 15); + }); + }); + group('logs', () { + late OpenapiGenerator generator; + setUpAll(() { + resetMockitoState(); + generator = OpenapiGenerator(logger: logger, runner: mockRunner); + }); + test('warning when using remote spec', () async { + final logs = []; + logger.onRecord.listen(logs.add); + when(mockRunner.loadSpecFile( + specConfig: anyNamed('specConfig'), isCached: true)) + .thenAnswer((realInvocation) => Future.value({})); + when(mockRunner.loadSpecFile(specConfig: anyNamed('specConfig'))) + .thenAnswer((realInvocation) => Future.value({})); + when(mockRunner.isSpecFileDirty( + cachedSpec: anyNamed('cachedSpec'), + loadedSpec: anyNamed('loadedSpec'))) + .thenAnswer((realInvocation) => Future.value(false)); + when(mockedArgs.isRemote).thenReturn(true); + when(mockRunner.runCommand( + command: anyNamed('command'), + workingDirectory: anyNamed('workingDirectory'))) + .thenAnswer((realInvocation) => + Future.value(ProcessResult(999, 0, 'success', ''))); + when(mockRunner.writeAnnotatedFile( + path: anyNamed('path'), content: anyNamed('content'))) + .thenAnswer((realInvocation) => Future.value()); + try { + await generator.generatorV2( + args: mockedArgs, + baseCommand: 'dart', + annotatedPath: 'annotatedPath'); + expect(logs.length, 6); + expect(logs[0].message, + 'Using a remote specification, a cache will still be create but may be outdated.'); + expect(logs[0].level, Level.WARNING); + } catch (e, _) { + print(e); + fail('should not have thrown.'); + } + }); + test('when no cache is found', () async { + // setup + final logs = []; + logger.onRecord.listen(logs.add); + when(mockRunner.loadSpecFile( + specConfig: anyNamed('specConfig'), isCached: true)) + .thenAnswer((realInvocation) => Future.value({})); + when(mockRunner.loadSpecFile(specConfig: anyNamed('specConfig'))) + .thenAnswer((realInvocation) => Future.value({})); + when(mockRunner.isSpecFileDirty( + cachedSpec: anyNamed('cachedSpec'), + loadedSpec: anyNamed('loadedSpec'))) + .thenAnswer((realInvocation) => Future.value(true)); + when(mockedArgs.isRemote).thenReturn(false); + when(mockedArgs.hasLocalCache).thenReturn(false); + when(mockRunner.runCommand( + command: anyNamed('command'), + workingDirectory: anyNamed('workingDirectory'))) + .thenAnswer((realInvocation) => + Future.value(ProcessResult(999, 0, 'success', ''))); + + when(mockRunner.loadAnnotatedFile(path: anyNamed('path'))) + .thenAnswer((realInvocation) => + Future.value(['cant be empty or throws'])); + when(mockRunner.writeAnnotatedFile( + path: anyNamed('path'), content: anyNamed('content'))) + .thenAnswer((realInvocation) => Future.value(VoidType)); + + // execution + await generator.generatorV2( + args: mockedArgs, + baseCommand: 'dart', + annotatedPath: 'annotatedPath'); + expect(logs.length, 15); + + final recordIndex = logs.indexWhere((element) => + element.message == 'No local cache found. Creating one.'); + expect(recordIndex, greaterThan(-1)); + expect(logs[recordIndex].level, Level.INFO); + }); + test('when cache is found', () async { + // setup + final logs = []; + logger.onRecord.listen(logs.add); + when(mockRunner.loadSpecFile( + specConfig: anyNamed('specConfig'), isCached: true)) + .thenAnswer((realInvocation) => Future.value({})); + when(mockRunner.loadSpecFile(specConfig: anyNamed('specConfig'))) + .thenAnswer((realInvocation) => Future.value({})); + when(mockRunner.isSpecFileDirty( + cachedSpec: anyNamed('cachedSpec'), + loadedSpec: anyNamed('loadedSpec'))) + .thenAnswer((realInvocation) => Future.value(true)); + when(mockedArgs.isRemote).thenReturn(false); + when(mockedArgs.hasLocalCache).thenReturn(true); + when(mockRunner.runCommand( + command: anyNamed('command'), + workingDirectory: anyNamed('workingDirectory'))) + .thenAnswer((realInvocation) => + Future.value(ProcessResult(999, 0, 'success', ''))); + + when(mockRunner.loadAnnotatedFile(path: anyNamed('path'))) + .thenAnswer((realInvocation) => + Future.value(['cant be empty or throws'])); + when(mockRunner.writeAnnotatedFile( + path: anyNamed('path'), content: anyNamed('content'))) + .thenAnswer((realInvocation) => Future.value(VoidType)); + + // execution + await generator.generatorV2( + args: mockedArgs, + baseCommand: 'dart', + annotatedPath: 'annotatedPath'); + expect(logs.length, 15); + + final recordIndex = logs.indexWhere((element) => + element.message == + 'Local cache found. Overwriting existing one.'); + expect(recordIndex, greaterThan(-1)); + expect(logs[recordIndex].level, Level.INFO); + }); + group('on failure', () { + test('has diff', () async { + // setup + final logs = []; + logger.onRecord.listen(logs.add); + when(mockRunner.loadSpecFile( + specConfig: anyNamed('specConfig'), isCached: true)) + .thenAnswer((realInvocation) => Future.value({})); + when(mockRunner.loadSpecFile(specConfig: anyNamed('specConfig'))) + .thenAnswer((realInvocation) => Future.value({})); + when(mockRunner.isSpecFileDirty( + cachedSpec: anyNamed('cachedSpec'), + loadedSpec: anyNamed('loadedSpec'))) + .thenThrow('uh oh'); + when(mockedArgs.isRemote).thenReturn(false); + when(mockedArgs.runSourceGen).thenReturn(true); + when(mockedArgs.shouldGenerateSources).thenReturn(true); + when(mockedArgs.shouldFetchDependencies).thenReturn(true); + when(mockRunner.cacheSpecFile( + updatedSpec: anyNamed('updatedSpec'), + cachedPath: anyNamed('cachedPath'))) + .thenAnswer((_) => Future.value(VoidType)); + when(mockRunner.runCommand( + command: anyNamed('command'), + workingDirectory: anyNamed('workingDirectory'))) + .thenAnswer((realInvocation) => + Future.value(ProcessResult(999, 0, 'success', ''))); + + when(mockRunner.loadAnnotatedFile(path: anyNamed('path'))) + .thenAnswer((realInvocation) => + Future.value(['cant be empty or throws'])); + when(mockRunner.writeAnnotatedFile( + path: anyNamed('path'), content: anyNamed('content'))) + .thenAnswer((realInvocation) => Future.value(VoidType)); + + // execution + await generator.generatorV2( + args: mockedArgs, + baseCommand: 'dart', + annotatedPath: 'annotatedPath'); + expect(logs.length, 5); + expect(logs[1].message, 'Failed to generate content.'); + expect(logs[1].level, Level.SEVERE); + }); + test('fails to format', () async { + // setup + final logs = []; + logger.onRecord.listen(logs.add); + when(mockRunner.loadSpecFile( + specConfig: anyNamed('specConfig'), isCached: true)) + .thenAnswer((realInvocation) => Future.value({})); + when(mockRunner.loadSpecFile(specConfig: anyNamed('specConfig'))) + .thenAnswer((realInvocation) => Future.value({})); + when(mockRunner.isSpecFileDirty( + cachedSpec: anyNamed('cachedSpec'), + loadedSpec: anyNamed('loadedSpec'))) + .thenAnswer((realInvocation) => Future.value(true)); + when(mockedArgs.isRemote).thenReturn(false); + when(mockedArgs.runSourceGen).thenReturn(true); + when(mockedArgs.shouldGenerateSources).thenReturn(true); + when(mockedArgs.shouldFetchDependencies).thenReturn(true); + when(mockRunner.cacheSpecFile( + updatedSpec: anyNamed('updatedSpec'), + cachedPath: anyNamed('cachedPath'))) + .thenAnswer((_) => Future.value(VoidType)); + when(mockedArgs.outputDirectory).thenReturn('pwd'); + when(mockRunner.runCommand( + command: anyNamed('command'), + workingDirectory: anyNamed('workingDirectory'))) + .thenAnswer((_) => + Future.value(ProcessResult(999, 0, 'success', ''))); + when(mockRunner.runCommand( + command: argThat( + TypeMatcher() + .having((c) => c.executable, 'executable', 'dart') + .having( + (c) => c.arguments, 'arguments', ['format', './']), + named: 'command', + ), + workingDirectory: + argThat(contains('pwd'), named: 'workingDirectory'), + )).thenAnswer((realInvocation) => + Future.value(ProcessResult(999, 1, '', 'err'))); + + when(mockRunner.loadAnnotatedFile(path: anyNamed('path'))) + .thenAnswer((realInvocation) => + Future.value(['cant be empty or throws'])); + when(mockRunner.writeAnnotatedFile( + path: anyNamed('path'), content: anyNamed('content'))) + .thenAnswer((realInvocation) => Future.value(VoidType)); + + // execution + await generator.generatorV2( + args: mockedArgs, + baseCommand: 'dart', + annotatedPath: 'annotatedPath'); + expect(logs.length, 15); + expect(logs[12].message, 'Failed to format generated code.'); + expect(logs[12].level, Level.SEVERE); + }); + }); + }); + }); + + group('hasDiff', () { + test('succeeds', () async { + final logs = []; + logger.onRecord.listen(logs.add); + when(mockRunner.loadSpecFile( + specConfig: anyNamed('specConfig'), isCached: true)) + .thenAnswer( + (realInvocation) => Future.value({})); + when(mockRunner.loadSpecFile(specConfig: anyNamed('specConfig'))) + .thenAnswer( + (realInvocation) => Future.value({})); + when(mockRunner.isSpecFileDirty( + cachedSpec: anyNamed('cachedSpec'), + loadedSpec: anyNamed('loadedSpec'))) + .thenAnswer((realInvocation) => Future.value(true)); + try { + expect( + await OpenapiGenerator(runner: mockRunner) + .hasDiff(args: realArguments), + isTrue); + expect(logs.length, 1); + expect(logs[0].message, 'Loaded cached and current spec files.'); + } catch (_, __) { + fail('should have completed successfully'); + } + }); + test('debug logs', () async { + final logs = []; + logger.onRecord.listen(logs.add); + when(mockedArgs.isDebug).thenReturn(true); + when(mockRunner.loadSpecFile( + specConfig: anyNamed('specConfig'), isCached: true)) + .thenAnswer( + (realInvocation) => Future.value({})); + when(mockRunner.loadSpecFile(specConfig: anyNamed('specConfig'))) + .thenAnswer( + (realInvocation) => Future.value({})); + when(mockRunner.isSpecFileDirty( + cachedSpec: anyNamed('cachedSpec'), + loadedSpec: anyNamed('loadedSpec'))) + .thenAnswer((realInvocation) => Future.value(true)); + try { + expect( + await OpenapiGenerator(runner: mockRunner) + .hasDiff(args: mockedArgs), + isTrue); + expect(logs.length, 1); + expect(logs[0].message, + 'Loaded cached and current spec files.\n{}\n{}'); + } catch (_, __) { + fail('should have completed successfully'); + } + }); + test('fails', () async { + when(mockRunner.loadSpecFile( + specConfig: anyNamed('specConfig'), isCached: true)) + .thenAnswer( + (realInvocation) => Future.value({})); + when(mockRunner.loadSpecFile(specConfig: anyNamed('specConfig'))) + .thenAnswer( + (realInvocation) => Future.value({})); + when(mockRunner.isSpecFileDirty( + cachedSpec: anyNamed('cachedSpec'), + loadedSpec: anyNamed('loadedSpec'))) + .thenThrow('uh oh'); + try { + await OpenapiGenerator(runner: mockRunner) + .hasDiff(args: realArguments); + fail('should have thrown'); + } catch (e, _) { + expect(e, isA()); + e as OutputMessage; + expect(e.message, 'Failed to check diff status.'); + expect(e.additionalContext, 'uh oh'); + expect(e.level, Level.SEVERE); + expect(e.stackTrace, isNotNull); + } + }); + }); + group('fetchDependencies', () { + test('returns successfully', () async { + when(mockedArgs.shouldFetchDependencies).thenReturn(true); + when(mockRunner.runCommand( + command: anyNamed('command'), + workingDirectory: anyNamed('workingDirectory'))) + .thenAnswer( + (_) => Future.value(ProcessResult(999, 0, 'yes', ''))); + try { + await OpenapiGenerator(runner: mockRunner) + .fetchDependencies(baseCommand: 'cmd', args: mockedArgs); + } catch (e, _) { + fail('should have completed successfully'); + } + }); + test('fails and returns an OutputMessage', () async { + when(mockedArgs.shouldFetchDependencies).thenReturn(true); + when(mockRunner.runCommand( + command: anyNamed('command'), + workingDirectory: anyNamed('workingDirectory'))) + .thenAnswer( + (_) => Future.value(ProcessResult(999, 1, '', 'uh oh'))); + try { + await OpenapiGenerator(runner: mockRunner) + .fetchDependencies(baseCommand: 'cmd', args: mockedArgs); + fail('should returned an error'); + } catch (e, _) { + expect(e, isA()); + e as OutputMessage; + expect(e.level, Level.SEVERE); + expect(e.message, 'Install within generated sources failed.'); + expect(e.additionalContext, 'uh oh'); + expect(e.stackTrace, isNotNull); + } + }); + group('logs', () { + tearDownAll(() => resetMockitoState()); + test('skips dependency fetch when flag is set', () async { + final logs = []; + logger.onRecord.listen(logs.add); + when(mockedArgs.shouldFetchDependencies).thenReturn(false); + + await OpenapiGenerator(logger: logger) + .fetchDependencies(baseCommand: 'cmd', args: mockedArgs); + + expect(logs.length, 1); + expect(logs[0].toString(), + contains('Skipping install step because flag was set.')); + expect(logs[0].level, Level.WARNING); + }); + test('debug', () async { + final logs = []; + logger.onRecord.listen(logs.add); + when(mockedArgs.shouldFetchDependencies).thenReturn(true); + when(mockedArgs.isDebug).thenReturn(true); + when(mockRunner.runCommand( + command: anyNamed('command'), + workingDirectory: anyNamed('workingDirectory'))) + .thenAnswer( + (_) => Future.value(ProcessResult(999, 0, 'yes', ''))); + + await OpenapiGenerator(logger: logger, runner: mockRunner) + .fetchDependencies(baseCommand: 'dart', args: mockedArgs); + + expect(logs.length, 2); + expect( + logs[0].message, + contains( + 'Installing dependencies with generated source. dart pub get')); + expect(logs[1].message, contains('yes')); + expect( + logs[1].message, contains('Install completed successfully.')); + expect(logs[0].level, Level.INFO); + expect(logs[1].level, Level.INFO); + }); + test('normal', () async { + final logs = []; + logger.onRecord.listen(logs.add); + when(mockedArgs.shouldFetchDependencies).thenReturn(true); + when(mockedArgs.isDebug).thenReturn(false); + when(mockRunner.runCommand( + command: anyNamed('command'), + workingDirectory: anyNamed('workingDirectory'))) + .thenAnswer( + (_) => Future.value(ProcessResult(999, 0, 'yes', ''))); + + await OpenapiGenerator(logger: logger, runner: mockRunner) + .fetchDependencies(baseCommand: 'dart', args: mockedArgs); + + expect(logs.length, 2); + expect( + logs[0].message, + contains( + 'Installing dependencies with generated source. dart pub get')); + expect(logs[1].message.contains('yes'), isFalse); + expect( + logs[1].message, contains('Install completed successfully.')); + expect(logs[0].level, Level.INFO); + expect(logs[1].level, Level.INFO); + }); + }); + }); + group('runSourceGen', () { + test('fails and returns an OutputMessage', () async { + when(mockRunner.runCommand( + command: anyNamed('command'), + workingDirectory: anyNamed('workingDirectory'))) + .thenAnswer( + (_) => Future.value(ProcessResult(999, 1, '', 'uh oh'))); + try { + await OpenapiGenerator(runner: mockRunner) + .runSourceGen(baseCommand: 'dart', args: mockedArgs); + fail('should returned an error'); + } catch (e, _) { + expect(e, isA()); + e as OutputMessage; + expect(e.level, Level.SEVERE); + expect(e.message, + 'Failed to generate source code. Build Command output:'); + expect(e.additionalContext, 'uh oh'); + expect(e.stackTrace, isNotNull); + } + }); + test('runs successfully', () async { + final logs = []; + logger.onRecord.listen(logs.add); + when(mockRunner.runCommand( + command: anyNamed('command'), + workingDirectory: anyNamed('workingDirectory'))) + .thenAnswer((_) => Future.value(ProcessResult(999, 0, '', ''))); + try { + await OpenapiGenerator(runner: mockRunner) + .runSourceGen(baseCommand: 'dart', args: mockedArgs); + expect(logs.length, 3); + expect(logs[0].message, 'Running source code generation.'); + expect(logs[1].message, + 'dart pub run build_runner build --delete-conflicting-outputs'); + expect(logs[2].message, 'Codegen completed successfully.'); + for (final log in logs) { + expect(log.level, Level.INFO); + } + } catch (e, _) { + fail('should have completed normally'); + } + }); + }); + group('generateSources', () { + test('skips when flag is set', () async { + final logs = []; + logger.onRecord.listen(logs.add); + when(mockedArgs.runSourceGen).thenReturn(false); + try { + await OpenapiGenerator(runner: mockRunner) + .generateSources(baseCommand: 'dart', args: mockedArgs); + expect(logs.length, 1); + expect(logs[0].message, + 'Skipping source gen step due to flag being set.'); + expect(logs[0].level, Level.WARNING); + } catch (e, _) { + fail('should have completed normally'); + } + }); + test('skips when not needed', () async { + final logs = []; + logger.onRecord.listen(logs.add); + when(mockedArgs.runSourceGen).thenReturn(true); + when(mockedArgs.shouldGenerateSources).thenReturn(false); + try { + await OpenapiGenerator(runner: mockRunner) + .generateSources(baseCommand: 'dart', args: mockedArgs); + expect(logs.length, 1); + expect(logs[0].message, + 'Skipping source gen because generator does not need it.'); + expect(logs[0].level, Level.INFO); + } catch (e, _) { + fail('should have completed normally'); + } + }); + test('completes successfully', () async { + final logs = []; + logger.onRecord.listen(logs.add); + when(mockedArgs.runSourceGen).thenReturn(true); + when(mockedArgs.shouldGenerateSources).thenReturn(true); + when(mockRunner.runCommand( + command: anyNamed('command'), + workingDirectory: anyNamed('workingDirectory'))) + .thenAnswer((_) => Future.value(ProcessResult(999, 0, '', ''))); + try { + await OpenapiGenerator(runner: mockRunner) + .generateSources(baseCommand: 'dart', args: mockedArgs); + expect(logs.length, 4); + expect(logs[3].message, 'Sources generated successfully.'); + expect(logs[3].level, Level.INFO); + } catch (e, _) { + fail('should have completed normally'); + } + }); + test('fails', () async { + final logs = []; + logger.onRecord.listen(logs.add); + when(mockedArgs.runSourceGen).thenReturn(true); + when(mockedArgs.shouldGenerateSources).thenReturn(true); + when(mockRunner.runCommand( + command: anyNamed('command'), + workingDirectory: anyNamed('workingDirectory'))) + .thenAnswer( + (_) => Future.value(ProcessResult(999, 1, '', 'uh oh'))); + try { + await OpenapiGenerator(runner: mockRunner) + .generateSources(baseCommand: 'dart', args: mockedArgs); + fail('should have failed'); + } catch (e, _) { + expect(e, isA()); + e as OutputMessage; + expect(e.message, 'Could not complete source generation'); + expect(e.level, Level.SEVERE); + expect(e.additionalContext, isA()); + expect(e.stackTrace, isNotNull); + } + }); + }); + group('updateAnnotatedFile', () { + test('fails', () async { + when(mockRunner.loadAnnotatedFile(path: 'annotatedPath')) + .thenAnswer((realInvocation) => Future.error('uh')); + try { + await OpenapiGenerator(runner: mockRunner) + .updateAnnotatedFile(annotatedPath: 'annotatedPath'); + fail('should have thrown an error'); + } catch (e, _) { + expect(e, isA()); + e as OutputMessage; + expect(e.message, 'Failed to update the annotated class file.'); + expect(e.level, Level.SEVERE); + expect(e.additionalContext, 'uh'); + expect(e.stackTrace, isNotNull); + } + }); + test('finds timestamp', () async { + final logs = []; + logger.onRecord.listen(logs.add); + when(mockRunner.loadAnnotatedFile(path: 'annotatedPath')).thenAnswer( + (realInvocation) => + Future.value(['$lastRunPlaceHolder: something', 'more'])); + when(mockRunner.writeAnnotatedFile( + path: 'annotatedPath', content: anyNamed('content'))) + .thenAnswer((realInvocation) => Future.value()); + try { + await OpenapiGenerator(runner: mockRunner) + .updateAnnotatedFile(annotatedPath: 'annotatedPath'); + expect(logs.length, 1); + expect(logs[0].message, + contains('Found generated timestamp. Updating with ')); + } catch (_, __) { + fail('should have completed successfully'); + } + }); + test('does not find timestamp', () async { + final logs = []; + logger.onRecord.listen(logs.add); + when(mockRunner.loadAnnotatedFile(path: 'annotatedPath')) + .thenAnswer((realInvocation) => Future.value(['more'])); + when(mockRunner.writeAnnotatedFile( + path: 'annotatedPath', content: anyNamed('content'))) + .thenAnswer((realInvocation) => Future.value()); + try { + await OpenapiGenerator(runner: mockRunner) + .updateAnnotatedFile(annotatedPath: 'annotatedPath'); + expect(logs.length, 1); + expect(logs[0].message, + contains('Creating generated timestamp with ')); + } catch (_, __) { + fail('should have completed successfully'); + } + }); + }); + group('formatCode', () { + test('logs on success', () async { + final logs = []; + logger.onRecord.listen(logs.add); + when(mockRunner.runCommand( + command: anyNamed('command'), + workingDirectory: anyNamed('workingDirectory'))) + .thenAnswer((_) => Future.value(ProcessResult(999, 0, '', ''))); + try { + await OpenapiGenerator(runner: mockRunner, logger: logger) + .formatCode(args: mockedArgs); + expect(logs.length, 1); + expect(logs[0].message, 'Successfully formatted code.'); + expect(logs[0].level, Level.INFO); + } catch (e, _) { + fail('should complete successfully'); + } + }); + test('fails and returns an OutputMessage', () async { + when(mockRunner.runCommand( + command: anyNamed('command'), + workingDirectory: anyNamed('workingDirectory'))) + .thenAnswer( + (_) => Future.value(ProcessResult(999, 1, '', 'uh oh'))); + try { + await OpenapiGenerator(runner: mockRunner) + .formatCode(args: mockedArgs); + fail('should returned an error'); + } catch (e, _) { + expect(e, isA()); + e as OutputMessage; + expect(e.level, Level.SEVERE); + expect(e.message, 'Failed to format generated code.'); + expect(e.additionalContext, 'uh oh'); + expect(e.stackTrace, isNotNull); + } + }); + }); + group('runOpenApiJar', () { + group('logs', () { + tearDownAll(() => resetMockitoState()); + test('normal', () async { + final logs = []; + logger.onRecord.listen(logs.add); + when( + mockRunner.runCommand( + command: anyNamed('command'), + workingDirectory: anyNamed( + 'workingDirectory', + ), + ), + ).thenAnswer( + (realInvocation) => Future.value( + ProcessResult(999, 0, 'jar successful', ''), + ), + ); + + when(mockedArgs.jarArgs) + .thenAnswer((realInvocation) => realArguments.jarArgs); + when(mockedArgs.isDebug).thenReturn(false); + await OpenapiGenerator(runner: mockRunner, logger: logger) + .runOpenApiJar(arguments: mockedArgs); + expect(logs.length, 2); + expect( + logs[0].message, + contains( + 'Running following command to generate openapi client - [ ${(await realArguments.jarArgs).join(' ')} ]')); + expect(logs[1].message.contains('jar successful'), isFalse); + expect(logs[1].message, + contains('Openapi generator completed successfully.')); + for (final log in logs) { + expect(log.level, Level.INFO); + } + }); + + test('debug', () async { + final logs = []; + logger.onRecord.listen(logs.add); + when( + mockRunner.runCommand( + command: anyNamed('command'), + workingDirectory: anyNamed( + 'workingDirectory', + ), + ), + ).thenAnswer( + (realInvocation) => Future.value( + ProcessResult(999, 0, 'jar successful', ''), + ), + ); + + when(mockedArgs.jarArgs) + .thenAnswer((realInvocation) => realArguments.jarArgs); + when(mockedArgs.isDebug).thenReturn(true); + await OpenapiGenerator(runner: mockRunner, logger: logger) + .runOpenApiJar(arguments: mockedArgs); + expect(logs.length, 2); + expect( + logs[0].message, + contains( + 'Running following command to generate openapi client - [ ${(await realArguments.jarArgs).join(' ')} ]')); + expect(logs[1].message, contains('jar successful')); + expect(logs[1].message, + contains('Openapi generator completed successfully.')); + for (final log in logs) { + expect(log.level, Level.INFO); + } + }); + }); + test('returns successfully', () async { + when( + mockRunner.runCommand( + command: anyNamed('command'), + workingDirectory: anyNamed( + 'workingDirectory', + ), + ), + ).thenAnswer( + (realInvocation) => Future.value( + ProcessResult(999, 0, 'completed successfully', ''), + ), + ); + try { + await OpenapiGenerator(runner: mockRunner) + .runOpenApiJar(arguments: realArguments); + } catch (e, _) { + fail('should have completed successfully.'); + } + }); + test('returns an error when the jar command fails', () async { + when( + mockRunner.runCommand( + command: anyNamed('command'), + workingDirectory: anyNamed( + 'workingDirectory', + ), + ), + ).thenAnswer( + (realInvocation) => Future.value( + ProcessResult(999, 1, '', 'something went wrong'), + ), + ); + + try { + await OpenapiGenerator(runner: mockRunner) + .runOpenApiJar(arguments: realArguments); + fail( + 'should have returned an error log.', + ); + } catch (e, _) { + expect(e, isA()); + e as OutputMessage; + expect(e.level, Level.SEVERE); + expect(e.message, 'Codegen Failed. Generator output:'); + expect(e.additionalContext, 'something went wrong'); + expect(e.stackTrace, isNotNull); + } + }); + }); + }); + }); +} diff --git a/openapi-generator/test/mocks.dart b/openapi-generator/test/mocks.dart new file mode 100644 index 0000000..d167d09 --- /dev/null +++ b/openapi-generator/test/mocks.dart @@ -0,0 +1,21 @@ +import 'dart:io'; + +import 'package:analyzer/dart/element/element.dart'; +import 'package:build/build.dart'; +import 'package:mockito/annotations.dart'; +import 'package:openapi_generator/src/models/command.dart'; +import 'package:openapi_generator/src/models/generator_arguments.dart'; +import 'package:openapi_generator/src/openapi_generator_runner.dart'; +import 'package:openapi_generator_annotations/openapi_generator_annotations.dart'; + +@GenerateNiceMocks([ + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec() +]) +void main() {} diff --git a/openapi-generator/test/specs/buckets.sh b/openapi-generator/test/specs/buckets.sh deleted file mode 100755 index 17ef658..0000000 --- a/openapi-generator/test/specs/buckets.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -# A simple bash script that sets up the local stack testing environment for AWS Remote specs -awslocal s3 mb s3://bucket -SPEC="$(curl -XGET https://raw.githubusercontent.com/Nexushunter/tagmine-api/main/openapi.yaml)" -echo "$SPEC" >> oasspec.yaml -awslocal s3api put-object --bucket bucket --key openapi.yaml --body oasspec.yaml \ No newline at end of file diff --git a/openapi-generator/test/specs/next_gen_builder_flutter_test_config.dart b/openapi-generator/test/specs/next_gen_builder_flutter_test_config.dart index b06211f..8f2f3c8 100644 --- a/openapi-generator/test/specs/next_gen_builder_flutter_test_config.dart +++ b/openapi-generator/test/specs/next_gen_builder_flutter_test_config.dart @@ -10,4 +10,4 @@ import 'package:openapi_generator_annotations/openapi_generator_annotations.dart cachePath: './test/specs/managed-cache.json', projectPubspecPath: './test/specs/flutter_pubspec.test.yaml', ) -class TestClassConfig extends OpenapiGeneratorConfig {} +class TestClassConfig {} diff --git a/openapi-generator/test/specs/next_gen_builder_test_aws_config.dart b/openapi-generator/test/specs/next_gen_builder_flutterw_test_config.dart similarity index 61% rename from openapi-generator/test/specs/next_gen_builder_test_aws_config.dart rename to openapi-generator/test/specs/next_gen_builder_flutterw_test_config.dart index 5fd3f12..f71442c 100644 --- a/openapi-generator/test/specs/next_gen_builder_test_aws_config.dart +++ b/openapi-generator/test/specs/next_gen_builder_flutterw_test_config.dart @@ -7,15 +7,11 @@ import 'package:openapi_generator_annotations/openapi_generator_annotations.dart 'https://raw.githubusercontent.com/Nexushunter/tagmine-api/main/openapi.yaml', inputSpec: RemoteSpec( path: - 'http://bucket.s3.us-east-1.localhost.localstack.cloud:4566/openapi.yaml', - headerDelegate: AWSRemoteSpecHeaderDelegate( - bucket: 'bucket', - accessKeyId: 'test', - secretAccessKey: 'test', - ), + 'https://raw.githubusercontent.com/Nexushunter/tagmine-api/main/openapi.yaml', ), generatorName: Generator.dio, useNextGen: true, + additionalProperties: AdditionalProperties(wrapper: Wrapper.flutterw), cachePath: './test/specs/output-nextgen/expected-args/cache.json', outputDirectory: './test/specs/output-nextgen/expected-args') -class TestClassConfig extends OpenapiGeneratorConfig {} +class TestClassConfig {} diff --git a/openapi-generator/test/specs/next_gen_builder_fvm_test_config.dart b/openapi-generator/test/specs/next_gen_builder_fvm_test_config.dart new file mode 100644 index 0000000..69e4b5c --- /dev/null +++ b/openapi-generator/test/specs/next_gen_builder_fvm_test_config.dart @@ -0,0 +1,19 @@ +library test_lib; + +import 'package:openapi_generator_annotations/openapi_generator_annotations.dart'; + +@Openapi( + inputSpecFile: + 'https://raw.githubusercontent.com/Nexushunter/tagmine-api/main/openapi.yaml', + inputSpec: RemoteSpec( + path: + 'https://raw.githubusercontent.com/Nexushunter/tagmine-api/main/openapi.yaml', + ), + generatorName: Generator.dio, + additionalProperties: AdditionalProperties( + wrapper: Wrapper.fvm, + ), + useNextGen: true, + cachePath: './test/specs/output-nextgen/expected-args/cache.json', + outputDirectory: './test/specs/output-nextgen/expected-args') +class TestClassConfig {} diff --git a/openapi-generator/test/specs/next_gen_builder_test_config.dart b/openapi-generator/test/specs/next_gen_builder_test_config.dart index d9f36c3..859f19e 100644 --- a/openapi-generator/test/specs/next_gen_builder_test_config.dart +++ b/openapi-generator/test/specs/next_gen_builder_test_config.dart @@ -13,4 +13,4 @@ import 'package:openapi_generator_annotations/openapi_generator_annotations.dart useNextGen: true, cachePath: './test/specs/output-nextgen/expected-args/cache.json', outputDirectory: './test/specs/output-nextgen/expected-args') -class TestClassConfig extends OpenapiGeneratorConfig {} +class TestClassConfig {} diff --git a/openapi-generator/test/specs/next_gen_builder_test_invalid_config.dart b/openapi-generator/test/specs/next_gen_builder_test_invalid_config.dart new file mode 100644 index 0000000..a19474d --- /dev/null +++ b/openapi-generator/test/specs/next_gen_builder_test_invalid_config.dart @@ -0,0 +1,16 @@ +library test_lib; + +import 'package:openapi_generator_annotations/openapi_generator_annotations.dart'; + +@Openapi( + inputSpecFile: + 'https://raw.githubusercontent.com/Nexushunter/tagmine-api/main/openapi.yaml', + inputSpec: RemoteSpec( + path: + 'https://raw.githubusercontent.com/Nexushunter/tagmine-api/main/openapi.yaml', + ), + generatorName: Generator.dio, + useNextGen: false, + cachePath: './test/specs/output-nextgen/expected-args/cache.json', + outputDirectory: './test/specs/output-nextgen/expected-args') +class TestClassConfig {} diff --git a/openapi-generator/test/test_annotations/test_configs.dart b/openapi-generator/test/test_annotations/test_configs.dart deleted file mode 100644 index 92622ac..0000000 --- a/openapi-generator/test/test_annotations/test_configs.dart +++ /dev/null @@ -1,77 +0,0 @@ -library test_annotations; - -import 'package:openapi_generator_annotations/openapi_generator_annotations.dart'; -import 'package:source_gen_test/annotations.dart'; - -@ShouldGenerate(r''' -const alwaysRun = false; - -const fetchDependencies = true; - -const generatorName = 'dio'; - -const inputSpecFile = ''; - -const runSourceGenOnOutput = true; - -const skipSpecValidation = false; - -const useNextGen = false; -''') -@Openapi(inputSpecFile: '', generatorName: Generator.dio) -class TestClassDefault extends OpenapiGeneratorConfig {} - -@ShouldThrow('useNextGen must be set when using cachePath', element: false) -@Openapi(inputSpecFile: '', generatorName: Generator.dio, cachePath: './') -class TestClassInvalidCachePathUsage extends OpenapiGeneratorConfig {} - -@ShouldGenerate(r''' -const additionalProperties = wrapper = 'flutterw'; - -const alwaysRun = false; - -const fetchDependencies = true; - -const generatorName = 'dart'; - -const inputSpecFile = ''; - -const runSourceGenOnOutput = true; - -const skipSpecValidation = false; - -const useNextGen = false; -''') -@Openapi( - inputSpecFile: '', - generatorName: Generator.dart, - additionalProperties: AdditionalProperties(wrapper: Wrapper.flutterw), -) -class TestClassHasCustomAnnotations extends OpenapiGeneratorConfig {} - -@ShouldGenerate(r''' -const additionalProperties = wrapper = 'flutterw', nullableFields = 'true'; - -const alwaysRun = false; - -const fetchDependencies = true; - -const generatorName = 'dart'; - -const inputSpecFile = ''; - -const runSourceGenOnOutput = true; - -const skipSpecValidation = false; - -const useNextGen = false; -''') -@Openapi( - inputSpecFile: '', - generatorName: Generator.dart, - additionalProperties: DioProperties( - wrapper: Wrapper.flutterw, - nullableFields: true, - ), -) -class TestClassHasDioProperties extends OpenapiGeneratorConfig {} diff --git a/openapi-generator/test/test_annotations/test_generator.dart b/openapi-generator/test/test_annotations/test_generator.dart deleted file mode 100644 index 1bee080..0000000 --- a/openapi-generator/test/test_annotations/test_generator.dart +++ /dev/null @@ -1,113 +0,0 @@ -import 'package:analyzer/dart/element/element.dart'; -import 'package:build/src/builder/build_step.dart'; -import 'package:openapi_generator/src/utils.dart'; -import 'package:openapi_generator_annotations/openapi_generator_annotations.dart'; -import 'package:source_gen/source_gen.dart' as src_gen; - -class TestGenerator extends src_gen.GeneratorForAnnotation { - final bool requireTestClassPrefix; - - const TestGenerator({this.requireTestClassPrefix = true}); - - @override - Iterable generateForAnnotatedElement(Element element, - src_gen.ConstantReader annotation, BuildStep buildStep) sync* { - assert(!annotation.isNull, 'The source generator should\'nt be null'); - - if (element is! ClassElement) { - throw src_gen.InvalidGenerationSourceError( - 'Only supports annotated classes.', - todo: 'Remove `TestAnnotation` from the associated element.', - element: element, - ); - } - - if (requireTestClassPrefix && !element.name.startsWith('TestClass')) { - throw src_gen.InvalidGenerationSourceError( - 'All classes must start with `TestClass`.', - todo: 'Rename the type or remove the `TestAnnotation` from class.', - element: element, - ); - } - - if (!(annotation.read('useNextGen').literalValue as bool)) { - if (annotation.read('cachePath').literalValue != null) { - throw src_gen.InvalidGenerationSourceError( - 'useNextGen must be set when using cachePath'); - } - } - - // KEEP THIS IN LINE WITH THE FIELDS OF THE ANNOTATION CLASS - final fields = [ - SupportedFields(name: 'additionalProperties', type: AdditionalProperties), - SupportedFields( - name: 'overwriteExistingFiles', isDeprecated: true, type: bool), - SupportedFields(name: 'skipSpecValidation', type: bool), - SupportedFields(name: 'inputSpecFile', isRequired: true, type: String), - SupportedFields(name: 'templateDirectory', type: String), - SupportedFields(name: 'generatorName', isRequired: true, type: Generator), - SupportedFields(name: 'outputDirectory', type: Map), - SupportedFields(name: 'typeMappings', type: Map), - SupportedFields(name: 'importMappings', type: Map), - SupportedFields(name: 'reservedWordsMappings', type: Map), - SupportedFields(name: 'inlineSchemaNameMappings', type: Map), - // SupportedFields(name:'inlineSchemaOptions'), - SupportedFields(name: 'apiPackage', type: String), - SupportedFields(name: 'fetchDependencies', type: bool), - SupportedFields(name: 'runSourceGenOnOutput', type: bool), - SupportedFields(name: 'alwaysRun', isDeprecated: true, type: bool), - SupportedFields(name: 'cachePath', type: String), - SupportedFields(name: 'useNextGen', type: bool), - SupportedFields(name: 'projectPubspecPath', type: String), - ]..sort((a, b) => a.name.compareTo(b.name)); - for (final field in fields) { - final v = annotation.read(field.name); - try { - if ([ - 'inputSpecFile', - 'projectPubspecPath', - 'apiPackage', - 'templateDirectory', - 'generatorName' - ].any((element) => field.name == element)) { - yield 'const ${field.name}=\'${convertToPropertyValue(v.objectValue)}\';\n'; - } else if (field.name == 'additionalProperties') { - final mapping = v.revive().namedArguments.map( - (key, value) => MapEntry(key, convertToPropertyValue(value))); - // TODO: Is this the expected behaviour? - // Iterable> entries; - // if (v.objectValue.type is DioProperties) { - // entries = DioProperties.fromMap(mapping).toMap().entries; - // } else if (v.objectValue.type is DioAltProperties) { - // entries = DioAltProperties.fromMap(mapping).toMap().entries; - // } else { - // entries = AdditionalProperties.fromMap(mapping).toMap().entries; - // } - yield 'const ${field.name}=${mapping.entries.fold('', foldStringMap(valueModifier: (value) => '\'$value\''))};'; - } else { - yield 'const ${field.name}=${convertToPropertyValue(v.objectValue)};\n'; - } - } catch (_, __) { - continue; - } - } - } - - @override - String toString() => - 'TestGenerator (requireTestClassPrefix:$requireTestClassPrefix)'; -} - -class SupportedFields { - final String name; - final bool isRequired; - final bool isDeprecated; - final T? type; - - const SupportedFields({ - required this.name, - this.isDeprecated = false, - this.isRequired = false, - required this.type, - }); -} diff --git a/openapi-generator/test/utils.dart b/openapi-generator/test/utils.dart index eea873a..97bdb38 100644 --- a/openapi-generator/test/utils.dart +++ b/openapi-generator/test/utils.dart @@ -1,54 +1,17 @@ import 'dart:io'; -import 'package:build/build.dart'; import 'package:build_test/build_test.dart'; -import 'package:openapi_generator/src/models/output_message.dart'; -import 'package:openapi_generator/src/openapi_generator_runner.dart'; import 'package:source_gen/source_gen.dart'; -final String pkgName = 'pkg'; - -final Builder builder = LibraryBuilder(OpenapiGenerator(testMode: true), - generatedExtension: '.openapi_generator'); final testSpecPath = '${Directory.current.path}${Platform.pathSeparator}test${Platform.pathSeparator}specs${Platform.pathSeparator}'; -/// Runs an in memory test variant of the generator with the given [source]. -/// -/// [path] available so an override for the adds generated comment test can -/// compare the output. -Future generate(String source, {String path = 'lib/myapp.dart'}) async { - final spec = File('${testSpecPath}openapi.test.yaml').readAsStringSync(); - var srcs = { - 'openapi_generator_annotations|lib/src/openapi_generator_annotations_base.dart': - File('../openapi-generator-annotations/lib/src/openapi_generator_annotations_base.dart') - .readAsStringSync(), - 'openapi_generator|$path': ''' - import 'package:openapi_generator_annotations/src/openapi_generator_annotations_base.dart'; - $source - class MyApp { - } - ''', - 'openapi_generator|openapi-spec.yaml': spec - }; - - // Capture any message from generation; if there is one, return that instead of - // the generated output. - String? logMessage; - void captureLog(dynamic logRecord) { - if (logRecord is OutputMessage) { - logMessage = - '${logMessage ?? ''}\n${logRecord.level} ${logRecord.message} \n ${logRecord.additionalContext} \n ${logRecord.stackTrace}'; - } else { - logMessage = - '${logMessage ?? ''}\n${logRecord.message ?? ''}\n${logRecord.error ?? ''}\n${logRecord.stackTrace ?? ''}'; - } - } - - var writer = InMemoryAssetWriter(); - await testBuilder(builder, srcs, - rootPackage: pkgName, writer: writer, onLog: captureLog); - return logMessage ?? - String.fromCharCodes( - writer.assets[AssetId(pkgName, 'lib/value.g.dart')] ?? []); -} +Future loadAnnoation(String testConfigPath) async => + (await resolveSource( + File('$testSpecPath/$testConfigPath').readAsStringSync(), + (resolver) async => + (await resolver.findLibraryByName('test_lib'))!)) + .getClass('TestClassConfig')! + .metadata + .map((e) => ConstantReader(e.computeConstantValue()!)) + .first; diff --git a/openapi-generator/test/verify_generation_test.dart b/openapi-generator/test/verify_generation_test.dart deleted file mode 100644 index e1dec00..0000000 --- a/openapi-generator/test/verify_generation_test.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'dart:io'; - -import 'package:openapi_generator_annotations/openapi_generator_annotations.dart'; -import 'package:source_gen_test/source_gen_test.dart'; - -import 'test_annotations/test_generator.dart'; - -void main() async { - final reader = await initializeLibraryReaderForDirectory( - '${Directory.current.path}${Platform.pathSeparator}test${Platform.pathSeparator}test_annotations', - 'test_configs.dart', - ); - - initializeBuildLogTracking(); - - testAnnotatedElements(reader, const TestGenerator()); -}