From 618cf4b02308ec2cd9f63a92c57664567dee3526 Mon Sep 17 00:00:00 2001 From: arrrrny Date: Wed, 12 Aug 2026 08:25:14 +0000 Subject: [PATCH] fix(test builder): add toggle case to per-method entity test generator (#289) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR #287 added the entity-methods default [get, update, toggle] to both di_plugin.dart and test_plugin.dart so canonical zfa make invocations route through per-method generation. Every other generator that switches on method already had a toggle case (usecase, controller, presenter, view, repository, datasource, di, mock) EXCEPT the per-method test builder (test_builder_entity.dart). Its switch covered get/getList/list /create/update/delete/watch/watchList but not toggle, so the test plugin crashed with ArgumentError: Unknown method: toggle before emitting any file. The two test-builder helpers (_getFallbackValues and _generateFutureTests in test_builder_helpers.dart) also had no toggle branch, so even if the switch were relaxed the emitted test file would have been empty. Fix: add case toggle to test_builder_entity.dart (className = Toggle${entityName}UseCase, returnTypeConstructor = t$entityName, isStream/isCompletable = false — mirrors the usecase generator) and add matching toggle branches to _getFallbackValues (registerFallbackValue gets a concrete ToggleParams instance) and _generateFutureTests (paramsExpr + arrangeCall/verifyCall as mockVarName.toggle(any())) in test_builder_helpers.dart. Also adds a regression test in test/integration/toggle_method_test.dart that exercises the exact failing config from the issue (methods: [get, update, toggle] + all generation flags + generateTest: true) and asserts the toggle usecase test file is emitted with the expected shape (ToggleTodoUseCase class, mockRepository.toggle call, ToggleParams constructor, TodoFields.id field reference). Closes #289. --- .../test/builders/test_builder_entity.dart | 13 ++++ .../test/builders/test_builder_helpers.dart | 35 +++++++++++ test/integration/toggle_method_test.dart | 63 +++++++++++++++++++ 3 files changed, 111 insertions(+) diff --git a/lib/src/plugins/test/builders/test_builder_entity.dart b/lib/src/plugins/test/builders/test_builder_entity.dart index 592ca8b5..3912bf0d 100644 --- a/lib/src/plugins/test/builders/test_builder_entity.dart +++ b/lib/src/plugins/test/builders/test_builder_entity.dart @@ -54,6 +54,19 @@ extension TestBuilderEntity on TestBuilder { className = 'Update${entityName}UseCase'; returnTypeConstructor = 't$entityName'; break; + case 'toggle': + // #289: PR #287 added 'toggle' to the entity-methods default used by + // the di/test plugins (['get', 'update', 'toggle']) so canonical + // `zfa make --preset=crud --with=vpc,state,di,test` routes to + // per-method generation. Every other generator (usecase, controller, + // presenter, view, repository, datasource, di) has a `toggle` case — + // the per-method test builder must too, otherwise the test plugin + // crashes with `Unknown method: toggle` before any file is written. + // Mirrors the usecase generator: `Toggle${entityName}UseCase` returns + // the toggled entity (Future), not a stream and not void. + className = 'Toggle${entityName}UseCase'; + returnTypeConstructor = 't$entityName'; + break; case 'delete': className = 'Delete${entityName}UseCase'; returnTypeConstructor = 'null'; diff --git a/lib/src/plugins/test/builders/test_builder_helpers.dart b/lib/src/plugins/test/builders/test_builder_helpers.dart index 1d2e020a..bdb75fef 100644 --- a/lib/src/plugins/test/builders/test_builder_helpers.dart +++ b/lib/src/plugins/test/builders/test_builder_helpers.dart @@ -89,6 +89,22 @@ extension TestBuilderHelpers on TestBuilder { 'UpdateParams<$idType, $dataType>', ).call([], {'id': idValue, 'data': dataValue}), ]; + case 'toggle': + // #289: registerFallbackValue needs a concrete ToggleParams instance + // matching the usecase generator's signature (ToggleParams + // with id/field/value). `${entityName}Fields` is the Field-class + // re-exported by the entity file; `config.queryField` (default 'id') + // resolves to a `Field` constant the same way the + // `get` branch below does for its QueryParams filter. + return [ + refer( + 'ToggleParams<$idType, ${entityName}Fields>', + ).call([], { + 'id': idValue, + 'field': refer('${entityName}Fields').property(config.queryField), + 'value': literalBool(true), + }), + ]; case 'delete': return [ refer('DeleteParams<$idType>').constInstance([], {'id': idValue}), @@ -182,6 +198,25 @@ extension TestBuilderHelpers on TestBuilder { verifyCall = refer( mockVarName, ).property('update').call([refer('any').call([])]); + } else if (method == 'toggle') { + // #289: Mirror the usecase generator's toggle shape — ToggleParams + // with id, field (a Field from ${entityName}Fields), and + // a bool value. The mock repository call is `toggle(any())`, identical to + // update/create — the per-method test builder only needs the params + // expression and the mock call shape to match. + paramsExpr = refer( + 'ToggleParams<$idType, ${entityName}Fields>', + ).call([], { + 'id': idValue, + 'field': refer('${entityName}Fields').property(config.queryField), + 'value': literalBool(true), + }); + arrangeCall = refer( + mockVarName, + ).property('toggle').call([refer('any').call([])]); + verifyCall = refer( + mockVarName, + ).property('toggle').call([refer('any').call([])]); } else if (method == 'delete') { paramsExpr = refer('DeleteParams<$idType>').call([], {'id': idValue}); arrangeCall = refer( diff --git a/test/integration/toggle_method_test.dart b/test/integration/toggle_method_test.dart index bebe8dbf..d7fedd61 100644 --- a/test/integration/toggle_method_test.dart +++ b/test/integration/toggle_method_test.dart @@ -165,4 +165,67 @@ void main() { expect(controllerContent, contains('isToggling')); expect(controllerContent, contains('_presenter.toggleTodo')); }); + + // Regression test for #289: PR #287 added 'toggle' to the entity-methods + // default used by the di/test plugins (['get', 'update', 'toggle']) so a + // canonical `zfa make --preset=crud --with=vpc,state,di,test` + // routes through the per-method generators. The test builder's method + // switch had no `toggle` case, so the test plugin crashed with + // `Unknown method: toggle` before emitting any file. This locks in the + // fix: with the test plugin on + toggle in the methods list, generation + // succeeds AND the per-method toggle usecase test file is emitted with the + // expected shape. + test( + '#289 — toggle test file is generated when test plugin is on (no Unknown method: toggle crash)', + () async { + final generator = CodeGenerator( + config: GeneratorConfig( + name: 'Todo', + methods: const ['get', 'update', 'toggle'], + generateData: true, + generateLocal: true, + generateUseCase: true, + generateVpcs: true, + generateState: true, + generateDi: true, + generateTest: true, + outputDir: outputDir, + ), + outputDir: outputDir, + options: const GeneratorOptions( + dryRun: false, + force: true, + verbose: false, + ), + ); + + final result = await generator.generate(); + expect( + result.success, + isTrue, + reason: 'Generation crashed: ${result.errors.join('; ')}', + ); + + // The per-method test builder must emit a toggle-specific test file. + final toggleTestFile = File( + '$outputDir/../../test/domain/usecases/todo/toggle_todo_usecase_test.dart', + ); + expect( + toggleTestFile.existsSync(), + isTrue, + reason: 'toggle_todo_usecase_test.dart should be generated', + ); + + final content = toggleTestFile.readAsStringSync(); + // Class name follows the usecase generator's pattern. + expect(content, contains('ToggleTodoUseCase')); + // The mock repository must be exercised via its toggle method. + expect(content, contains('mockRepository.toggle(')); + // The params constructor must match the usecase generator's signature. + expect(content, contains('ToggleParams')); + // The Field constant must come from the entity's Fields class + // (config.queryField defaults to 'id'). + expect(content, contains('TodoFields.id')); + }, + ); }