From 8bac75c17cc612a5e7e7d809ea5e211fd3fed7b2 Mon Sep 17 00:00:00 2001 From: Alexander Sandor <137198655+SandPod@users.noreply.github.com> Date: Fri, 29 Nov 2024 07:57:22 +0100 Subject: [PATCH] refactor: Model load refactors and tests (#3004) --- .../models/model_parser/model_parser.dart | 4 +- .../serverpod_cli/lib/src/config/config.dart | 59 +- .../src/generator/generator_continuous.dart | 6 +- .../lib/src/util/model_helper.dart | 75 +- .../model_helper/model_file_load_test.dart | 817 +++++++++++++++++ .../{ => model_helper}/model_helper_test.dart | 141 +-- .../module_model_file_load_test.dart | 842 ++++++++++++++++++ 7 files changed, 1773 insertions(+), 171 deletions(-) create mode 100644 tools/serverpod_cli/test/integration/util/model_helper/model_file_load_test.dart rename tools/serverpod_cli/test/integration/util/{ => model_helper}/model_helper_test.dart (64%) create mode 100644 tools/serverpod_cli/test/integration/util/model_helper/module_model_file_load_test.dart diff --git a/tools/serverpod_cli/lib/src/analyzer/models/model_parser/model_parser.dart b/tools/serverpod_cli/lib/src/analyzer/models/model_parser/model_parser.dart index 6cd4641afc..690191b29c 100644 --- a/tools/serverpod_cli/lib/src/analyzer/models/model_parser/model_parser.dart +++ b/tools/serverpod_cli/lib/src/analyzer/models/model_parser/model_parser.dart @@ -69,7 +69,7 @@ class ModelParser { fileName: outFileName, fields: fields, indexes: indexes, - subDirParts: protocolSource.protocolRootPathParts, + subDirParts: protocolSource.subDirPathParts, documentation: classDocumentation, isException: documentTypeName == Keyword.exceptionType, serverOnly: serverOnly, @@ -106,7 +106,7 @@ class ModelParser { values: values, serialized: serializeAs, documentation: enumDocumentation, - subDirParts: protocolSource.protocolRootPathParts, + subDirParts: protocolSource.subDirPathParts, serverOnly: serverOnly, type: enumType, ); diff --git a/tools/serverpod_cli/lib/src/config/config.dart b/tools/serverpod_cli/lib/src/config/config.dart index f157558a74..ea8b7a2df7 100644 --- a/tools/serverpod_cli/lib/src/config/config.dart +++ b/tools/serverpod_cli/lib/src/config/config.dart @@ -46,8 +46,28 @@ class ServerpodModulesNotFoundException implements Exception { String toString() => message; } +abstract interface class ModelLoadConfig { + /// Path parts to the lib/src/protocol directory of the server package. + List get protocolSourcePathParts; + + /// Path parts to the lib/src/models directory of the server package. + List get modelSourcePathParts; + + /// Path parts to the lib/src folder of the server package. + List get srcSourcePathParts; + + /// Path parts to the lib folder of the server package. + List get libSourcePathParts; + + /// Relative path parts to the model directory + List get relativeModelSourcePathParts; + + /// Relative path parts to the protocol directory + List get relativeProtocolSourcePathParts; +} + /// The configuration of the generation and analyzing process. -class GeneratorConfig { +class GeneratorConfig implements ModelLoadConfig { const GeneratorConfig({ required this.name, required this.type, @@ -97,22 +117,25 @@ class GeneratorConfig { /// Might be relative. final List serverPackageDirectoryPathParts; - /// Path parts to the lib folder of the server package. + @override List get libSourcePathParts => [...serverPackageDirectoryPathParts, 'lib']; - /// Relative path parts to the protocol directory + @override + List get srcSourcePathParts => [...libSourcePathParts, 'src']; + + @override List get relativeProtocolSourcePathParts => ['lib', 'src', 'protocol']; - /// Path parts to the protocol directory of the server package. + @override List get protocolSourcePathParts => [...serverPackageDirectoryPathParts, ...relativeProtocolSourcePathParts]; - /// Relative path parts to the model directory + @override List get relativeModelSourcePathParts => ['lib', 'src', 'models']; - /// Path parts to the model directory of the server package. + @override List get modelSourcePathParts => [...serverPackageDirectoryPathParts, ...relativeModelSourcePathParts]; @@ -439,7 +462,7 @@ generatedServerModel: ${p.joinAll(generatedServeModelPathParts)} } /// Describes the configuration of a Serverpod module a package depends on. -class ModuleConfig { +class ModuleConfig implements ModelLoadConfig { PackageType type; /// The user defined nickname of the module. @@ -458,13 +481,27 @@ class ModuleConfig { /// Might be relative. final List serverPackageDirectoryPathParts; - /// Path parts to the protocol directory of the server package. + @override + List get libSourcePathParts => + [...serverPackageDirectoryPathParts, 'lib']; + + @override + List get srcSourcePathParts => [...libSourcePathParts, 'src']; + + @override + List get relativeProtocolSourcePathParts => + ['lib', 'src', 'protocol']; + + @override List get protocolSourcePathParts => - [...serverPackageDirectoryPathParts, 'lib', 'src', 'protocol']; + [...serverPackageDirectoryPathParts, ...relativeProtocolSourcePathParts]; - /// Path parts to the model directory of the server package. + @override + List get relativeModelSourcePathParts => ['lib', 'src', 'models']; + + @override List get modelSourcePathParts => - [...serverPackageDirectoryPathParts, 'lib', 'src', 'models']; + [...serverPackageDirectoryPathParts, ...relativeModelSourcePathParts]; /// The migration versions of the module. List migrationVersions; diff --git a/tools/serverpod_cli/lib/src/generator/generator_continuous.dart b/tools/serverpod_cli/lib/src/generator/generator_continuous.dart index 22b026213d..d84ac91273 100644 --- a/tools/serverpod_cli/lib/src/generator/generator_continuous.dart +++ b/tools/serverpod_cli/lib/src/generator/generator_continuous.dart @@ -31,9 +31,6 @@ Future performGenerateContinuously({ 'Initial code generation complete. Listening for changes.', ); - var modelSourcePath = p.joinAll(config.modelSourcePathParts); - var protocolSourcePath = p.joinAll(config.protocolSourcePathParts); - Timer? debouncedGenerate; await for (WatchEvent event in watchers) { log.debug('File changed: $event'); @@ -43,8 +40,7 @@ Future performGenerateContinuously({ if (ModelHelper.isModelFile( event.path, - modelSourcePath, - protocolSourcePath, + loadConfig: config, )) { shouldGenerate = true; var modelUri = Uri.parse(p.absolute(event.path)); diff --git a/tools/serverpod_cli/lib/src/util/model_helper.dart b/tools/serverpod_cli/lib/src/util/model_helper.dart index c8ce71f954..eba65d70d6 100644 --- a/tools/serverpod_cli/lib/src/util/model_helper.dart +++ b/tools/serverpod_cli/lib/src/util/model_helper.dart @@ -9,13 +9,13 @@ class ModelSource { String moduleAlias; String yaml; Uri yamlSourceUri; - List protocolRootPathParts; + List subDirPathParts; ModelSource( this.moduleAlias, this.yaml, this.yamlSourceUri, - this.protocolRootPathParts, + this.subDirPathParts, ); } @@ -33,23 +33,17 @@ class ModelHelper { ) async { var modelSources = []; - var relativeModelSourcePath = joinAll(config.relativeModelSourcePathParts); - var relativeProtocolSourcePath = - joinAll(config.relativeProtocolSourcePathParts); - var modelSource = await _loadYamlModelsFromDisk( defaultModuleAlias, _absolutePathParts(config.modelSourcePathParts), - relativeModelSourcePath: relativeModelSourcePath, - relativeProtocolSourcePath: relativeProtocolSourcePath, + loadConfig: config, ); modelSources.addAll(modelSource); modelSource = await _loadYamlModelsFromDisk( defaultModuleAlias, _absolutePathParts(config.protocolSourcePathParts), - relativeModelSourcePath: relativeModelSourcePath, - relativeProtocolSourcePath: relativeProtocolSourcePath, + loadConfig: config, ); modelSources.addAll(modelSource); @@ -57,16 +51,14 @@ class ModelHelper { modelSource = await _loadYamlModelsFromDisk( module.nickname, module.modelSourcePathParts, - relativeModelSourcePath: relativeModelSourcePath, - relativeProtocolSourcePath: relativeProtocolSourcePath, + loadConfig: module, ); modelSources.addAll(modelSource); modelSource = await _loadYamlModelsFromDisk( module.nickname, module.protocolSourcePathParts, - relativeModelSourcePath: relativeModelSourcePath, - relativeProtocolSourcePath: relativeProtocolSourcePath, + loadConfig: module, ); modelSources.addAll(modelSource); } @@ -86,14 +78,9 @@ class ModelHelper { static Future> _loadYamlModelsFromDisk( String moduleAlias, List pathParts, { - required String relativeModelSourcePath, - required String relativeProtocolSourcePath, + required ModelLoadConfig loadConfig, }) async { - var files = await _loadAllModelFiles( - pathParts, - relativeModelSourcePath: relativeModelSourcePath, - relativeProtocolSourcePath: relativeProtocolSourcePath, - ); + var files = await _loadAllModelFiles(pathParts, loadConfig: loadConfig); List sources = []; for (var model in files) { @@ -103,7 +90,7 @@ class ModelHelper { moduleAlias, yaml, model.uri, - extractPathFromModelRoot(pathParts, model.uri), + _extractPathFromModelRoot(pathParts, model.uri), )); } @@ -111,14 +98,15 @@ class ModelHelper { } static bool isModelFile( - String path, - String modelSourcePath, - String protocolSourcePath, - ) { - var hasValidPath = path.containsAny([ - modelSourcePath, - protocolSourcePath, - ]); + String path, { + required ModelLoadConfig loadConfig, + }) { + var allowedModelPaths = [ + joinAll(loadConfig.relativeModelSourcePathParts), + joinAll(loadConfig.relativeProtocolSourcePathParts), + ]; + + var hasValidPath = path.containsAny(allowedModelPaths); var hasValidExtension = modelFileExtensions.any( (ext) => path.endsWith(ext), @@ -129,8 +117,7 @@ class ModelHelper { static Future> _loadAllModelFiles( List absolutePathParts, { - required String relativeModelSourcePath, - required String relativeProtocolSourcePath, + required ModelLoadConfig loadConfig, }) async { List modelSourceFileList = []; @@ -151,24 +138,28 @@ class ModelHelper { return modelSourceFileList.whereType().where((file) => isModelFile( file.path, - relativeModelSourcePath, - relativeProtocolSourcePath, + loadConfig: loadConfig, )); } - static List extractPathFromConfig(GeneratorConfig config, Uri uri) { - if (isWithin(joinAll(config.protocolSourcePathParts), uri.path)) { - return extractPathFromModelRoot(config.protocolSourcePathParts, uri); - } - - if (isWithin(joinAll(config.modelSourcePathParts), uri.path)) { - return extractPathFromModelRoot(config.modelSourcePathParts, uri); + static List extractPathFromConfig(ModelLoadConfig config, Uri uri) { + List> paths = [ + config.protocolSourcePathParts, + config.modelSourcePathParts, + config.srcSourcePathParts, + config.libSourcePathParts, + ]; + + for (var path in paths) { + if (isWithin(joinAll(path), uri.path)) { + return _extractPathFromModelRoot(path, uri); + } } return split(uri.path); } - static List extractPathFromModelRoot( + static List _extractPathFromModelRoot( List pathParts, Uri fileUri, ) { diff --git a/tools/serverpod_cli/test/integration/util/model_helper/model_file_load_test.dart b/tools/serverpod_cli/test/integration/util/model_helper/model_file_load_test.dart new file mode 100644 index 0000000000..166e778704 --- /dev/null +++ b/tools/serverpod_cli/test/integration/util/model_helper/model_file_load_test.dart @@ -0,0 +1,817 @@ +import 'dart:io'; + +import 'package:path/path.dart'; +import 'package:serverpod_cli/analyzer.dart'; +import 'package:serverpod_cli/src/test_util/builders/generator_config_builder.dart'; +import 'package:serverpod_cli/src/util/model_helper.dart'; +import 'package:test/test.dart'; +import 'package:uuid/uuid.dart'; + +void main() { + late Directory testDirectory; + setUpAll(() { + testDirectory = Directory(join(Directory.current.path, const Uuid().v4())); + testDirectory.createSync(recursive: true); + }); + + tearDownAll(() { + testDirectory.deleteSync(recursive: true); + }); + + group('Given a valid model class with *.yaml file ending', () { + var modelFileName = 'example.yaml'; + + late Directory testProject; + late GeneratorConfig config; + setUp(() { + testProject = Directory(join(testDirectory.path, const Uuid().v4())); + testProject.createSync(recursive: true); + config = GeneratorConfigBuilder() + .withServerPackageDirectoryPathParts(split(testProject.path)) + .withModules([]).build(); + }); + + tearDown(() { + testProject.deleteSync(recursive: true); + }); + + group('placed in the "lib/src/models" directory when loaded', () { + late List models; + + setUp(() async { + var modelFile = File(join( + testProject.path, + 'lib', + 'src', + 'models', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is serialized.', () async { + expect(models, hasLength(1)); + }); + + test('then modelSource has no subDirPathParts.', () async { + expect(models.firstOrNull?.subDirPathParts, isEmpty); + }); + }); + + group('placed in the "lib/src/protocol" directory when loaded', () { + late List models; + + setUp(() async { + var modelFile = File(join( + testProject.path, + 'lib', + 'src', + 'protocol', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is serialized.', () async { + expect(models, hasLength(1)); + }); + + test('then modelSource has no subDirPathParts.', () async { + expect(models.firstOrNull?.subDirPathParts, isEmpty); + }); + }); + + group( + 'placed in a subdirectory of the "lib/src/models" directory when loaded', + () { + late List models; + + setUp(() async { + var modelFile = File(join( + testProject.path, + 'lib', + 'src', + 'models', + 'sub', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String + +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is serialized.', () async { + expect(models, hasLength(1)); + }); + + test('then modelSource has the correct subDirPathParts.', () async { + expect(models.firstOrNull?.subDirPathParts, ['sub']); + }); + }); + + group('placed in the "lib/src" directory when loaded', () { + late List models; + + setUp(() async { + var modelFile = File(join( + testProject.path, + 'lib', + 'src', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is not serialized.', () async { + expect(models, hasLength(0)); + }); + }); + }); + + group('Given a valid model class with *.yml file ending', () { + var modelFileName = 'example.yml'; + + late Directory testProject; + late GeneratorConfig config; + setUp(() { + testProject = Directory(join(testDirectory.path, const Uuid().v4())); + testProject.createSync(recursive: true); + config = GeneratorConfigBuilder() + .withServerPackageDirectoryPathParts(split(testProject.path)) + .withModules([]).build(); + }); + + tearDown(() { + testProject.deleteSync(recursive: true); + }); + + group('placed in the "lib/src/models" directory when loaded', () { + late List models; + + setUp(() async { + var modelFile = File(join( + testProject.path, + 'lib', + 'src', + 'models', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is serialized.', () async { + expect(models, hasLength(1)); + }); + + test('then modelSource has no subDirPathParts.', () async { + expect(models.firstOrNull?.subDirPathParts, isEmpty); + }); + }); + + group('placed in the "lib/src/protocol" directory when loaded', () { + late List models; + + setUp(() async { + var modelFile = File(join( + testProject.path, + 'lib', + 'src', + 'protocol', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is serialized.', () async { + expect(models, hasLength(1)); + }); + + test('then modelSource has no subDirPathParts.', () async { + expect(models.firstOrNull?.subDirPathParts, isEmpty); + }); + }); + + group( + 'placed in a subdirectory of the "lib/src/models" directory when loaded', + () { + late List models; + + setUp(() async { + var modelFile = File(join( + testProject.path, + 'lib', + 'src', + 'models', + 'sub', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String + +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is serialized.', () async { + expect(models, hasLength(1)); + }); + + test('then modelSource has the correct subDirPathParts.', () async { + expect(models.firstOrNull?.subDirPathParts, ['sub']); + }); + }); + + group( + 'placed in a subdirectory of the "lib/src/protocol" directory when loaded', + () { + late List models; + + setUp(() async { + var modelFile = File(join( + testProject.path, + 'lib', + 'src', + 'protocol', + 'sub', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String + +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is serialized.', () async { + expect(models, hasLength(1)); + }); + + test('then modelSource has the correct subDirPathParts.', () async { + expect(models.firstOrNull?.subDirPathParts, ['sub']); + }); + }); + + group('placed in the "lib/src" directory when loaded', () { + late List models; + + setUp(() async { + var modelFile = File(join( + testProject.path, + 'lib', + 'src', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is not serialized.', () async { + expect(models, hasLength(0)); + }); + }); + }); + + group('Given a valid model class with *.spy file ending', () { + var modelFileName = 'example.spy'; + + late Directory testProject; + late GeneratorConfig config; + setUp(() { + testProject = Directory(join(testDirectory.path, const Uuid().v4())); + testProject.createSync(recursive: true); + config = GeneratorConfigBuilder() + .withServerPackageDirectoryPathParts(split(testProject.path)) + .withModules([]).build(); + }); + + tearDown(() { + testProject.deleteSync(recursive: true); + }); + + group('placed in the "lib/src/models" directory when loaded', () { + late List models; + + setUp(() async { + var modelFile = File(join( + testProject.path, + 'lib', + 'src', + 'models', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is serialized.', () async { + expect(models, hasLength(1)); + }); + + test('then modelSource has no subDirPathParts.', () async { + expect(models.firstOrNull?.subDirPathParts, isEmpty); + }); + }); + + group('placed in the "lib/src/protocol" directory when loaded', () { + late List models; + + setUp(() async { + var modelFile = File(join( + testProject.path, + 'lib', + 'src', + 'protocol', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is serialized.', () async { + expect(models, hasLength(1)); + }); + + test('then modelSource has no subDirPathParts.', () async { + expect(models.firstOrNull?.subDirPathParts, isEmpty); + }); + }); + + group( + 'placed in a subdirectory of the "lib/src/models" directory when loaded', + () { + late List models; + + setUp(() async { + var modelFile = File(join( + testProject.path, + 'lib', + 'src', + 'models', + 'sub', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String + +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is serialized.', () async { + expect(models, hasLength(1)); + }); + + test('then modelSource has the correct subDirPathParts.', () async { + expect(models.firstOrNull?.subDirPathParts, ['sub']); + }); + }); + + group( + 'placed in a subdirectory of the "lib/src/protocol" directory when loaded', + () { + late List models; + + setUp(() async { + var modelFile = File(join( + testProject.path, + 'lib', + 'src', + 'protocol', + 'sub', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String + +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is serialized.', () async { + expect(models, hasLength(1)); + }); + + test('then modelSource has the correct subDirPathParts.', () async { + expect(models.firstOrNull?.subDirPathParts, ['sub']); + }); + }); + + group('placed in the "lib/src" directory when loaded', () { + late List models; + + setUp(() async { + var modelFile = File(join( + testProject.path, + 'lib', + 'src', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is not serialized.', () async { + expect(models, hasLength(0)); + }); + }); + }); + + group('Given a valid model class with *.spy.yaml file ending', () { + var modelFileName = 'example.spy.yaml'; + + late Directory testProject; + late GeneratorConfig config; + setUp(() { + testProject = Directory(join(testDirectory.path, const Uuid().v4())); + testProject.createSync(recursive: true); + config = GeneratorConfigBuilder() + .withServerPackageDirectoryPathParts(split(testProject.path)) + .withModules([]).build(); + }); + + tearDown(() { + testProject.deleteSync(recursive: true); + }); + + group('placed in the "lib/src/models" directory when loaded', () { + late List models; + + setUp(() async { + var modelFile = File(join( + testProject.path, + 'lib', + 'src', + 'models', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is serialized.', () async { + expect(models, hasLength(1)); + }); + + test('then modelSource has no subDirPathParts.', () async { + expect(models.firstOrNull?.subDirPathParts, isEmpty); + }); + }); + + group('placed in the "lib/src/protocol" directory when loaded', () { + late List models; + + setUp(() async { + var modelFile = File(join( + testProject.path, + 'lib', + 'src', + 'protocol', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is serialized.', () async { + expect(models, hasLength(1)); + }); + + test('then modelSource has no subDirPathParts.', () async { + expect(models.firstOrNull?.subDirPathParts, isEmpty); + }); + }); + + group( + 'placed in a subdirectory of the "lib/src/models" directory when loaded', + () { + late List models; + + setUp(() async { + var modelFile = File(join( + testProject.path, + 'lib', + 'src', + 'models', + 'sub', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String + +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is serialized.', () async { + expect(models, hasLength(1)); + }); + + test('then modelSource has the correct subDirPathParts.', () async { + expect(models.firstOrNull?.subDirPathParts, ['sub']); + }); + }); + + group( + 'placed in a subdirectory of the "lib/src/protocol" directory when loaded', + () { + late List models; + + setUp(() async { + var modelFile = File(join( + testProject.path, + 'lib', + 'src', + 'protocol', + 'sub', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String + +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is serialized.', () async { + expect(models, hasLength(1)); + }); + + test('then modelSource has the correct subDirPathParts.', () async { + expect(models.firstOrNull?.subDirPathParts, ['sub']); + }); + }); + + group('placed in the "lib/src" directory when loaded', () { + late List models; + + setUp(() async { + var modelFile = File(join( + testProject.path, + 'lib', + 'src', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is not serialized.', () async { + expect(models, hasLength(0)); + }); + }); + }); + + group('Given a valid model class with *.spy.yml file ending', () { + var modelFileName = 'example.spy.yml'; + + late Directory testProject; + late GeneratorConfig config; + setUp(() { + testProject = Directory(join(testDirectory.path, const Uuid().v4())); + testProject.createSync(recursive: true); + config = GeneratorConfigBuilder() + .withServerPackageDirectoryPathParts(split(testProject.path)) + .withModules([]).build(); + }); + + tearDown(() { + testProject.deleteSync(recursive: true); + }); + + group('placed in the "lib/src/models" directory when loaded', () { + late List models; + + setUp(() async { + var modelFile = File(join( + testProject.path, + 'lib', + 'src', + 'models', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is serialized.', () async { + expect(models, hasLength(1)); + }); + + test('then modelSource has no subDirPathParts.', () async { + expect(models.firstOrNull?.subDirPathParts, isEmpty); + }); + }); + + group('placed in the "lib/src/protocol" directory when loaded', () { + late List models; + + setUp(() async { + var modelFile = File(join( + testProject.path, + 'lib', + 'src', + 'protocol', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is serialized.', () async { + expect(models, hasLength(1)); + }); + + test('then modelSource has no subDirPathParts.', () async { + expect(models.firstOrNull?.subDirPathParts, isEmpty); + }); + }); + + group( + 'placed in a subdirectory of the "lib/src/models" directory when loaded', + () { + late List models; + + setUp(() async { + var modelFile = File(join( + testProject.path, + 'lib', + 'src', + 'models', + 'sub', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String + +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is serialized.', () async { + expect(models, hasLength(1)); + }); + + test('then modelSource has the correct subDirPathParts.', () async { + expect(models.firstOrNull?.subDirPathParts, ['sub']); + }); + }); + + group( + 'placed in a subdirectory of the "lib/src/protocol" directory when loaded', + () { + late List models; + + setUp(() async { + var modelFile = File(join( + testProject.path, + 'lib', + 'src', + 'protocol', + 'sub', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String + +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is serialized.', () async { + expect(models, hasLength(1)); + }); + + test('then modelSource has the correct subDirPathParts.', () async { + expect(models.firstOrNull?.subDirPathParts, ['sub']); + }); + }); + + group('placed in the "lib/src" directory when loaded', () { + late List models; + + setUp(() async { + var modelFile = File(join( + testProject.path, + 'lib', + 'src', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is not serialized.', () async { + expect(models, hasLength(0)); + }); + }); + }); +} diff --git a/tools/serverpod_cli/test/integration/util/model_helper_test.dart b/tools/serverpod_cli/test/integration/util/model_helper/model_helper_test.dart similarity index 64% rename from tools/serverpod_cli/test/integration/util/model_helper_test.dart rename to tools/serverpod_cli/test/integration/util/model_helper/model_helper_test.dart index fddfc900a2..ee0216cc35 100644 --- a/tools/serverpod_cli/test/integration/util/model_helper_test.dart +++ b/tools/serverpod_cli/test/integration/util/model_helper/model_helper_test.dart @@ -1,27 +1,10 @@ import 'dart:io'; import 'package:path/path.dart'; -import 'package:serverpod_cli/analyzer.dart'; +import 'package:serverpod_cli/src/test_util/builders/generator_config_builder.dart'; import 'package:serverpod_cli/src/util/model_helper.dart'; import 'package:test/test.dart'; -GeneratorConfig createGeneratorConfig([ - List serverPackageDirectoryPathParts = const [], -]) { - return GeneratorConfig( - name: 'test', - type: PackageType.server, - serverPackage: 'test_server', - dartClientPackage: 'test_client', - dartClientDependsOnServiceClient: true, - serverPackageDirectoryPathParts: serverPackageDirectoryPathParts, - relativeDartClientPackagePathParts: [], - modules: [], - extraClasses: [], - enabledFeatures: [], - ); -} - void main() { group('Test path extraction - extractPathFromConfig.', () { var serverRootDir = Directory(join( @@ -51,7 +34,9 @@ void main() { 'test.yaml', )); - var config = createGeneratorConfig(split(serverRootDir.path)); + var config = GeneratorConfigBuilder() + .withServerPackageDirectoryPathParts(split(serverRootDir.path)) + .build(); var pathParts = ModelHelper.extractPathFromConfig( config, @@ -80,7 +65,9 @@ void main() { 'test.yaml', )); - var config = createGeneratorConfig(split(serverRootDir.path)); + var config = GeneratorConfigBuilder() + .withServerPackageDirectoryPathParts(split(serverRootDir.path)) + .build(); var pathParts = ModelHelper.extractPathFromConfig( config, @@ -109,7 +96,9 @@ void main() { 'test.yaml', )); - var config = createGeneratorConfig(split(serverRootDir.path)); + var config = GeneratorConfigBuilder() + .withServerPackageDirectoryPathParts(split(serverRootDir.path)) + .build(); var pathParts = ModelHelper.extractPathFromConfig( config, @@ -120,7 +109,7 @@ void main() { }); test( - 'Given a model with a path outside of the protocol or models folder, then parts list contains all the path parts', + 'Given a model with a path outside of the lib folder, then parts list contains all the path parts', () { var filePathParts = [ 'test', @@ -130,12 +119,12 @@ void main() { 'protocol_helper', 'has_serverpod_server_project', 'test_server', - 'lib', - 'src', ]; var modelFile = File(joinAll(filePathParts)); - var config = createGeneratorConfig(split(serverRootDir.path)); + var config = GeneratorConfigBuilder() + .withServerPackageDirectoryPathParts(split(serverRootDir.path)) + .build(); var pathParts = ModelHelper.extractPathFromConfig( config, @@ -146,84 +135,6 @@ void main() { }); }); - group('Test path extraction - extractPathFromModelRoot.', () { - test( - 'Given a model path directly inside the model folder, then the parts list is empty.', - () { - var modelFile = File(join( - 'test', - 'integration', - 'util', - 'test_assets', - 'protocol_helper', - 'has_serverpod_server_project', - 'test_server', - 'lib', - 'src', - 'protocol', - 'test.yaml', - )); - - var rootPath = [ - 'test', - 'integration', - 'util', - 'test_assets', - 'protocol_helper', - 'has_serverpod_server_project', - 'test_server', - 'lib', - 'src', - 'protocol', - ]; - - var pathParts = ModelHelper.extractPathFromModelRoot( - rootPath, - modelFile.uri, - ); - - expect(pathParts, []); - }); - - test( - 'Given a model with a nested path inside the model folder, then the parts list contains the nested path.', - () { - var modelFile = File(join( - 'test', - 'util', - 'test_assets', - 'protocol_helper', - 'has_serverpod_server_project', - 'test_server', - 'lib', - 'src', - 'protocol', - 'nested', - 'folder', - 'test.yaml', - )); - - var rootPath = [ - 'test', - 'util', - 'test_assets', - 'protocol_helper', - 'has_serverpod_server_project', - 'test_server', - 'lib', - 'src', - 'protocol' - ]; - - var pathParts = ModelHelper.extractPathFromModelRoot( - rootPath, - modelFile.uri, - ); - - expect(pathParts, ['nested', 'folder']); - }); - }); - group('Test yaml model loader.', () { var serverRootDir = Directory(join( 'test', @@ -235,7 +146,9 @@ void main() { 'test_server', )); - var config = createGeneratorConfig(split(serverRootDir.path)); + var config = GeneratorConfigBuilder() + .withServerPackageDirectoryPathParts(split(serverRootDir.path)) + .withModules([]).build(); test( 'Given a serverpod project with model files then the converted model path has the file uri set.', @@ -258,11 +171,11 @@ void main() { }); test( - 'Given a serverpod project with model files then the converted model path has the rootPathParts set to empty arrays.', + 'Given a serverpod project with model files then the converted model path has the subDirPathParts set to empty arrays.', () async { var models = await ModelHelper.loadProjectYamlModelsFromDisk(config); - var rootPathParts = models.map((e) => e.protocolRootPathParts); + var rootPathParts = models.map((e) => e.subDirPathParts); expect(rootPathParts.first, []); @@ -270,7 +183,7 @@ void main() { }); test( - 'Given a serverpod project with model files in a nested folder then the converted model path has the rootPathParts set to the nested folder inside the models directory.', + 'Given a serverpod project with model files in a nested folder then the converted model path has the subDirPathParts set to the nested folder inside the models directory.', () async { var serverRootDir = Directory(join( 'test', @@ -282,11 +195,13 @@ void main() { 'test_server', )); - var config = createGeneratorConfig(split(serverRootDir.path)); + var config = GeneratorConfigBuilder() + .withServerPackageDirectoryPathParts(split(serverRootDir.path)) + .withModules([]).build(); var models = await ModelHelper.loadProjectYamlModelsFromDisk(config); - var rootPathParts = models.map((e) => e.protocolRootPathParts); + var rootPathParts = models.map((e) => e.subDirPathParts); expect(rootPathParts.first, ['nested', 'folder']); }); @@ -323,7 +238,9 @@ fields: 'test_server', )); - var config = createGeneratorConfig(split(serverRootDir.path)); + var config = GeneratorConfigBuilder() + .withServerPackageDirectoryPathParts(split(serverRootDir.path)) + .withModules([]).build(); var models = await ModelHelper.loadProjectYamlModelsFromDisk(config); @@ -345,7 +262,9 @@ fields: 'test_server', )); - var config = createGeneratorConfig(split(serverRootDir.path)); + var config = GeneratorConfigBuilder() + .withServerPackageDirectoryPathParts(split(serverRootDir.path)) + .withModules([]).build(); var models = await ModelHelper.loadProjectYamlModelsFromDisk(config); diff --git a/tools/serverpod_cli/test/integration/util/model_helper/module_model_file_load_test.dart b/tools/serverpod_cli/test/integration/util/model_helper/module_model_file_load_test.dart new file mode 100644 index 0000000000..2a2825b614 --- /dev/null +++ b/tools/serverpod_cli/test/integration/util/model_helper/module_model_file_load_test.dart @@ -0,0 +1,842 @@ +import 'dart:io'; + +import 'package:path/path.dart'; +import 'package:serverpod_cli/src/config/config.dart'; +import 'package:serverpod_cli/src/test_util/builders/generator_config_builder.dart'; +import 'package:serverpod_cli/src/test_util/builders/module_config_builder.dart'; +import 'package:serverpod_cli/src/util/model_helper.dart'; +import 'package:test/test.dart'; +import 'package:uuid/uuid.dart'; + +void main() { + late Directory testDirectory; + late List testProjectPathParts; + setUpAll(() { + testDirectory = Directory(join(Directory.current.path, const Uuid().v4())); + testDirectory.createSync(recursive: true); + var testProject = Directory(join(testDirectory.path, const Uuid().v4())); + testProject.createSync(recursive: true); + testProjectPathParts = split(testProject.path); + }); + + tearDownAll(() { + testDirectory.deleteSync(recursive: true); + }); + + group('Given a valid model class with *.yaml file ending', () { + var modelFileName = 'example.yaml'; + + late Directory moduleProject; + late GeneratorConfig config; + setUp(() { + moduleProject = Directory(join(testDirectory.path, const Uuid().v4())); + moduleProject.createSync(recursive: true); + config = GeneratorConfigBuilder() + .withServerPackageDirectoryPathParts(testProjectPathParts) + .withModules([ + ModuleConfigBuilder('test_module') + .withServerPackageDirectoryPathParts(split(moduleProject.path)) + .build() + ]).build(); + }); + + tearDown(() { + moduleProject.deleteSync(recursive: true); + }); + + group('placed in the module "lib/src/models" directory when loaded', () { + late List models; + + setUp(() async { + var modelFile = File(join( + moduleProject.path, + 'lib', + 'src', + 'models', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is serialized.', () async { + expect(models, hasLength(1)); + }); + + test('then modelSource has no subDirPathParts.', () async { + expect(models.firstOrNull?.subDirPathParts, isEmpty); + }); + }); + + group('placed in the module "lib/src/protocol" directory when loaded', () { + late List models; + + setUp(() async { + var modelFile = File(join( + moduleProject.path, + 'lib', + 'src', + 'protocol', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is serialized.', () async { + expect(models, hasLength(1)); + }); + + test('then modelSource has no subDirPathParts.', () async { + expect(models.firstOrNull?.subDirPathParts, isEmpty); + }); + }); + + group( + 'placed in a subdirectory of the module "lib/src/models" directory when loaded', + () { + late List models; + + setUp(() async { + var modelFile = File(join( + moduleProject.path, + 'lib', + 'src', + 'models', + 'sub', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String + +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is serialized.', () async { + expect(models, hasLength(1)); + }); + + test('then modelSource has the correct subDirPathParts.', () async { + expect(models.firstOrNull?.subDirPathParts, ['sub']); + }); + }); + + group('placed in the module "lib/src" directory when loaded', () { + late List models; + + setUp(() async { + var modelFile = File(join( + moduleProject.path, + 'lib', + 'src', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is not serialized.', () async { + expect(models, hasLength(0)); + }); + }); + }); + + group('Given a valid model class with *.yml file ending', () { + var modelFileName = 'example.yml'; + + late Directory moduleProject; + late GeneratorConfig config; + setUp(() { + moduleProject = Directory(join(testDirectory.path, const Uuid().v4())); + moduleProject.createSync(recursive: true); + config = GeneratorConfigBuilder() + .withServerPackageDirectoryPathParts(testProjectPathParts) + .withModules([ + ModuleConfigBuilder('test_module') + .withServerPackageDirectoryPathParts(split(moduleProject.path)) + .build() + ]).build(); + }); + + tearDown(() { + moduleProject.deleteSync(recursive: true); + }); + + group('placed in the module "lib/src/models" directory when loaded', () { + late List models; + + setUp(() async { + var modelFile = File(join( + moduleProject.path, + 'lib', + 'src', + 'models', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is serialized.', () async { + expect(models, hasLength(1)); + }); + + test('then modelSource has no subDirPathParts.', () async { + expect(models.firstOrNull?.subDirPathParts, isEmpty); + }); + }); + + group('placed in the module "lib/src/protocol" directory when loaded', () { + late List models; + + setUp(() async { + var modelFile = File(join( + moduleProject.path, + 'lib', + 'src', + 'protocol', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is serialized.', () async { + expect(models, hasLength(1)); + }); + + test('then modelSource has no subDirPathParts.', () async { + expect(models.firstOrNull?.subDirPathParts, isEmpty); + }); + }); + + group( + 'placed in a subdirectory of the module "lib/src/models" directory when loaded', + () { + late List models; + + setUp(() async { + var modelFile = File(join( + moduleProject.path, + 'lib', + 'src', + 'models', + 'sub', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String + +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is serialized.', () async { + expect(models, hasLength(1)); + }); + + test('then modelSource has the correct subDirPathParts.', () async { + expect(models.firstOrNull?.subDirPathParts, ['sub']); + }); + }); + + group( + 'placed in a subdirectory of the module "lib/src/protocol" directory when loaded', + () { + late List models; + + setUp(() async { + var modelFile = File(join( + moduleProject.path, + 'lib', + 'src', + 'protocol', + 'sub', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String + +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is serialized.', () async { + expect(models, hasLength(1)); + }); + + test('then modelSource has the correct subDirPathParts.', () async { + expect(models.firstOrNull?.subDirPathParts, ['sub']); + }); + }); + + group('placed in the module "lib/src" directory when loaded', () { + late List models; + + setUp(() async { + var modelFile = File(join( + moduleProject.path, + 'lib', + 'src', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is not serialized.', () async { + expect(models, hasLength(0)); + }); + }); + }); + + group('Given a valid model class with *.spy file ending', () { + var modelFileName = 'example.spy'; + + late Directory moduleProject; + late GeneratorConfig config; + setUp(() { + moduleProject = Directory(join(testDirectory.path, const Uuid().v4())); + moduleProject.createSync(recursive: true); + config = GeneratorConfigBuilder() + .withServerPackageDirectoryPathParts(testProjectPathParts) + .withModules([ + ModuleConfigBuilder('test_module') + .withServerPackageDirectoryPathParts(split(moduleProject.path)) + .build() + ]).build(); + }); + + tearDown(() { + moduleProject.deleteSync(recursive: true); + }); + + group('placed in the module "lib/src/models" directory when loaded', () { + late List models; + + setUp(() async { + var modelFile = File(join( + moduleProject.path, + 'lib', + 'src', + 'models', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is serialized.', () async { + expect(models, hasLength(1)); + }); + + test('then modelSource has no subDirPathParts.', () async { + expect(models.firstOrNull?.subDirPathParts, isEmpty); + }); + }); + + group('placed in the module "lib/src/protocol" directory when loaded', () { + late List models; + + setUp(() async { + var modelFile = File(join( + moduleProject.path, + 'lib', + 'src', + 'protocol', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is serialized.', () async { + expect(models, hasLength(1)); + }); + + test('then modelSource has no subDirPathParts.', () async { + expect(models.firstOrNull?.subDirPathParts, isEmpty); + }); + }); + + group( + 'placed in a subdirectory of the module "lib/src/models" directory when loaded', + () { + late List models; + + setUp(() async { + var modelFile = File(join( + moduleProject.path, + 'lib', + 'src', + 'models', + 'sub', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String + +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is serialized.', () async { + expect(models, hasLength(1)); + }); + + test('then modelSource has the correct subDirPathParts.', () async { + expect(models.firstOrNull?.subDirPathParts, ['sub']); + }); + }); + + group( + 'placed in a subdirectory of the module "lib/src/protocol" directory when loaded', + () { + late List models; + + setUp(() async { + var modelFile = File(join( + moduleProject.path, + 'lib', + 'src', + 'protocol', + 'sub', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String + +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is serialized.', () async { + expect(models, hasLength(1)); + }); + + test('then modelSource has the correct subDirPathParts.', () async { + expect(models.firstOrNull?.subDirPathParts, ['sub']); + }); + }); + + group('placed in the module "lib/src" directory when loaded', () { + late List models; + + setUp(() async { + var modelFile = File(join( + moduleProject.path, + 'lib', + 'src', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is not serialized.', () async { + expect(models, hasLength(0)); + }); + }); + }); + + group('Given a valid model class with *.spy.yaml file ending', () { + var modelFileName = 'example.spy.yaml'; + + late Directory moduleProject; + late GeneratorConfig config; + setUp(() { + moduleProject = Directory(join(testDirectory.path, const Uuid().v4())); + moduleProject.createSync(recursive: true); + config = GeneratorConfigBuilder() + .withServerPackageDirectoryPathParts(testProjectPathParts) + .withModules([ + ModuleConfigBuilder('test_module') + .withServerPackageDirectoryPathParts(split(moduleProject.path)) + .build() + ]).build(); + }); + + tearDown(() { + moduleProject.deleteSync(recursive: true); + }); + + group('placed in the module "lib/src/models" directory when loaded', () { + late List models; + + setUp(() async { + var modelFile = File(join( + moduleProject.path, + 'lib', + 'src', + 'models', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is serialized.', () async { + expect(models, hasLength(1)); + }); + + test('then modelSource has no subDirPathParts.', () async { + expect(models.firstOrNull?.subDirPathParts, isEmpty); + }); + }); + + group('placed in the module "lib/src/protocol" directory when loaded', () { + late List models; + + setUp(() async { + var modelFile = File(join( + moduleProject.path, + 'lib', + 'src', + 'protocol', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is serialized.', () async { + expect(models, hasLength(1)); + }); + + test('then modelSource has no subDirPathParts.', () async { + expect(models.firstOrNull?.subDirPathParts, isEmpty); + }); + }); + + group( + 'placed in a subdirectory of the module "lib/src/models" directory when loaded', + () { + late List models; + + setUp(() async { + var modelFile = File(join( + moduleProject.path, + 'lib', + 'src', + 'models', + 'sub', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String + +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is serialized.', () async { + expect(models, hasLength(1)); + }); + + test('then modelSource has the correct subDirPathParts.', () async { + expect(models.firstOrNull?.subDirPathParts, ['sub']); + }); + }); + + group( + 'placed in a subdirectory of the module "lib/src/protocol" directory when loaded', + () { + late List models; + + setUp(() async { + var modelFile = File(join( + moduleProject.path, + 'lib', + 'src', + 'protocol', + 'sub', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String + +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is serialized.', () async { + expect(models, hasLength(1)); + }); + + test('then modelSource has the correct subDirPathParts.', () async { + expect(models.firstOrNull?.subDirPathParts, ['sub']); + }); + }); + + group('placed in the module "lib/src" directory when loaded', () { + late List models; + + setUp(() async { + var modelFile = File(join( + moduleProject.path, + 'lib', + 'src', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is not serialized.', () async { + expect(models, hasLength(0)); + }); + }); + }); + + group('Given a valid model class with *.spy.yml file ending', () { + var modelFileName = 'example.spy.yml'; + + late Directory moduleProject; + late GeneratorConfig config; + setUp(() { + moduleProject = Directory(join(testDirectory.path, const Uuid().v4())); + moduleProject.createSync(recursive: true); + config = GeneratorConfigBuilder() + .withServerPackageDirectoryPathParts(testProjectPathParts) + .withModules([ + ModuleConfigBuilder('test_module') + .withServerPackageDirectoryPathParts(split(moduleProject.path)) + .build() + ]).build(); + }); + + tearDown(() { + moduleProject.deleteSync(recursive: true); + }); + + group('placed in the module "lib/src/models" directory when loaded', () { + late List models; + + setUp(() async { + var modelFile = File(join( + moduleProject.path, + 'lib', + 'src', + 'models', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is serialized.', () async { + expect(models, hasLength(1)); + }); + + test('then modelSource has no subDirPathParts.', () async { + expect(models.firstOrNull?.subDirPathParts, isEmpty); + }); + }); + + group('placed in the module "lib/src/protocol" directory when loaded', () { + late List models; + + setUp(() async { + var modelFile = File(join( + moduleProject.path, + 'lib', + 'src', + 'protocol', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is serialized.', () async { + expect(models, hasLength(1)); + }); + + test('then modelSource has no subDirPathParts.', () async { + expect(models.firstOrNull?.subDirPathParts, isEmpty); + }); + }); + + group( + 'placed in a subdirectory of the module "lib/src/models" directory when loaded', + () { + late List models; + + setUp(() async { + var modelFile = File(join( + moduleProject.path, + 'lib', + 'src', + 'models', + 'sub', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String + +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is serialized.', () async { + expect(models, hasLength(1)); + }); + + test('then modelSource has the correct subDirPathParts.', () async { + expect(models.firstOrNull?.subDirPathParts, ['sub']); + }); + }); + + group( + 'placed in a subdirectory of the module "lib/src/protocol" directory when loaded', + () { + late List models; + + setUp(() async { + var modelFile = File(join( + moduleProject.path, + 'lib', + 'src', + 'protocol', + 'sub', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String + +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is serialized.', () async { + expect(models, hasLength(1)); + }); + + test('then modelSource has the correct subDirPathParts.', () async { + expect(models.firstOrNull?.subDirPathParts, ['sub']); + }); + }); + + group('placed in the module "lib/src" directory when loaded', () { + late List models; + + setUp(() async { + var modelFile = File(join( + moduleProject.path, + 'lib', + 'src', + modelFileName, + )); + modelFile.createSync(recursive: true); + modelFile.writeAsStringSync(''' + class: Example + fields: + name: String +'''); + models = await ModelHelper.loadProjectYamlModelsFromDisk(config); + }); + + test('then the class is not serialized.', () async { + expect(models, hasLength(0)); + }); + }); + }); +}