Skip to content

fix: 6 known-broken write command payloads caught by integration_write cassettes #18

Description

@axisrow

Context

During the VCR cassette recording session for PR #17 / #14, several write-commands failed against the sandbox with specific payload errors. Tests were hardened to pytest.fail on unknown errors and only pytest.skip on a narrow _is_sandbox_error pattern — so each item below currently skips at replay time via a known error pattern captured in its cassette.

This is an umbrella issue tracking those 6 outstanding payload issues. They split roughly into two groups: 3 real CLI bugs (direct-cli builds a wrong payload) and 3 test-fixture gaps (my integration test sends JSON missing required fields). Both categories convert a skipped test into a passing one once fixed, which in turn strengthens the regression guard.

Why cassettes are regression guards for this work

For each item below, PR #17 committed a YAML cassette that freezes the current broken request → error response pair. The fix workflow is the same shape every time:

  1. Edit the offending file (CLI code or test payload) so the request body changes.
  2. pytest -m integration_write -v — the existing cassette will immediately fail to match, because vcrpy's body matcher sees the new request.
  3. Re-record with real sandbox token:
    set -a && source .env && set +a
    pytest -m integration_write --record-mode=rewrite -v -k <TestClass>
    
  4. Audit the new cassette for token/login leaks:
    grep -r "$YANDEX_DIRECT_TOKEN" tests/cassettes/   # must be empty
    grep -r "$YANDEX_DIRECT_LOGIN" tests/cassettes/   # must be empty
    
  5. Remove the now-obsolete extra_patterns=(...) from the test's skip branch, so the test now asserts the API accepted the payload instead of silently skipping.
  6. Commit both: the source fix AND the regenerated cassette.

If anyone later reintroduces the old broken payload, body matching will fail and CI will flag it — that is the regression guard.

Real CLI bugs (3)

  • 1. feeds add — hardcodes unknown field SourceType

    • File: direct_cli/commands/feeds.py:75
    • Current: feed_data = {"Name": name, "Source": url, "SourceType": "URL"}
    • API: error_code=8000, error_detail=An item in the Feeds array contains the unknown parameter SourceType
    • Cassette: tests/cassettes/test_integration_write/TestWriteFeeds.test_add_update_delete.yaml
    • Fix hint: drop the SourceType key. Feed kind is inferred from the nested BusinessType / FileFeed structure per Yandex Direct API docs. Verify via Context7 docs before removing.
    • Test to unskip: TestWriteFeeds.test_add_update_delete — drop extra_patterns=("unknown parameter",) afterwards.
  • 2. adextensions add — sends duplicate top-level Type field

    • File: direct_cli/commands/adextensions.py:75
    • Current: ext_data = {"Type": ext_type} before merging user JSON
    • API: error_code=8000, error_detail=An item in the AdExtensions array contains the unknown parameter Type
    • Cassette: tests/cassettes/test_integration_write/TestWriteAdExtensions.test_add_delete.yaml
    • Fix hint: stop building ext_data with Type. The API derives extension type from the nested field name (Callout / Sitelinks / ...). Drop the CLI --type option or make it dry-run-only.
    • Same Type-field bug class as PR fix+test: stop sending implicit Type field in add commands; add dry-run coverage #12 (ads / smartadtargets). Re-use the same fix pattern.
    • Test to unskip: TestWriteAdExtensions.test_add_delete.
  • 3. bidmodifiers set cannot create new modifiers

    • File: direct_cli/commands/bidmodifiers.py:79
    • Current: set builds {CampaignId, Type, BidModifier} with no Id.
    • API: error_code=8000, error_detail=The required field Id is omitted in an item in the BidModifiers array — the set method UPDATES existing modifiers only.
    • Cassette: tests/cassettes/test_integration_write/TestWriteBidModifiersSet.test_set_without_id_is_rejected.yaml (frozen as a regression anchor by PR test: VCR cassette replay + regression guards for integration_write #17)
    • Fix hint: add a bidmodifiers add subcommand backed by the API's add method (structured BidAdjustments payload per API docs). Keep set working with --id for updates.
    • Test to write: new TestWriteBidModifiersAdd.test_add_delete that creates a MOBILE_ADJUSTMENT modifier and deletes it. The current test_set_without_id_is_rejected stays as a regression anchor for the update path.

Test-fixture payload gaps (3)

  • 4. TestWriteDynamicAds.test_add_update_delete — payload missing Operand

    • File: tests/test_integration_write.py — the test passes --json '{"Name":"Test Webpage","Conditions":[{"Operator":"CONTAINS","Arguments":["test"]}]}'
    • API: required field Operand is omitted in an item in the Conditions array
    • Cassette: tests/cassettes/test_integration_write/TestWriteDynamicAds.test_add_update_delete.yaml
    • Fix hint: set Operand explicitly, e.g. "Operand": "URL" with "Operator": "CONTAINS" and "Arguments": ["test"]. dynamicads add is a pass-through for user JSON — the CLI isn't wrong, my test payload was incomplete.
    • Effect: flips the test from skip → pass, real lifecycle assertion on dynamicads add → update → delete.
  • 5. TestWriteSmartAdTargets.test_add_update_delete — payload missing Name

    • File: tests/test_integration_write.py — the test passes --json '{"Subtype":"UNIQUE","Priority":3}'
    • API: required field Name is omitted
    • Cassette: tests/cassettes/test_integration_write/TestWriteSmartAdTargets.test_add_update_delete.yaml
    • Fix hint: add "Name": "test-smart-target" to the payload (or whatever the spec calls for — verify field name via Context7 docs).
    • Effect: flips skip → pass. Critical — this test is the live-API regression guard for the Type-field fix from PR fix+test: stop sending implicit Type field in add commands; add dry-run coverage #12; currently it skips, meaning the fix is not actually regression-tested.
  • 6. TestWriteRetargeting.test_add_deleteRules[0] missing Arguments

    • File: tests/test_integration_write.py — the test passes --json '{"Rules":[{"LowerBound":1,"UpperBound":365}]}'
    • API: required field Arguments is omitted in an item
    • Cassette: tests/cassettes/test_integration_write/TestWriteRetargeting.test_add_delete.yaml
    • Fix hint: Rules items for AUDIENCE_SEGMENT retargeting lists need an Arguments array of goal/segment IDs. Verify structure via Context7.
    • Effect: flips skip → pass.

Done-when

  • All 6 checkboxes above closed.
  • pytest -m integration_write -v (replay) shows 6 additional passing tests — none of items 1–6 skip anymore.
  • Cassettes regenerated and audited (no token/login leaks).
  • For items 1–3, follow-up PR bumps the patch version.

Links

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingenhancementFeature addition

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions