Hive Hive
Sign in

fix(server): return a suite shard plan instead of a 500 when the client omits enumerated suites

GitHub issue · Closed

Metadata
Source
tuist/tuist #11615
Updated
Jul 5, 2026
Domains
Testing
Details

What changed

Tuist.Shards.resolve_units/4 for suite granularity now pattern-matches a non-empty list ([_ | _] = suites) before using client-supplied test_suites, so a present-but-nil value falls through to server-side history derivation instead of being passed downstream as the unit list. modules gets the same nil-coalescing treatment via a shared params_modules/1 helper, so the "module" granularity path can’t hit the same crash.

Follow-up to #11581.

Why — the 500

After #11581, the CLI no longer boots every test bundle to enumerate suites. A suite-granularity request therefore arrives with modules populated but no test_suites. The controller always builds params via:

modules: Map.get(body_params, :modules),
test_suites: Map.get(body_params, :test_suites),

so the key is present with a nil value, not omitted.

The old code read it with Map.get(params, :test_suites, []). The [] default only applies to a missing key — a present-but-nil value returns nil. The guard suites when suites != [] then matched (nil != [] is true), and nil flowed downstream as the unit list. The first Enum call over it crashed, surfacing to the client as:

Failed to create shard plan due to an unknown server response of 500.

This is exactly what Simon hit after #11581 merged.

Root cause

Map.get(map, key, default) returns default only when the key is absent. Once the controller normalized every optional field to Map.get(body_params, field) (present key, nil value), the [] defaults inside resolve_units/4 were dead, and a nil leaked into enumeration.

Why this solution over the obvious alternative

Defaulting in the controller (Map.get(body_params, :test_suites) || []) would fix this one call site but leave resolve_units/4 fragile for any other caller. Guarding on the shape actually required — a non-empty list — makes the domain function correct regardless of whether callers pass nil, [], or an omitted key, and routes the empty/nil case to the intended history-derived path.

Validation

  • Added Tuist.ShardsTest case: suite granularity with test_suites: nil derives suites from history and produces the expected plan.
  • Added TuistWeb.API.ShardsControllerTest case: posting a suite-granularity plan with modules and no test_suites returns 200 with a valid shard_count / shards.
  • Local full-suite run was wedged on compile; relying on CI to validate.

Note

mix.lock carries a transitive cowboy 2.16.1 → 2.17.0 / cowlib 2.17.1 → 2.18.0 bump. The resolver pulls it in and flags the older cowlib as vulnerable, so it rides along here rather than being reverted.

🤖 Generated with Claude Code

Flights

Investigate, reproduce, or fix this item in an isolated repository. Each Flight preserves its outcome and agent session.

New Flights are paused Configure model inference, GitHub, and a sandbox provider to start another Flight. Existing results remain available below.
No Flights yet

Start a Flight and preserve its objective, outcome, and session here.

Comments

No GitHub comments yet.