whisper-money/tests/Feature
Víctor Falcón 77d55f88df
refactor(mcp): dedupe record resolution and field updates in the write tools (#767)
## Why

Second pass at the complexity report. After the trial-experiment code
(deleted by #762) and the analytics controller (#766), the worst
offenders were the MCP write tools:

- `UpdateTransaction::write` — cyclomatic complexity **20**
- `UpdateAutomationRule::write` — cyclomatic complexity **13**

Both for the same reason: a long wall of `if ($request->has('x')) {
$model->x = ...; }` blocks, one per optional field.

## What changed

Two shapes moved into `WriteTool`:

**`modelInSpace()`** — resolving a record in the space was written five
times (account, transaction, category, label, plus a rule resolver that
existed twice, once in `UpdateAutomationRule` and once in
`DeleteAutomationRule`), each a copy of the same query + null check +
message. The four public helpers are now one-liners over it, and
`ruleInSpace()` is shared instead of duplicated.

**`applyFields()`** — assigns only the fields the request actually
carries, which is the "only what you pass changes" contract these tools
document. Values are closures, so resolving a related model
(`accountInSpace`, `categoryInSpace` — either can throw a validation
error) still happens only when its field is present.

## Metrics

| | before | after |
|---|---|---|
| methods over complexity 10 | 35 | 33 |
| `UpdateTransaction::write` | 20 | 7 |
| `UpdateAutomationRule::write` | 13 | 6 |
| duplicated lines | 5.34% | 5.34% |

Duplication does not move, and it's worth being straight about why: what
jscpd still flags across `app/Mcp/Tools` is **file headers** — the `use`
block, the `#[Description]` attribute, the class line and the opening of
`schema()`. Nine near-identical lines per tool that no extraction can
remove. Deleting the duplicated `ruleInSpace` shortened
`DeleteAutomationRule` enough that its header became a clone pair with
another tool's, so the counter stayed flat while the real duplication
went away.

## Testing

`tests/Feature/Mcp` — 46 tests green, plus a new one. Nothing asserted
the resolver failure messages before, and this PR changes how they are
built (a shared template plus an optional hint), so the new test pins
both branches: the message that points at a listing tool (`Call
search_transactions to find ids.`) and the one that has no hint to
append.
2026-08-11 14:35:01 +02:00
..
Ai feat(stats): post the Discord stats reports in Spanish, opened by an AI summary (#752) 2026-08-10 10:13:40 +02:00
Api
…
Auth
…
Console fix(demo): stop demo:reset from colliding on the fake Stripe subscription id (#756) 2026-08-10 12:48:13 +00:00
Events
…
Jobs
…
Listeners
…
Mcp refactor(mcp): dedupe record resolution and field updates in the write tools (#767) 2026-08-11 14:35:01 +02:00
Onboarding fix(onboarding): don't trap users on the syncing step when a bank sync fails (#745) 2026-08-09 18:40:49 +02:00
OpenBanking fix(binance): value past days from Binance's own snapshot total (#763) 2026-08-11 11:04:06 +00:00
Services
…
Settings fix(budgets): keep per-transaction email notifications opt-in (#735) 2026-07-25 12:36:13 +02:00
Spaces
…
Sync
…
AccountBalanceControllerTest.php
…
AccountControllerTest.php
…
AccountImportConfigTest.php
…
AccountUserCurrencyServiceTest.php
…
AiConsentSettingsTest.php
…
AiConsentTest.php
…
AlignAccountsEncryptedFlagMigrationTest.php
…
ApplyRealEstateRevaluationTest.php
…
AuthenticatedLayoutSafeAreaTest.php
…
AutomationRuleApplicationTest.php
…
AutomationRuleEvaluationTest.php
…
AutomationRuleTest.php fix(ai): stop a learned rule title from 500-ing the user's category change (#741) 2026-08-09 15:39:14 +00:00
BackfillAccountIbansCommandTest.php
…
BackfillXxxAccountCurrenciesTest.php
…
BalanceLookupTest.php
…
BudgetHistoricalAssignmentTest.php
…
BudgetNotificationTest.php feat(budgets): enable email notifications by default and coalesce alerts per transaction (#733) 2026-07-24 15:33:14 +02:00
BudgetPeriodDateTest.php
…
BudgetPeriodServiceTest.php
…
BudgetTest.php
…
BudgetTransactionServiceTest.php
…
BulkUpdateTransactionsTest.php
…
CancelFreeEnableBankingConnectionsCommandTest.php
…
CashflowAnalyticsTest.php
…
CashflowPageTest.php
…
CatchAllBudgetTest.php
…
CategoryMonthlyBreakdownTest.php
…
CrapCommandTest.php ci: add duplication and complexity quality checks (#765) 2026-08-11 13:29:37 +02:00
CurrencyConversionServiceTest.php
…
DashboardAnalyticsTest.php
…
DashboardTest.php
…
DecryptTransactionsTest.php
…
DeleteUserCommandTest.php
…
DemoAccountRestrictionsTest.php fix(demo): stop demo:reset from colliding on the fake Stripe subscription id (#756) 2026-08-10 12:48:13 +00:00
DisconnectBankingConnectionsCommandTest.php
…
DiscordReportEmbedLimitsTest.php feat(stats): post the Discord stats reports in Spanish, opened by an AI summary (#752) 2026-08-10 10:13:40 +02:00
DiscordWebhookTest.php
…
EncryptionTest.php
…
ExampleTest.php
…
ExchangeRateServiceTest.php
…
GenerateStripePromotionCodesCommandTest.php chore(deps): update composer dependencies to latest (#764) 2026-08-11 11:15:27 +00:00
IdorVulnerabilityTest.php
…
ImportDataTest.php
…
InertiaSharedDataTest.php
…
IntegrationRequestTest.php
…
LabelTest.php
…
LoanTest.php
…
LocalizationTest.php feat(currencies): add the Danish Krone (DKK) (#754) 2026-08-10 12:08:56 +02:00
LoggingConfigTest.php
…
MailSenderTest.php
…
NewTransactionsMarkerTest.php
…
OpenAiAppsChallengeTest.php feat(mcp): serve the ChatGPT app directory domain challenge (#749) 2026-08-10 07:46:49 +00:00
PersistUpsellSourceFromStripeTest.php
…
PlaintextTransactionsTest.php
…
PlanFeatureTest.php
…
PopoverSafeAreaTest.php
…
PostStripeEventToDiscordTest.php
…
PurgeResidualEncryptionArtifactsJobTest.php
…
PwaTest.php
…
QueueConfigTest.php
…
ReEvaluateTransactionRulesTest.php
…
RealEstateAvailabilityTest.php
…
RealEstateTest.php
…
ResendSyncCommandTest.php
…
RouteNotificationForMailTest.php
…
RuleEngineParityTest.php
…
SavedFilterTest.php feat(transactions): add a monthly trend view to the analysis drawer (#736) 2026-07-26 17:03:52 +02:00
SelfServeRefundTest.php fix(demo): stop demo:reset from colliding on the fake Stripe subscription id (#756) 2026-08-10 12:48:13 +00:00
SendAiCohortReportCommandTest.php feat(stats): post the Discord stats reports in Spanish, opened by an AI summary (#752) 2026-08-10 10:13:40 +02:00
SendAiConsentFollowUpEmailsCommandTest.php
…
SendDailyStatsReportCommandTest.php feat(stats): post the Discord stats reports in Spanish, opened by an AI summary (#752) 2026-08-10 10:13:40 +02:00
SendExperimentFunnelReportCommandTest.php feat(stats): post the Discord stats reports in Spanish, opened by an AI summary (#752) 2026-08-10 10:13:40 +02:00
SendStuckCohortReportCommandTest.php
…
SendSubscriptionFunnelReportCommandTest.php feat(stats): post the Discord stats reports in Spanish, opened by an AI summary (#752) 2026-08-10 10:13:40 +02:00
SentryConfigTest.php
…
SentryUserMiddlewareTest.php
…
SetLocaleTest.php
…
SitemapTest.php
…
StrayHttpRequestGuardTest.php
…
StripeSubscriptionStatsCommandTest.php
…
SubscriptionExperimentTest.php chore(deps): update composer dependencies to latest (#764) 2026-08-11 11:15:27 +00:00
SubscriptionTest.php
…
SuggestionPersistenceTest.php
…
SyncBankingConnectionsCommandTest.php
…
SyncStripePricesCommandTest.php
…
TrackLastActiveAtTest.php
…
TransactionAnalysisTest.php feat(transactions): add a monthly trend view to the analysis drawer (#736) 2026-07-26 17:03:52 +02:00
TransactionFilterTest.php
…
TransactionSideClassificationTest.php
…
TransactionTest.php
…
WelcomeBanksOrderingTest.php
…