fix(campaigns): priority-goals Value enforced as micro-currency#401
Conversation
Per Yandex Direct API contract (add-text-campaign, strategies-types), PriorityGoalsItem.Value is xsd:long in advertiser currency × 1,000,000 — the same micro-currency contract as --budget and other money flags after #399/#400. Previously parse_priority_goals_spec accepted raw integers and forwarded them unchanged, so --priority-goals 1:500 emitted Value: 500 which the API interprets as 0.0005 currency units. - direct_cli/utils.py: parse_priority_goals_spec rejects negative values and values below MICRO_RUBLE_MIN with a helpful "Did you mean N×1,000,000?" suggestion. - direct_cli/commands/campaigns.py: --priority-goals help text now documents the micro-currency contract on both option declarations (add + update). - direct_cli/_bidding_strategy.py: removed the obsolete "KNOWN ISSUE #387" comment. - tests/test_dry_run.py: bumped all priority-goals fixture values to realistic micro-currency amounts (20→20000000 etc.) and added two reject tests: sub-MICRO_RUBLE_MIN value and negative value. Closes #387. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
|
@claude review. Focus on critical issues: bugs, security vulnerabilities, logical errors, data loss risks, performance problems. Do NOT nitpick style, naming conventions, minor formatting, or subjective preferences — only flag issues that could break functionality or cause real harm in production. |
|
Claude finished @axisrow's task in 3m 10s —— View job Claude PR Review
VerdictThe campaigns-side fix is sound and well-tested. Validation mirrors No issues flagged in the diff itself. Critical finding — sibling bug NOT addressed (same #387 root cause)
# direct_cli/commands/strategies.py:255-281
def _parse_priority_goal(spec: str) -> dict:
...
try:
item = {"GoalId": int(parts[0]), "Value": int(parts[1])}
except ValueError:
...That parser is called from both strategy mutations:
The WSDL evidence this PR's description leans on — still emits This PR is explicitly scoped to
The two parsers have different signatures ( Nits (not blockers)
|
`direct strategies add` / `direct strategies update` build PriorityGoals.Items via a separate parser (`_parse_priority_goal`) which forwarded `Value` as a raw int — same #387 data-loss class as the campaigns side. Per WSDL (tests/wsdl_cache/strategies.xml lines 503-509), PriorityGoalsItem.Value is xsd:long advertiser currency × 1,000,000 for standalone Strategy* items too, so the contract is shared and the validation must be too. - Extract `validate_priority_goal_value(value_int, error_prefix)` in utils.py from the campaign-side parser (single source of truth for the < 0 and 0 < v < MICRO_RUBLE_MIN guards, plus the `Did you mean N×1,000,000?` hint). - Call it from `_parse_priority_goal` in strategies.py so `direct strategies add ... --priority-goal 1:500` is now rejected at CLI parse time instead of silently sending Value=500 (≈0.0005 RUB). - Update help text on both `--priority-goal` decorators to document the micro-currency contract. - Add `test_strategies_add_rejects_priority_goal_value_below_micro_min` and `test_strategies_update_rejects_priority_goal_negative_value` parallel to the campaigns reject tests. Now genuinely Closes #387 (review-found sibling bug on the strategies surface). Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
|
@claude review. Focus on critical issues: bugs, security vulnerabilities, logical errors, data loss risks, performance problems. Do NOT nitpick style, naming conventions, minor formatting, or subjective preferences — only flag issues that could break functionality or cause real harm in production. |
|
Claude finished @axisrow's task in 2m 18s —— View job Claude PR Review
Verdict — LGTM, no critical issuesThe follow-up commit What I verified1. Shared helper is the single source of truth. 2. All
No third path exists. 3. Tests cover both surfaces and both failure modes.
The strategies update test reaches the parser correctly: with only 4. CHANGELOG correctly scopes the breaking change to both 5. Existing fixture migration is consistent. Every prior Notes (non-blocking)
Branch: |
Six PAYLOAD_CASES fixtures in tests/api_coverage_payloads.py still used raw-ruble Value (e.g. ``1234567:80``) which the new parse_priority_goals_spec validator rejects as below MICRO_RUBLE_MIN. Bump them to ``1234567:80000000,9876543:20000000`` to match the post-#387 contract — same change the first commit made for tests/test_dry_run.py fixtures. This unblocks CI: test_dry_run_payload_schema_coverage[ campaigns-add-argv48,53,54,55,56] and test_dry_run_payload_schema_coverage[campaigns-update-argv57], plus the three cascading schema-gate tests. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
|
@claude review. Focus on critical issues: bugs, security vulnerabilities, logical errors, data loss risks, performance problems. Do NOT nitpick style, naming conventions, minor formatting, or subjective preferences — only flag issues that could break functionality or cause real harm in production. |
|
Claude finished @axisrow's task in 3m 5s —— View job Claude PR Review
Verdict — LGTM, no critical issuesNo bugs, security vulnerabilities, logical errors, data-loss risks, or perf regressions found. The earlier sibling-bug call-out has been fully addressed by commit What I verified1. Single source of truth. 2. Both parsers wired through the helper.
3. No third
No bypass path. 4. Strategies-update reject test correctly reaches the parser. For
Confirmed. 5. Fixture migration is complete. Every prior 6. The Notes (non-blocking, no action required)
|
Summary
Closes #387 —
--priority-goalsnow enforces the same micro-currencycontract as the rest of the campaign money flags (
--budget,--average-cpa, …) that were tightened in #399/#400.Per Yandex Direct API (add-text-campaign, strategies-types),
PriorityGoalsItem.Valueisxsd:longin advertiser currency multipliedby 1,000,000 for every strategy that accepts
PriorityGoals:AVERAGE_CPA_MULTIPLE_GOALSPAY_FOR_CONVERSION_MULTIPLE_GOALSMAX_PROFITAverageCpa/AverageRoi/AverageCrrwithIsMetrikaSourceOfValue=YESThe WSDL (
tests/wsdl_cache/campaigns.xmllines 1928-1934,tests/wsdl_cache/strategies.xmllines 503-514) confirmsValueis thesame
xsd:longforTextCampaign,UnifiedCampaign,DynamicTextCampaign,SmartCampaign, and standaloneStrategy*items — no per-strategy unit variation.
Previously
parse_priority_goals_specparsedvalue_partas rawintand forwarded it unchanged, so--priority-goals 1:500emittedValue: 500which the API interprets as0.0005advertiser-currencyunits (4 orders of magnitude off the user's intent).
Changes
direct_cli/utils.py—parse_priority_goals_specrejects negativevalues and values below
MICRO_RUBLE_MIN(100,000 = 0.1 unit) with ahelpful
Did you mean N×1,000,000?suggestion. The contract nowmirrors
MicroRublesParamType.convert(used by--budgetetc.).direct_cli/commands/campaigns.py—--priority-goalshelp text onboth
addandupdateoption declarations now documents themicro-currency contract.
direct_cli/_bidding_strategy.py— removed obsoleteKNOWN ISSUE #387comment block.tests/test_dry_run.py— bumped all--priority-goalsfixturevalues to realistic micro-currency amounts (e.g.
20→20000000,80→80000000,500→500000000) and added two new reject tests:test_campaigns_add_rejects_priority_goals_value_below_micro_min—raw-ruble input (
1:500) is rejected with the suggestion.test_campaigns_add_rejects_priority_goals_negative_value—negative
Valueis rejected up-front.Test plan
pytest tests/test_dry_run.py -k priority_goals— 29 passed.pytest tests/test_dry_run.py— 1003 passed.flake8 direct_cli/utils.py direct_cli/_bidding_strategy.py direct_cli/commands/campaigns.py— clean for changed files.direct campaigns add --dry-run --type TEXT_CAMPAIGN --name X --start-date 2026-06-01 --search-strategy AVERAGE_CPA_MULTIPLE_GOALS --network-strategy SERVING_OFF --priority-goals 1:80000000,2:20000000producesValue: 80000000/Value: 20000000in the wire payload.--priority-goals 1:500is rejected with the "Did you mean 500000000?" hint.🤖 Generated with Claude Code