Skip to content

fix(run_tests): support snake_case params and handle double-serialized arrays#690

Open
bruno1308 wants to merge 2 commits intoCoplayDev:betafrom
bruno1308:fix/run-tests-filter-snake-case
Open

fix(run_tests): support snake_case params and handle double-serialized arrays#690
bruno1308 wants to merge 2 commits intoCoplayDev:betafrom
bruno1308:fix/run-tests-filter-snake-case

Conversation

@bruno1308
Copy link

@bruno1308 bruno1308 commented Feb 6, 2026

Summary

  • snake_case parameter support: GetFilterOptions() in RunTests.cs only checked camelCase keys (testNames, groupNames, etc.) but the MCP tool schema defines them in snake_case (test_names, group_names, etc.). Added fallback lookups so both naming conventions work.
  • Double-serialized array handling: Some MCP clients (e.g. Claude Code) serialize array parameters as a stringified JSON array inside an outer array (["[\"name1\"]"]). Added detection and unwrapping for this pattern in ParseStringArray().
  • Same snake_case fix in GetTestJob.cs: include_details and include_failed_tests parameters also only checked camelCase.

Root Cause

When an MCP client sends test_names: ["MyTest"], the C# handler looked up @params["testNames"] which returned null, so no filter was applied. The double-serialization issue compounded this — even if the key matched, the array value ["[\"MyTest\"]"] would extract the stringified JSON as a literal test name, which never matched any test.

Testing

Verified with Claude Code MCP client:

  • Before: run_tests(test_names=["SomeTest"]) always ran 0 tests
  • After: correctly filters to only the specified tests

Fixes #689

🤖 Generated with Claude Code

Summary by Sourcery

Improve MCP test commands parameter handling for better compatibility with different clients.

Bug Fixes:

  • Allow run_tests and get_test_job commands to read both camelCase and snake_case parameter names for filter and include options.
  • Correctly parse array parameters that may be provided as stringified JSON arrays, double-serialized arrays, or nested arrays so test filters apply as intended.

Summary by CodeRabbit

  • Improvements
    • Parameters now accept both camelCase and snake_case for greater flexibility.
    • Boolean options (e.g., includeDetails, includeFailedTests) reliably default to false when omitted.
    • Test filter inputs (test names, groups, categories, assemblies) now accept JSON-style arrays and varied string formats for more robust parsing.

…d arrays

The MCP tool schema defines filter parameters in snake_case (test_names,
group_names, etc.) but GetFilterOptions() only checked camelCase keys,
causing test filtering to silently fail — all tests would run instead of
the filtered subset.

Additionally, some MCP clients serialize array parameters as a stringified
JSON array inside an outer array (e.g. ["[\"name1\"]"]), which was not
handled by ParseStringArray().

Changes:
- Add snake_case fallback to all ParseStringArray calls in RunTests.cs
- Handle stringified JSON arrays in String token branch
- Handle double-serialized arrays in Array token branch
- Handle nested arrays ([[name1, name2]]) in Array token branch
- Add snake_case fallback for include_details/include_failed_tests in
  both RunTests.cs and GetTestJob.cs

Fixes CoplayDev#689

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Feb 6, 2026

Reviewer's Guide

Adds snake_case parameter support and more robust array parsing for MCP test commands, ensuring both naming conventions and double-serialized arrays are correctly handled in RunTests and GetTestJob.

Sequence diagram for run_tests parameter parsing and filter application

sequenceDiagram
    actor Dev
    participant MCPClient
    participant MCPBridge
    participant RunTests
    participant ParamCoercion
    participant TestJobManager

    Dev->>MCPClient: Invoke run_tests(test_names=["SomeTest"], include_details=true)
    MCPClient->>MCPBridge: JSON RPC with snake_case params
    MCPBridge->>RunTests: HandleCommand(@params)

    activate RunTests
    RunTests->>ParamCoercion: CoerceBool(@params[includeDetails] ?? @params[include_details], false)
    ParamCoercion-->>RunTests: includeDetails
    RunTests->>ParamCoercion: CoerceBool(@params[includeFailedTests] ?? @params[include_failed_tests], false)
    ParamCoercion-->>RunTests: includeFailedTests

    RunTests->>RunTests: GetFilterOptions(@params)
    activate RunTests
    RunTests->>RunTests: ParseStringArray("testNames", "test_names")
    Note over RunTests: token = @params["testNames"] ?? @params["test_names"]
    RunTests-->>RunTests: Detect stringified or double-serialized array
    RunTests-->>RunTests: Normalize to string[] testNames
    RunTests-->>RunTests: Build TestFilterOptions
    deactivate RunTests

    RunTests->>TestJobManager: StartJob(mode, filterOptions)
    TestJobManager-->>RunTests: jobId
    deactivate RunTests

    RunTests-->>MCPBridge: Success response with jobId
    MCPBridge-->>MCPClient: Response
    MCPClient-->>Dev: Shows filtered test run started
Loading

Updated class diagram for RunTests and GetTestJob parameter handling

classDiagram
    class RunTests {
        +static Task~object~ HandleCommand(JObject params)
        -static TestFilterOptions GetFilterOptions(JObject params)
        -static string[] ParseStringArray(string camelKey, string snakeKey)
    }

    class GetTestJob {
        +static object HandleCommand(JObject params)
    }

    class TestFilterOptions {
        +string[] testNames
        +string[] groupNames
        +string[] categoryNames
        +string[] assemblyNames
    }

    class ParamCoercion {
        +static bool CoerceBool(JToken token, bool defaultValue)
    }

    class TestJobManager {
        +static string StartJob(string mode, TestFilterOptions filterOptions)
        +static TestJob GetJob(string jobId)
    }

    class TestJob {
        +string jobId
        +string status
        +bool includeDetails
        +bool includeFailedTests
    }

    RunTests ..> ParamCoercion : uses
    RunTests ..> TestFilterOptions : creates
    RunTests ..> TestJobManager : starts_job
    GetTestJob ..> ParamCoercion : uses
    GetTestJob ..> TestJobManager : retrieves_job
    TestJobManager --> TestJob : manages
    RunTests --> TestFilterOptions : configures_filters
    GetTestJob --> TestJob : returns_job_state
Loading

File-Level Changes

Change Details Files
Support both camelCase and snake_case boolean parameters for test detail flags.
  • Updated includeDetails parameter lookup to fall back to include_details when camelCase is missing.
  • Updated includeFailedTests parameter lookup to fall back to include_failed_tests when camelCase is missing.
  • Applied the same camelCase + snake_case lookup pattern in both RunTests and GetTestJob handlers.
MCPForUnity/Editor/Tools/RunTests.cs
MCPForUnity/Editor/Tools/GetTestJob.cs
Enhance string-array parameter parsing to handle multiple JSON-encoding edge cases.
  • Modified ParseStringArray to accept both camelCase and snake_case keys via dual-parameter lookup.
  • When given a string token, detect and parse stringified JSON arrays (e.g. "["name1", "name2"]") into a string array, ignoring empty/whitespace entries.
  • When given an array token, detect and unwrap double-serialized arrays such as ["["name1"]"] by JSON-parsing the inner string when it appears to be a JSON array.
  • When given an array token, detect and unwrap single nested arrays like [["name1", "name2"]] so the inner array is flattened for value extraction.
  • Preserve existing behavior of filtering out null/whitespace values and returning null when no valid entries remain.
  • Wire updated ParseStringArray into filter option extraction for testNames, groupNames, categoryNames, and assemblyNames, with snake_case fallbacks for each.
MCPForUnity/Editor/Tools/RunTests.cs

Assessment against linked issues

Issue Objective Addressed Explanation
#689 In RunTests.GetFilterOptions, accept snake_case parameter names (test_names, group_names, category_names, assembly_names) in addition to camelCase so test filtering works for MCP clients.
#689 In RunTests.ParseStringArray, correctly handle double-serialized arrays where the outer value is an array containing a single stringified JSON array (e.g. ["["MyTest"]"]).
#689 In RunTests.HandleCommand and GetTestJob.HandleCommand, accept snake_case boolean parameters include_details and include_failed_tests in addition to camelCase names.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 6, 2026

📝 Walkthrough

Walkthrough

Replace direct param access with ParamCoercion utilities: add GetParam and CoerceStringArray helpers, use them in RunTests and GetTestJob to accept camelCase and snake_case keys and to handle double-serialized/nested string-array formats.

Changes

Cohort / File(s) Summary
ParamCoercion helpers
MCPForUnity/Editor/Helpers/ParamCoercion.cs
Added GetParam(JObject, string camelKey, string snakeKey = null) and CoerceStringArray(JToken) plus necessary usings. Note: file contains duplicate declarations of these methods in the patch — review for duplicate-symbol/compile issues.
RunTests parameter parsing & filters
MCPForUnity/Editor/Tools/RunTests.cs
Replaced bespoke ParseStringArray and direct token reads with ParamCoercion.GetParam + ParamCoercion.CoerceStringArray; added snake_case fallback for filter keys and handling for double-serialized/nested string arrays.
GetTestJob boolean flags
MCPForUnity/Editor/Tools/GetTestJob.cs
Replaced direct bool extraction with ParamCoercion.GetParam(...)/CoerceBool(...) to accept both includeDetails/include_details and includeFailedTests/include_failed_tests.

Sequence Diagram(s)

(omitted — changes are parsing-focused and do not introduce a multi-component sequential flow requiring a diagram)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Possibly related PRs

Suggested reviewers

  • msanatan

Poem

🐰 I nibbled code beneath the moonlight,
Keys of snake and camel now both take flight.
Nested strings unwrapped, arrays set free,
Filters hop true — hooray, that's me! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed Title accurately summarizes the main changes: snake_case parameter support and double-serialized array handling in run_tests, which are the core objectives.
Linked Issues check ✅ Passed All objectives from issue #689 are met: snake_case fallbacks added [689], double-serialized array detection implemented [689], GetTestJob.cs updated [689], and correct filtering verified [689].
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing #689: ParamCoercion utilities support parameter handling, RunTests.cs and GetTestJob.cs apply these utilities as specified, with no extraneous modifications.
Description check ✅ Passed PR description is comprehensive, covering root cause analysis, specific fixes across three files, testing verification with the Claude Code client, and a clear reference to the resolved issue.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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 and usage tips.

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've left some high level feedback:

  • The snake_case / camelCase fallback pattern (@params?["includeDetails"] ?? @params?["include_details"] and in ParseStringArray) is repeated in a few places; consider extracting a small helper for parameter lookup to keep this logic centralized and reduce the chance of future inconsistencies.
  • In ParseStringArray, the broad catch { /* not a valid JSON array, treat as plain string */ } around JArray.Parse silently swallows all exceptions; consider narrowing this to JSON-specific exceptions or at least logging in debug builds so unexpected issues with parameter formats are easier to diagnose.
  • The nested-array handling ([[name1, name2]]) in ParseStringArray currently collapses only a single-element outer array; if multi-level or mixed nesting is a realistic input, you may want to generalize this unwrapping to avoid surprising behavior for inputs like [["a"], ["b"]].
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The snake_case / camelCase fallback pattern (`@params?["includeDetails"] ?? @params?["include_details"]` and in `ParseStringArray`) is repeated in a few places; consider extracting a small helper for parameter lookup to keep this logic centralized and reduce the chance of future inconsistencies.
- In `ParseStringArray`, the broad `catch { /* not a valid JSON array, treat as plain string */ }` around `JArray.Parse` silently swallows all exceptions; consider narrowing this to JSON-specific exceptions or at least logging in debug builds so unexpected issues with parameter formats are easier to diagnose.
- The nested-array handling (`[[name1, name2]]`) in `ParseStringArray` currently collapses only a single-element outer array; if multi-level or mixed nesting is a realistic input, you may want to generalize this unwrapping to avoid surprising behavior for inputs like `[["a"], ["b"]]`.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Address code review feedback:
- Extract snake_case/camelCase param lookup into ParamCoercion.GetParam()
- Extract string array coercion (with double-serialization handling) into
  ParamCoercion.CoerceStringArray()
- Narrow catch clauses from bare catch to JsonException
- Add explicit comment clarifying nested array unwrapping limitation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@bgr
Copy link

bgr commented Feb 6, 2026

I've applied the changed files in this PR (as of 854b421) straight into my Library\PackageCache\com.coplaydev.unity-mcp@418cf0dcc873\Editor (v9.4.1-beta.1) and can confirm that Claude is happily using test filtering now.

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.

run_tests test filtering broken: snake_case params ignored + double-serialized arrays

2 participants