Skip to content

fix: resolve compatibility test failures#4

Merged
skyoo2003 merged 8 commits intomainfrom
fix/compatibility-server-startup
Apr 19, 2026
Merged

fix: resolve compatibility test failures#4
skyoo2003 merged 8 commits intomainfrom
fix/compatibility-server-startup

Conversation

@skyoo2003
Copy link
Copy Markdown
Owner

Summary

  • Fix all 28 failing compatibility tests by addressing server-side response format mismatches, REST-JSON routing issues, and test parameter compatibility with current botocore versions
  • Add _build_devcloud_cmd and _start_server_error helpers with full test coverage
  • Use DEVNULL for long-running server process to avoid pipe blocking; only spawn PIPE subprocess on failure for stderr capture
  • Add pyproject.toml with pythonpath for proper module resolution

Server-side fixes

  • bedrock: Return tags as [{key, value}] list instead of map in listTagsForResource
  • s3tables: Strip /v1 prefix in resolveOp to match boto3's URL paths
  • scheduler: Disambiguate create vs update (both POST) by checking if schedule exists; read GroupName from body params in getSchedule
  • serverlessrepo: Use strParam helper for camelCase/PascalCase param compatibility; emit both cases in CloudFormation response keys; read organizationId in unshareApplication
  • textract: Convert Tags to [{Key, Value}] list in adapterToMap

Test fixes

  • route53: Handle botocore version differences for TPI/CIDR/RDS params
  • support: Add required issueType/serviceCode/categoryCode params
  • textract: Use 3-char minimum S3 bucket name

Test plan

  • All Go unit tests pass (go test ./...)
  • Local conftest helper tests pass
  • CI compatibility test suite: all 699+ tests should pass

@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented Apr 19, 2026

Reviewer's Guide

Adjusts multiple AWS service emulations and compatibility tests to match current botocore/boto3 expectations, including S3Tables REST-JSON routing and ARN handling, Route53 XML response shapes and request parameters, Scheduler REST-JSON behavior, ServerlessRepo casing/parameter compatibility, Bedrock/Textract/CloudWatch/EventBridge behavior tweaks, and test harness improvements (dynamic ports, startup diagnostics) so that all compatibility tests pass.

File-Level Changes

Change Details Files
Rework S3Tables REST-JSON routing, path parsing, and store lookups to support ARN-based URLs and table operations that match boto3’s S3Tables client behavior.
  • Replace pathParam-based extraction in S3Tables handlers with s3PathParts and query-string based lookup where required (e.g., getTable, listTables).
  • Simplify resolveOp: strip /v1 prefix, collapse ARN segments, and map top-level buckets/namespaces/tables/get-table routes directly to S3Tables operations.
  • Introduce s3PathParts and pathSegment helpers plus a revised collapseARN to robustly rejoin ARN segments and derive bucket ARN, namespace, table name, and subresources from boto3-style URLs.
  • Update S3Tables provider tests to exercise the new REST paths (e.g., POST /buckets, /namespaces/{bucketARN}/{ns}, /tables/{bucketARN}/{ns}, /get-table with query params, maintenance-job-status, etc.).
  • Teach the S3Tables store GetBucket and DeleteBucket methods to accept either a bucket name or ARN by switching SQL queries based on the presence of "arn:" in the input.
internal/services/s3tables/provider.go
internal/services/s3tables/provider_test.go
internal/services/s3tables/store.go
tests/compatibility/test_s3tables.py
Align Route53 XML response shapes and Route53 compatibility tests with current models for DNSSEC, traffic policy instances, CIDR collections, and reusable delegation sets, including botocore version differences.
  • Change ActivateKeySigningKey and DeactivateKeySigningKey handlers to return ChangeInfo blocks (Id/Status/SubmittedAt) instead of KeySigningKey structures.
  • Update CIDR collection handlers to use Collection/Id/Name/Version XML structure and adjust listCidrCollections/ListCidrLocations response element names and nesting.
  • Refactor reusable delegation set handlers to use Id/CallerReference/NameServers fields and adjust list response root and MaxItems type.
  • Extend Route53 provider tests to assert on new XML shapes for CIDR collections and delegation sets, matching the updated handlers.
  • Add helper functions in Route53 compatibility tests to build Create/UpdateTrafficPolicyInstance kwargs based on the installed botocore model, and relax/assert fields to support schema differences.
  • Adjust Route53 compatibility tests for DNSSEC, KSK lifecycle, CIDR collection APIs, and reusable delegation sets to use the new request/response field names and required arguments.
internal/services/route53/provider.go
internal/services/route53/provider_test.go
tests/compatibility/test_route53.py
Improve Scheduler service REST-JSON compatibility by disambiguating CreateSchedule vs UpdateSchedule and handling GroupName across body and query params.
  • In the scheduler request dispatcher, when resolveOp returns CreateSchedule, detect existing schedules and dynamically route the request to UpdateSchedule instead.
  • Extend getSchedule to accept GroupName from body params, camelCase and PascalCase query parameters (GroupName/groupName), falling back to path-derived group where absent.
  • Update deleteSchedule to similarly honor GroupName/groupName query parameters before using the path-derived group name.
internal/services/scheduler/provider.go
Enhance ServerlessRepo compatibility with mixed-case parameters and CloudFormation-related responses.
  • Use a strParam helper to read Description/Author/HomePageUrl/ReadmeUrl in both PascalCase and camelCase when updating applications.
  • Emit both camelCase and PascalCase keys in listApplications, CloudFormation change set/template create, and get template responses (e.g., ApplicationId/applicationId, TemplateId/templateId, SemanticVersion/semanticVersion).
  • Update unshareApplication to read OrganizationId/Principal in both PascalCase and camelCase via strParam.
  • Return both lower- and upper-case keys for dependencies and nextToken in listApplicationDependencies for client compatibility.
internal/services/serverlessrepo/provider.go
Adjust miscellaneous service behaviors (Bedrock, Textract, CloudWatch, EventBridge) and related tests to align with AWS semantics and avoid nil/shape mismatches.
  • Change Bedrock listTagsForResource to return tags as a list of {key,value} objects instead of a map and update the corresponding provider test to validate the new shape.
  • Ensure Textract listTagsForResource never returns a nil Tags map, and adapt the Textract compatibility tests to construct resource ARNs and use a valid 3-character S3 bucket name for lending analysis.
  • Include Dimensions and conditionally include Configuration in CloudWatch DescribeAnomalyDetectors responses only when set, matching expectations about optional fields.
  • Update EventBridge rule matching to support both lowercase "source" and TitleCase "Source" fields in events.
  • Adjust Support compatibility tests to pass required issueType/serviceCode/categoryCode parameters when describing supported languages.
internal/services/bedrock/provider.go
internal/services/bedrock/provider_test.go
internal/services/textract/provider.go
tests/compatibility/test_textract.py
internal/services/cloudwatch/provider.go
internal/services/eventbridge/store.go
tests/compatibility/test_support.py
Improve test harness and configuration for the devcloud server to be robust and configurable via environment (especially port and startup error handling).
  • Update compatibility test conftest to choose a free port when DEVCLOUD_PORT is unset, propagate that port to the server environment, and expose DEVCLOUD_URL accordingly.
  • Introduce _start_server_error helper that spins up a short-lived debug server instance with PIPEd stdout/stderr to capture startup errors and raises a detailed RuntimeError if the server fails to become healthy within a shortened debug timeout.
  • Set DEVCLOUD_PORT from the environment in the Go config layer (applyEnvOverrides) when provided, overriding the config file value.
  • Use DEVNULL for the long-running server process in tests to prevent pipe blocking, only using PIPE in the debug helper when startup fails.
tests/compatibility/conftest.py
internal/config/config.go

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

@github-actions github-actions Bot added tests Test code and test infrastructure services AWS service implementations bug Something isn't working labels Apr 19, 2026
@skyoo2003 skyoo2003 self-assigned this Apr 19, 2026
Copy link
Copy Markdown
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 found 1 issue, and left some high level feedback:

  • In _start_server_error, the error message claims the server did not start within 30s while _wait_for_server is called with timeout=5; consider wiring the actual timeout value into the message to avoid confusion during debugging.
  • The try/except TypeError fallbacks in test_traffic_policy_instance make it harder to notice unexpected TypeErrors from other sources; checking the botocore version or operation model up front and selecting a single call signature would be more robust.
  • In the Route 53 CIDR collection and reusable delegation set tests, you removed assertions on Name/State without replacing them; consider asserting on other stable fields (e.g., IDs or collection contents) so the tests still validate the created resources' shape rather than just that the calls succeed.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `_start_server_error`, the error message claims the server did not start within 30s while `_wait_for_server` is called with `timeout=5`; consider wiring the actual timeout value into the message to avoid confusion during debugging.
- The `try/except TypeError` fallbacks in `test_traffic_policy_instance` make it harder to notice unexpected `TypeError`s from other sources; checking the botocore version or operation model up front and selecting a single call signature would be more robust.
- In the Route 53 CIDR collection and reusable delegation set tests, you removed assertions on `Name`/`State` without replacing them; consider asserting on other stable fields (e.g., IDs or collection contents) so the tests still validate the created resources' shape rather than just that the calls succeed.

## Individual Comments

### Comment 1
<location path="internal/services/serverlessrepo/provider.go" line_range="511-514" />
<code_context>
 		return shared.JSONError("BadRequestException", "ApplicationId is required", http.StatusBadRequest), nil
 	}
-	principal, _ := params["OrganizationId"].(string)
+	principal := strParam(params, "OrganizationId", "organizationId")
 	if principal == "" {
 		principal, _ = params["Principal"].(string)
 	}
</code_context>
<issue_to_address>
**suggestion:** Use the same helper when falling back to the Principal parameter for consistency and camelCase support.

The fallback still uses a direct type assertion for `"Principal"`, so a REST-style `"principal"` value would be ignored. For consistency and camelCase support, use `strParam(params, "Principal", "principal")` in the fallback as well.

```suggestion
	principal := strParam(params, "OrganizationId", "organizationId")
	if principal == "" {
		principal = strParam(params, "Principal", "principal")
	}
```
</issue_to_address>

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.

Comment thread internal/services/serverlessrepo/provider.go
skyoo2003 and others added 6 commits April 19, 2026 21:32
The conftest unconditionally passed `-config devcloud.yaml`, but the file
doesn't exist in the repo. When `-config` is explicit, main.go uses
config.Load() which calls os.Exit(1) on missing file. Since stderr was
DEVNULL, the error was invisible and _wait_for_server timed out at 30s.

- Only pass `-config devcloud.yaml` when the file exists (fallback to embedded defaults)
- Capture stderr and include it in the error message for debugging
Address code review comments: use DEVNULL for the long-running server
process to avoid blocking, only spawn a short-lived PIPE subprocess on
failure. Extract _build_devcloud_cmd and _start_server_error helpers
with full test coverage.
Server-side fixes:
- bedrock: return tags as [{key,value}] list instead of map
- s3tables: strip /v1 prefix in resolveOp for boto3 path routing
- scheduler: disambiguate create/update POST by checking if schedule exists
- scheduler: read GroupName from body params in getSchedule
- serverlessrepo: use strParam for camelCase/PascalCase param compat
- serverlessrepo: emit both cases in CloudFormation response keys
- serverlessrepo: read organizationId in unshareApplication
- textract: convert Tags to [{Key,Value}] list in adapterToMap

Test fixes:
- route53: handle botocore version differences for TPI/CIDR/RDS params
- support: add required issueType/serviceCode/categoryCode params
- textract: use 3-char minimum S3 bucket name
- Wire actual debug timeout value into _start_server_error message
- Replace try/except TypeError with botocore model introspection in route53 tests
- Add DelegationSetId assertion for reusable delegation set test
- Use strParam for Principal fallback in unshareApplication
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
Fix S3Tables resolveOp routing (add CreateNamespace at n=3,
PutTableEncryption at n=5), fix unit test URL patterns to match
actual routing. All 704 compat tests and all Go unit tests pass.
@skyoo2003 skyoo2003 force-pushed the fix/compatibility-server-startup branch from de2c0bb to 40a5697 Compare April 19, 2026 12:34
@skyoo2003
Copy link
Copy Markdown
Owner Author

@sourcery-ai review

Copy link
Copy Markdown
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 found 4 issues, and left some high level feedback:

  • The new S3Tables routing logic now duplicates URL parsing in both resolveOp and s3PathParts (including /v1 stripping and ARN collapsing); consider centralizing this into a single helper to reduce the risk of them diverging on future changes.
  • In tests/compatibility/conftest.py, _start_server_error reports a 30s startup timeout but the secondary debug wait is hard-coded to 5s; aligning the timeout constants and message would make debugging clearer.
  • For the Route53 reusable delegation set and CIDR collection handlers, values like CallerReference and Version are currently hard-coded (e.g., "rds-ref-1", 1); if possible, deriving these from the incoming request would better reflect real API behavior and reduce surprises for clients that inspect them.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new S3Tables routing logic now duplicates URL parsing in both resolveOp and s3PathParts (including /v1 stripping and ARN collapsing); consider centralizing this into a single helper to reduce the risk of them diverging on future changes.
- In tests/compatibility/conftest.py, _start_server_error reports a 30s startup timeout but the secondary debug wait is hard-coded to 5s; aligning the timeout constants and message would make debugging clearer.
- For the Route53 reusable delegation set and CIDR collection handlers, values like CallerReference and Version are currently hard-coded (e.g., "rds-ref-1", 1); if possible, deriving these from the incoming request would better reflect real API behavior and reduce surprises for clients that inspect them.

## Individual Comments

### Comment 1
<location path="internal/services/s3tables/provider.go" line_range="800-801" />
<code_context>
+func collapseARN(parts []string, startIdx int) []string {
</code_context>
<issue_to_address>
**issue (bug_risk):** collapseARN now over-collapses and will swallow namespace/table/subresource segments into the ARN

The new implementation advances while the next segment is non-empty and lacks `:`, so for a path like `/buckets/arn:aws:s3tables:...:bucket/my-bucket/namespaces/ns/tables/foo`, it will walk through `my-bucket`, `namespaces`, `ns`, `tables`, and `foo`. `arnEnd` becomes `len(parts)`, so the ARN collapses into the entire tail of the path. Downstream, `resolveOp` and `s3PathParts` no longer see `namespaces`, `tables`, or the table name as separate segments, breaking routing and parameter extraction.

The older version avoided this by stopping at known subresource names. We should restore that behavior (e.g., stop at known subresources or at most join `startIdx` and `startIdx+1`), otherwise any ARN with trailing segments is likely misparsed.
</issue_to_address>

### Comment 2
<location path="internal/services/s3tables/provider.go" line_range="1017-1024" />
<code_context>
-func pathParam(path, key string) string {
+// s3PathParts parses a boto3-style S3Tables URL and returns the extracted
+// bucket ARN (or name), namespace, table name, and subresource.
+func s3PathParts(path string) (bucketARN, namespace, tableName, subresource string) {
+	path = strings.TrimPrefix(path, "/v1")
+	parts := strings.Split(strings.Trim(path, "/"), "/")
+	if len(parts) >= 2 && strings.Contains(parts[1], "arn:") {
+		parts = collapseARN(parts, 1)
+	}
+	// For table URLs, also collapse ARN at position 3
+	if len(parts) >= 4 && parts[0] == "tables" && strings.Contains(parts[3], "arn:") {
+		parts = collapseARN(parts, 3)
+	}
</code_context>
<issue_to_address>
**issue (bug_risk):** s3PathParts relies on collapseARN behavior that is no longer correct, compounding the parsing bug for table URLs

Because `s3PathParts` now calls `collapseARN(parts, 1)` (and `collapseARN(parts, 3)` for table URLs), the new `collapseARN` behavior will likely collapse too many segments for paths like `/tables/arn:...:bucket/my-bucket/ns/table/metadata-location`, shifting or emptying `namespace`, `tableName`, and `subresource`.

After fixing `collapseARN`, please ensure we have coverage for:
- `/namespaces/{bucketArn}` and `/namespaces/{bucketArn}/{ns}` → correct `bucketARN` and `namespace`
- `/tables/{bucketArn}/{ns}/{table}` and `/tables/{bucketArn}/{ns}/{table}/{subresource}` → all four return values populated correctly

As written, the interaction between `collapseARN` and `s3PathParts` will cause several callers (e.g. `createTable`, `deleteTable`, `getTableBucketPolicy`) to see incorrect names.
</issue_to_address>

### Comment 3
<location path="internal/services/scheduler/provider.go" line_range="59-65" />
<code_context>
 		op = resolveOp(req.Method, req.URL.Path)
 	}

+	// REST-JSON: both create and update use POST to /schedules/{name}.
+	// When resolveOp returns CreateSchedule, check if the schedule already
+	// exists and redirect to UpdateSchedule.
+	if op == "CreateSchedule" {
+		name := scheduleName(req.URL.Path)
+		groupName := scheduleGroup(req.URL.Path)
+		if gp, ok := params["GroupName"].(string); ok && gp != "" {
+			groupName = gp
+		}
</code_context>
<issue_to_address>
**suggestion (bug_risk):** CreateSchedule -> UpdateSchedule dispatch only checks `GroupName` and ignores `groupName` from REST-JSON bodies

The `HandleRequest` shortcut only reads `GroupName` from `params`, but REST-JSON bodies typically use `groupName`, as in your `getSchedule`/`deleteSchedule` handlers. If a client sends only `groupName` in the body, this code will still rely on the path-derived group name (which may be empty/defaulted), leading to incorrect existence checks.

Please update this to resolve both `GroupName` and `groupName` from `params` (e.g. via a shared helper/`strParam`-style function) before calling `GetSchedule`, to stay consistent with the other handlers.

Suggested implementation:

```golang
	// REST-JSON: both create and update use POST to /schedules/{name}.
	// When resolveOp returns CreateSchedule, check if the schedule already
	// exists and redirect to UpdateSchedule.
	if op == "CreateSchedule" {
		name := scheduleName(req.URL.Path)
		groupName := scheduleGroup(req.URL.Path)

		// Prefer explicit group name from request parameters if present.
		// Support both "GroupName" (camel-case) and "groupName" (lower camel),
		// as used by other handlers (e.g. getSchedule/deleteSchedule).
		if gp, ok := params["GroupName"].(string); !ok || gp == "" {
			if gpLower, okLower := params["groupName"].(string); okLower && gpLower != "" {
				gp = gpLower
			}
		}
		if gp != "" {
			groupName = gp
		}

		if _, err := p.store.GetSchedule(name, groupName); err == nil {
			op = "UpdateSchedule"
		}
	}

```

If your codebase already has a shared helper (e.g. a `strParam` function) used by `getSchedule`/`deleteSchedule` to resolve `"GroupName"`/`"groupName"`, you may prefer to:
1. Replace the inline resolution logic here with a call to that helper to keep behavior consistent.
2. Ensure the helper takes the existing path-derived `groupName` as a default/fallback value.
</issue_to_address>

### Comment 4
<location path="internal/services/bedrock/provider_test.go" line_range="187-193" />
<code_context>
 	rb2 := parseBody(t, resp2)
-	tags := rb2["tags"].(map[string]any)
-	assert.Equal(t, "test", tags["env"])
+	tagList := rb2["tags"].([]any)
+	tagMap := map[string]string{}
+	for _, t := range tagList {
+		tm := t.(map[string]any)
+		tagMap[tm["key"].(string)] = tm["value"].(string)
+	}
+	assert.Equal(t, "test", tagMap["env"])

 	// Untag
</code_context>
<issue_to_address>
**suggestion (testing):** Add a test case for listing tags when no tags are present

Since this test already covers the non-empty `[{"key","value"}]` shape, please also add a case where `ListTagsForResource` is called on a resource with no tags and assert that `tags` is an empty list. That will lock in the expected empty-state behavior for callers.
</issue_to_address>

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.

Comment thread internal/services/s3tables/provider.go Outdated
Comment thread internal/services/s3tables/provider.go Outdated
Comment thread internal/services/scheduler/provider.go
Comment thread internal/services/bedrock/provider_test.go
- Centralize URL parsing into parseS3TablesPath, replacing fragile
  collapseARN that could over-collapse segments (Comments 1 & 2)
- Fix scheduler CreateSchedule dispatch to check both GroupName and
  groupName from REST-JSON body (Comment 3)
- Add empty-tags assertion to bedrock TestTagging (Comment 4)
- Align _start_server_error timeout message with actual value
  (Overall Comment 2)
@skyoo2003
Copy link
Copy Markdown
Owner Author

@sourcery-ai resolve

@skyoo2003 skyoo2003 merged commit 0e43e5b into main Apr 19, 2026
10 checks passed
@skyoo2003 skyoo2003 deleted the fix/compatibility-server-startup branch April 19, 2026 12:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working services AWS service implementations tests Test code and test infrastructure

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant