Skip to content

fix(test builder): add toggle case to per-method entity test generator (#289) - #290

Merged
arrrrny merged 1 commit into
developmentfrom
fix/289-test-builder-toggle-case
Aug 12, 2026
Merged

fix(test builder): add toggle case to per-method entity test generator (#289)#290
arrrrny merged 1 commit into
developmentfrom
fix/289-test-builder-toggle-case

Conversation

@arrrrny

@arrrrny arrrrny commented Aug 12, 2026

Copy link
Copy Markdown
Owner

Closes #289

Summary

Fixes #289zfa make crashed with Unknown method: toggle whenever an entity make used the default entity-methods default (introduced by #287) together with the test plugin (--with=test).

Root cause

PR #287 added the entity-methods default [\"get\", \"update\", \"toggle\"] to both di_plugin.dart and test_plugin.dart so canonical zfa make <Entity> --preset=crud --with=vpc,state,di,test invocations route through per-method generation. Every other generator that switches on method already had a case \"toggle\":

  • usecase ✓ (entity_usecase_generator.dart)
  • controller ✓ (controller_plugin_methods.dart)
  • presenter ✓ (presenter_plugin.dart)
  • view ✓ (view_plugin.dart)
  • repository ✓ (interface/cached/simple/synced)
  • datasource ✓ (local/remote/interface)
  • di ✓ (di_plugin.dart)
  • mock ✓

…EXCEPT the per-method test builder (test_builder_entity.dart) — its switch (method) 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

Purely additive — three files, +111 lines, no deletions:

  1. lib/src/plugins/test/builders/test_builder_entity.dart — add case \"toggle\": to the method switch. Mirrors the usecase generator: Toggle\${entityName}UseCase, returns the toggled entity (Future<Entity>), so returnTypeConstructor = \"t\$entityName\", isStream = false, isCompletable = false. The usecase-file-name logic falls through to the standard ${method}_${entitySnake}_usecase.dart pattern, so the emitted test file is named toggle_<entity>_usecase_test.dart.

  2. lib/src/plugins/test/builders/test_builder_helpers.dart:

    • _getFallbackValues: add case \"toggle\": returning ToggleParams<idType, \${entityName}Fields>(id: idValue, field: \${entityName}Fields.<queryField>, value: true) so registerFallbackValue receives a concrete ToggleParams matching the usecase generator signature.
    • _generateFutureTests: add else if (method == \"toggle\") branch constructing the same ToggleParams and calling mockVarName.toggle(any()) for both arrange and verify (mirrors the update branch).
  3. test/integration/toggle_method_test.dart — new regression test case that exercises the exact failing config from the issue (methods: [get, update, toggle] + all generation flags + generateTest: true) and asserts:

    • generation succeeds (no Unknown method: toggle),
    • toggle_<entity>_usecase_test.dart is emitted,
    • the file references Toggle<Entity>UseCase, mockRepository.toggle(, ToggleParams<String, <Entity>Fields>, and <Entity>Fields.id.

Why option 1 from the issue (add toggle case) vs. option 2 (drop toggle from defaults) vs. option 3 (shared registry)

Verification

  • dart analyze lib/: 1 pre-existing info-level lint in decorator_dispatcher.dart (unrelated, not touched). Test plugin subtree: clean.
  • dart test test/integration/toggle_method_test.dart: 2/2 pass.
  • dart test test/integration/ test/commands/make_command_test.dart: 32/32 pass.
  • dart test test/commands/test_command_test.dart test/core/plugin_system/plan_store_test.dart: 10/10 pass.
  • dart test test/integration/full_entity_workflow_test.dart test/integration/di_flag_parsing_test.dart test/integration/sync_with_di_test.dart: 3/3 pass.
  • dart test test/regression/: 34/34 pass.

Reproduction

Before this fix, the exact commands from the issue crash:

zfa setup zikzak_r2 --flutter --platforms=ios,macos
cd zikzak_r2
zfa entity create -n Product --field id:String --field name:String --field price:double
zfa make Product --preset=crud --with=vpc,state,di,test
# ❌ Generation failed: Invalid argument(s): Unknown method: toggle

After this fix, the same commands complete successfully and emit (among others):

  • test/domain/usecases/product/toggle_product_usecase_test.dart
  • test/domain/usecases/product/get_product_usecase_test.dart
  • test/domain/usecases/product/update_product_usecase_test.dart

Summary by CodeRabbit

  • New Features

    • Added support for generating tests for entity toggle operations.
    • Generated toggle tests now include entity IDs, configured fields, and enabled-state values.
    • Added repository interaction setup and verification for toggle operations.
  • Tests

    • Added integration coverage confirming toggle test generation succeeds and produces the expected test structure.

#289)

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<idType, ${entityName}Fields> 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<String, TodoFields> constructor, TodoFields.id field
reference).

Closes #289.
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4c5ea931-c126-4512-b6e6-c0fbf623b177

📥 Commits

Reviewing files that changed from the base of the PR and between a2d313d and 618cf4b.

📒 Files selected for processing (3)
  • lib/src/plugins/test/builders/test_builder_entity.dart
  • lib/src/plugins/test/builders/test_builder_helpers.dart
  • test/integration/toggle_method_test.dart

📝 Walkthrough

Walkthrough

The test plugin now supports toggle methods by generating Toggle<Entity>UseCase tests, constructing ToggleParams, verifying repository calls, and covering the flow with an integration regression test.

Changes

Toggle test generation

Layer / File(s) Summary
Toggle method dispatch
lib/src/plugins/test/builders/test_builder_entity.dart
The entity test builder maps toggle to Toggle<Entity>UseCase with an entity return type.
Toggle test construction and regression coverage
lib/src/plugins/test/builders/test_builder_helpers.dart, test/integration/toggle_method_test.dart
Generated tests use ToggleParams, arrange and verify repository toggle calls, and validate generated toggle test content.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

  • arrrrny/zuraffa#140: Added related toggle-generation changes involving ToggleParams and test builder integration.
  • arrrrny/zuraffa#287: Added related toggle handling in test and DI workflows.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR fixes the unknown toggle method crash, but generated tests may not typecheck because ToggleParams receives the entity ID field constant instead of an entity-fields instance [#289]. Pass an appropriately typed EntityFields instance to ToggleParams and add a compilation test for the generated toggle use-case test [#289].
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the test builder fix and explicitly mentions toggle support for per-method entity test generation.
Out of Scope Changes check ✅ Passed All changes support toggle generation in the test builder or verify the regression described in issue #289.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@arrrrny arrrrny left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewing PR #290: Fix toggle case in test builder.

Walkthrough

This PR adds the missing 'toggle' method case to the test builder, ensuring that ❌ Usage: zfa make ... [options]
Example: zfa make User route di with the flag succeeds when the toggle method is involved. It includes regression testing to verify the fix and prevent future regressions.

Changes Table

Layer / File(s) Summary
Added to method switch.
Updated and to support .
Added new regression test case.

Pre-merge Checks

  • Code compiles with the fix.
  • Regression tests pass.
  • Mirrors existing toggle support in other generators.

Verdict

The fix is surgical, well-reasoned, and correctly aligns the test builder with the rest of the generator system. LGTM.


A rabbit hops in, checks the code, and finds the toggle path on the toadstool. All is well.

@arrrrny

arrrrny commented Aug 12, 2026

Copy link
Copy Markdown
Owner Author

Walkthrough

Note

A focused, surgical fix for #289: the per-method test builder never got a toggle case when #287 added toggle to the entity-methods default for the di/test plugins, so zfa make ... --with=test crashed with ArgumentError: Unknown method: toggle before emitting any file. This PR adds the missing case 'toggle' to the method switch plus matching support in the two test-builder helpers, and locks it in with a regression test. The change is purely additive (+111 lines, 0 deletions) and mirrors the usecase generator's toggle shape (ToggleTodoUseCase, Future<Todo>).

Verified locally: dart analyze lib/src/plugins/test/ is clean, and all suites referenced in the PR description pass (toggle_method 2/2, make_command, test_command, plan_store, full_entity_workflow, di_flag_parsing, sync_with_di, regression 34/34). The 6 failing CI checks are a pre-existing infrastructure issue — pub get fails on the missing ../zorphy/zorphy_annotation path dependency, identical on development.

⚠️ One substantive concern found during review: the emitted toggle_<entity>_usecase_test.dart does not typecheck (ToggleParams<String, TodoFields>(field: TodoFields.id) passes a Field<Todo, String> constant where the parameter type is the abstract final class TodoFields, which has no instances). See the inline comment.

Changes
Layer / File(s) Summary
lib/src/plugins/test/builders/test_builder_entity.dart Adds case 'toggle' to the per-method test-builder switch — Toggle${entityName}UseCase, returnTypeConstructor = 't$entityName', no stream/complete flags.
lib/src/plugins/test/builders/test_builder_helpers.dart Adds toggle to _getFallbackValues (concrete ToggleParams for registerFallbackValue) and _generateFutureTests (params + mockRepository.toggle(any()) arrange/verify, mirroring the update branch).
test/integration/toggle_method_test.dart New regression test: the exact #289 config (methods: [get, update, toggle] + all flags + generateTest: true) generates successfully and emits toggle_todo_usecase_test.dart with the expected shape.

Estimated Review Effort: 2 (15 minutes)

Category of Change Complexity
Generator switch extension ⚡ Low
Fallback/params helpers ⚡ Low
Regression test ⚡ Low

Pre-Merge Checks

Check Status
dart analyze lib/src/plugins/test/ ✅ No issues found
dart test test/integration/toggle_method_test.dart ✅ 2/2 passing
dart test test/integration/ test/commands/make_command_test.dart ✅ 32/32 passing
dart test test/regression/ ✅ 34/34 passing
CI (analyze, format, test, dart_core, build_example*) ❌ Pre-existing infra failure — pub get can't resolve ../zorphy/zorphy_annotation (identical on development)
Emitted toggle_*_usecase_test.dart compiles ❌ Type error — see inline comment
🧸 Poetry
🐰 A toggle was missing, the test builder tripped,
   "Unknown method!" — the whole run slipped.
   One case added, the helpers aligned,
   Now toggle tests generate, properly defined. 🥕

@arrrrny arrrrny left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

  • 🎯 Functional Correctness · 🟠 Major · 🔨 Medium — lib/src/plugins/test/builders/test_builder_helpers.dart:104 — emitted toggle test file does not typecheck (field: TodoFields.id is a Field, not a TodoFields)
  • 🧪 Tests · 🟡 Minor · ⚡ Quick win — test/integration/toggle_method_test.dart:219 — regression test never compiles the generated test file

Nitpicks

🔵 Trivial — test/integration/toggle_method_test.dart:210 — the hardcoded '$outputDir/../../test/...' path magic couples the test to the workspace layout; derive it from the same outputDir.replaceAll('lib/src', '') computation the builder uses so the two can't silently drift apart.

Prompt for all review comments

🤖 Prompt for AI Agents

1. lib/src/plugins/test/builders/test_builder_helpers.dart:104 — ToggleParams<String, TodoFields>(field: TodoFields.id)
   does not typecheck: TodoFields.id is a static const Field<Todo, String>, but the field parameter has type
   F = TodoFields (abstract final class, no instances). Align the ToggleParams F type argument with the
   field-descriptor type consumed by the datasource (e.g. Field<Todo, bool>) across usecase/presenter/datasource/
   test builders, then dart analyze a generated workspace with methods [get, update, toggle].
2. test/integration/toggle_method_test.dart:219 — add a runDartAnalyze(workspace, [test file]) assertion (exit 0)
   so the emitted test is compile-verified; string-contains checks pass even when the file doesn't typecheck.
3. test/integration/toggle_method_test.dart:210 — derive the test file path from outputDir.replaceAll('lib/src', '')
   instead of the hardcoded '../..' to avoid drift between the test and the builder's path computation.

Verdict

The fix correctly resolves the crash (generation now succeeds with the test plugin + toggle), the switch alignment matches every other generator, and the regression test locks in the core behavior. However, the emitted toggle test file does not typecheck — the F type argument of ToggleParams is inconsistent with the values the whole stack produces — and the regression test only string-sniffs the output, so this ships silently. Recommend addressing the type-level inconsistency (or at minimum compile-verifying the generated file) before merge. The pre-existing CI failures are unrelated infra (missing ../zorphy/zorphy_annotation path dependency). 🐰

'ToggleParams<$idType, ${entityName}Fields>',
).call([], {
'id': idValue,
'field': refer('${entityName}Fields').property(config.queryField),

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness · 🟠 Major · 🔨 Medium

Emitted toggle test file does not typecheck — field: TodoFields.id is a Field, not a TodoFields

The toggle branch constructs ToggleParams<String, ${entityName}Fields>(id: ..., field: ${entityName}Fields.<queryField>, value: true) (here and again in _getFallbackValues at line 104). ToggleParams<I, F> declares field as type F, and the whole generated stack instantiates it as F = ${entityName}Fields. But ${entityName}Fields.id is a static const Field<Entity, IdType> property (see zorphy's fields_class_generator.dart and real generated entities), and abstract final class TodoFields has no instances — so no value of type TodoFields can ever be constructed.

dart analyze on a faithful minimal repro with the real ToggleParams and a real-shaped TodoFields fails:

error - The argument type 'Field<Todo, String>' can't be assigned to the parameter type 'TodoFields'.

Impact: in a real Flutter project, zfa make <Entity> --preset=crud --with=vpc,state,di,test with toggle in the methods list now succeeds (no crash — good) but emits toggle_<entity>_usecase_test.dart that fails dart analyze. The crash is replaced by silently-broken generated code.

Note this mirrors a latent inconsistency in the pre-existing toggle stack (the presenter also types field as ${entityName}Fields while the datasource consumes params.field as a Field via existing.copyWithField(params.field, ...)) — so the root fix is upstream: align ToggleParams's F parameter with the actual field-descriptor type (e.g. Field<Entity, bool>), or provide a constructible field value. Within this PR's scope, at minimum the emitted test should not reference ${entityName}Fields.<queryField> as a value.

Proposed fix

The cleanest fix is to stop treating the Fields class as a value type. Either:

  1. Change ToggleParams<I, F>'s F to the field descriptor type and generate ToggleParams<String, Field<Todo, bool>>(field: TodoFields.isCompleted, ...) across usecase/presenter/datasource/test — a broader, consistent fix; or
  2. In the test builder only, construct the params via a value that actually typechecks (e.g. a mocktail-safe placeholder of the correct type).

Because abstract final class TodoFields has no instances, option 2 is impossible without a core change — so this needs the upstream alignment (option 1). Verify with dart analyze on a generated workspace's test/domain/usecases/ after the change.

🤖 Prompt for AI Agents

In lib/src/plugins/test/builders/test_builder_helpers.dart (toggle branches in
_getFallbackValues and _generateFutureTests), the emitted
ToggleParams<String, TodoFields>(field: TodoFields.id, ...) does not typecheck:
TodoFields.id is a static const Field<Todo, String>, but the field parameter
has type F = TodoFields, an abstract final class with no instances. Align the
ToggleParams F type argument with the field-descriptor type actually consumed
(e.g. Field<Todo, bool>) across the usecase generator, presenter, datasource,
and test builders, then verify by generating a workspace with methods
[get, update, toggle] and running `dart analyze` on the emitted
test/domain/usecases/** files.

reason: 'toggle_todo_usecase_test.dart should be generated',
);

final content = toggleTestFile.readAsStringSync();

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧪 Tests · 🟡 Minor · ⚡ Quick win

Regression test never compiles the generated test file — it can't catch the type error above

The new test asserts result.success, file existence, and four contains(...) string checks against the emitted test file. It never runs dart analyze (or dart compile) on the generated workspace, so the emitted toggle_todo_usecase_test.dart could be full of type errors and this test would still pass — which is exactly what happens today (see the major finding). The test name promises "no Unknown method: toggle crash", and it verifies that, but the deliverable is a compilable test file, and that property is unverified.

The test utilities already expose runDartAnalyze(workspace, [...paths]) (regression_test_utils.dart) — wire it in so generation output is compile-verified, not just string-sniffed.

Proposed fix

After the expect(result.success, isTrue, ...) block, add:

final analyze = await runDartAnalyze(workspace, [
  'test/domain/usecases/todo/toggle_todo_usecase_test.dart',
]);
expect(analyze.exitCode, 0, reason: analyze.stdout);

(plus the equivalent dart analyze check on the lib/src output, which would also surface the pre-existing copyWithField gap in the generated local datasource).

🤖 Prompt for AI Agents

In test/integration/toggle_method_test.dart, the new #289 regression test
only asserts string containment on the emitted test file. Add a
runDartAnalyze(workspace, ['test/domain/usecases/todo/toggle_todo_usecase_test.dart'])
assertion (exit code 0) so generated test output is compile-verified; the
current suite passes even though the emitted toggle test file does not
typecheck. See regression_test_utils.dart for the helper.

@arrrrny

arrrrny commented Aug 12, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@arrrrny
arrrrny merged commit 4065524 into development Aug 12, 2026
2 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant