fix(conformance): send empty path labels like omitted ones; route53/lambda to 100%

Merged
VI
4h ago
+1031-88

Summary

Part of restoring true 100% Smithy conformance (main's CI probe is at 256,922/257,391; the baseline check only fails a service that drops >100 below its recorded count, so these were hidden).

Probe: empty path labels. For negative_too_short_<label> with min=1, the probe sent an empty label (e.g. GET /2015-03-31/functions/, GET /2013-04-01/queryloggingconfig/). No SDK can send that (smithy-rs refuses empty labels), and the URL is byte-identical to the List op with a trailing slash, which AWS serves as a list (#1645). An empty label is now sent like an omitted one: the {Name} placeholder for model-built paths, and a pattern-invalid sentinel for Lambda's legacy path (a valid-looking sentinel creates layers / is an idempotent delete). S3 keeps sending its empty Key (a sentinel key would be a valid object key). The variant still sends a real request and is judged on the response.

Before/after on all 43 REST probe services (main binaries vs branch binaries, fresh server each): no service lost a passing variant and no service's total changed.

servicemainbranch
bedrock4029/40444044/4044
cloudfront4110/41124112/4112
lambda3941/39493949/3949
route532381/23942394/2394
other 39unchanged

Route53. The router dropped every empty segment, so a request with an empty id matched no route and returned an undeclared 404 InvalidArgument (9 ops). It now routes on the raw path: keep interior empty segments and trim a trailing slash; then keep the trailing empty segment; then fall back to the old behaviour. GET /hostedzone/ still lists; DELETE /hostedzone/ returns NoSuchHostedZone; /hostedzone//rrset no longer routes to GetHostedZone("rrset").

Lambda. LayerName is validated (140 chars, the model's pattern, bare name or layer ARN) on all seven layer ops, and CallbackId against its base64 pattern on the three durable-execution callback ops, returning ValidationException with the AWS "1 validation error detected" wording (added to Lambda's service_common_errors, as these are API-level constraint errors the model does not declare per op).

Conformance seed. The GetFunctionConcurrency documented example assumes 250 reserved concurrency (and the DeleteFunctionConcurrency example clears it), so the probe seeds it only around that op and restores the previous value through a drop guard.

Cross-account Lambda layer access via layer ARNs is handled separately. The baseline is re-recorded for every service at once in a final PR.

Test plan

  • Unit: route53 router (3), layer name + callback id validation, probe empty-label handling (incl. S3 unchanged), seed guard (3, incl. restore on panic).
  • E2E: empty_id_label_reaches_its_operation (route53, raw HTTP), malformed_layer_name_is_rejected_by_every_layer_op, durable_execution_callback_id_must_match_its_pattern; route53, lambda_layers, lambda_regional, lambda_persistence, host_routing and CloudFormation lambda/route53 suites pass locally. Docker-dependent Lambda invoke tests ran in CI only (no Docker locally).
  • Conformance tests: route53 (5 files), lambda.
  • Probe table above.

Summary by cubic

Brings route53 and lambda Smithy conformance to 100% by fixing how empty path labels are probed and routed, and adding Lambda input validation that was missing. No other service lost a passing variant.

  • Empty @httpLabel values are now sent like omitted ones: no SDK can serialize an empty label, and the URL collapses into the List operation's trailing-slash form. S3 still sends its empty Key.
  • Route 53 routes on the raw wire path, keeping interior empty segments, so an empty ID reaches its operation instead of returning an undeclared 404 InvalidArgument; GET /hostedzone/ still lists.
  • LayerName (bare name or layer ARN, max 140 chars) and CallbackId (base64 pattern) are validated across all layer and durable-execution callback operations, returning ValidationException with AWS's "1 validation error detected" wording. Layer ARNs minted for fakecloud's own region and account are exempt from the ARN pattern, since --region local or a non-AWS --account-id wouldn't match AWS's shape.
  • The probe seeds my-function's reserved concurrency to 250 only around GetFunctionConcurrency, whose documented example assumes it, and restores the previous value with a drop guard that survives panics.