Skip to content

Conversation

@vertex451
Copy link

@vertex451 vertex451 commented Dec 1, 2025

Parity Testing

./testdata/sdl/ contains test scenarios. Each directory has:

  • input.yaml — SDL input
  • manifest.json — Generated manifest (from Go parser)
  • groups.json — Generated deployment groups (from Go parser)

Tests validate both parsers(Golang and TS) produce identical output from the same input.

Schema Validation

Three schemas validate the SDL pipeline:

  • sdl-input.schema.yaml — Validates input YAML (required fields, formats, enums, patterns)
  • manifest-output.schema.yaml — Validates manifest JSON output
  • groups-output.schema.yaml — Validates groups JSON output

Test Fixtures

  • testdata/sdl/v2.0/, v2.1/ — Valid fixtures for parity tests
  • testdata/sdl/invalid/ — Both schema and parsers reject
  • testdata/sdl/schema-only-invalid/ — Schema rejects, Go accepts (stricter schema rules)

IDE validation

IDE supports YAML validation against the schema when creating manifests in the editor.

Screenshot 2025-12-02 at 16 19 34 Screenshot 2025-12-02 at 16 19 42

Important note:

  1. I generated input and output for tests using AI, let me know if it makes sense for you.
  2. Parity tests run 3 times in CI. The dedicated sdl-parity job is is redundant but it is visible in CI status.

🔧 Purpose of the Change

  • Other: [tests]

📌 Related Issues

✅ Checklist

  • I've updated relevant documentation
  • Code follows Akash Network's style guide
  • I've added/updated relevant unit tests
  • Dependencies have been properly updated
  • I agree and adhered to the Contribution Guidelines

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 1, 2025

Walkthrough

Add SDL parity infrastructure: embedded SDL input schema and validator, deterministic Go builders, a Go fixture generator, many new valid/invalid SDL fixtures, Go and TypeScript parity tests, TypeScript Go-format output support, output schemas/docs, Make targets, and a CI job to run SDL parity tests.

Changes

Cohort / File(s) Summary
CI & Build
\.github/workflows/tests.yaml, make/test.mk, \.gitignore
Add sdl-parity GitHub Actions job; new Make targets generate-sdl-fixtures and test-sdl-parity; ignore test/build and coverage artifacts.
Go: fixture generator
go/sdl/cmd/generate-sdl-fixtures/main.go
New CLI that reads input.yaml fixtures and writes manifest.json and groups.json.
Go: schema validation & SDL integration
go/sdl/schema_validator.go, go/sdl/sdl.go, go/sdl/sdl-input.schema.yaml
Embed/compile SDL input schema (gojsonschema) with thread-safe caching and logger hook; validate inputs and report schema vs runtime validation results from SDL Read.
Go: deterministic builders
go/sdl/groupBuilder_v2.go, go/sdl/groupBuilder_v2_1.go
Iterate storage keys in sorted order to produce deterministic StorageParams ordering.
Go: parity tests
go/sdl/parity_test.go
New test suite enumerating fixtures, validating inputs and generated manifest/groups against schemas, and comparing outputs to fixture expectations.
TypeScript: Go-format & validation
ts/src/sdl/SDL/SDL.ts, ts/src/sdl/types.ts, ts/package.json, ts/src/sdl/SDL/SDL.spec.ts
Add Go-format conversion, stricter runtime validation, rename quantity→size, ensure GPU/price formatting, update types/tests and add ajv dev dependency.
TS: parity tests
ts/src/sdl/SDL/parity.spec.ts
New Jest suite to load fixtures, validate schemas with AJV, produce Go-format outputs and compare to fixture outputs.
Schemas & docs
specs/sdl/manifest-output.schema.yaml, specs/sdl/groups-output.schema.yaml, specs/sdl/README.md
New manifest/groups output schemas and README documenting schema roles and limitations.
Fixtures — valid
testdata/sdl/v2.0/..., testdata/sdl/v2.1/...
Many new valid fixtures (input.yaml, manifest.json, groups.json) covering GPU, http options, endpoints, storage classes, persistent storage, multiple services, pricing, placement, port ranges, credentials, etc.
Fixtures — invalid / schema-only invalid
testdata/sdl/invalid/*.yaml, testdata/sdl/schema-only-invalid/*.yaml
Numerous invalid test cases exercising parsing/runtime/schema failures (missing fields, invalid ports, negative resources, credential errors, schema-only failures).
Go module & small edits
go/sdl/go.mod, go/cli/go.mod, go/provider/client/client.go, go/util/jwt/jwt_test.go
Add gojsonschema and related deps; minor receiver/signature cleanup; simplify jwt test loop.
TS snapshots & expectations
ts/test/fixtures/sdl/groups-basic-snapshot.json, ts/src/sdl/SDL/SDL.spec.ts
Update snapshots/tests to reflect size field, GPU units, signedBy nullability, and 18-decimal price formatting.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Make as Make / CI
participant Gen as generate-sdl-fixtures (Go CLI)
participant Fixtures as testdata/sdl
participant GoSDL as Go SDL reader
participant TSSDL as TS SDL parser
participant Schemas as JSON/YAML Schemas
participant Tests as parity tests (Go/TS)

Note over Make,Gen: CI invokes generation step
Make->>Gen: run generator over `testdata/sdl/*/input.yaml`
Gen->>Fixtures: write `manifest.json` and `groups.json`
Note over Fixtures,GoSDL: validators & comparisons
GoSDL->>Schemas: validate input against embedded schema
TSSDL->>Schemas: validate outputs with AJV
GoSDL->>Fixtures: produce manifest/groups (go format)
TSSDL->>Fixtures: produce manifest/groups (go format)
Tests->>GoSDL: run Go parity assertions vs fixtures
Tests->>TSSDL: run TS parity assertions vs fixtures
Tests->>Make: report results

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas needing extra attention:
    • go/sdl/schema_validator.go — embedding, lazy compilation, concurrency, logger injection, and error semantics.
    • ts/src/sdl/SDL/SDL.ts — convertToGoFormat, validation rules, and format-dependent output shapes.
    • go/sdl/parity_test.go and ts/src/sdl/SDL/parity.spec.ts — fixture discovery, schema compilation/validation, and equality semantics for JSON comparisons.
    • groupBuilder_v2*.go — deterministic ordering changes that affect generated fixtures and tests.
    • Large fixture set — ensure new schemas precisely match generated outputs (pricing precision, memory/storage field names, signedBy/null handling).

Possibly related issues

  • #1311 — Investigate possibility to compile GoLang SDL builder to webassembly — This PR implements shared fixtures, schemas and parity tests that serve the "shared tests/expectations" fallback described in the issue.

Possibly related PRs

  • chain-sdk#107 — Touches TypeScript manifest generation and params handling; closely related to TS output/format changes here.
  • chain-sdk#4 — Modifies go/sdl/sdl.go; related to schema-validation hooks added in this PR.
  • chain-sdk#34 — Adjusts Go module dependencies; related to go.mod dependency updates included here.

Suggested reviewers

  • stalniy
  • baktun14

Poem

"I hopped through fixtures late tonight,
I formatted groups and set their sight.
Go builds them true, TS checks the score,
Two languages dance and differ no more.
🥕🐇"

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.70% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'SDL parity tests' is concise and clearly describes the main purpose of the PR - adding parity tests for SDL validation.
Linked Issues check ✅ Passed The PR addresses issue #1311 by implementing parity tests as the fallback solution when Go-to-WebAssembly compilation is not feasible, ensuring both implementations remain synchronized.
Out of Scope Changes check ✅ Passed Changes are focused on adding SDL parity tests, test fixtures, schemas, and supporting code updates. Minor changes to method signatures and refactorings (like groupBuilder sorting) are peripheral improvements supporting the test infrastructure.
Description check ✅ Passed PR description follows most of the template but has some missing elements in the checklist confirmation.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch artem/sdl-parity-tests

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.

@vertex451 vertex451 changed the title Artem/sdl parity tests SDLparity tests Dec 1, 2025
@vertex451 vertex451 changed the title SDLparity tests SDL parity tests Dec 1, 2025
@vertex451 vertex451 force-pushed the artem/sdl-parity-tests branch from c464548 to c669081 Compare December 2, 2025 15:56
@vertex451 vertex451 marked this pull request as ready for review December 2, 2025 16:02
@vertex451 vertex451 requested a review from a team as a code owner December 2, 2025 16:02
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
go/sdl/go.mod (1)

7-15: gojsonschema dependency is used in production JWT schema validation

The gojsonschema dependency is imported in go/util/jwt/schema.go for production JWT schema validation (initialized at package level), in addition to its use in SDL parity tests. The dependency is appropriate for these use cases, though be aware it initializes at package load time and uses reflection, which incurs startup overhead.

ts/src/sdl/SDL/SDL.ts (1)

834-846: Essential change: Deterministic storage ordering for parity testing.

Sorting storage keys alphabetically before mapping ensures consistent, reproducible manifest output. This is critical for cross-language parity validation.

♻️ Duplicate comments (1)
go/sdl/groupBuilder_v2.go (1)

100-121: Sorted iteration over storage volumes for deterministic manifests

Using storageNames + sort.Strings before appending StorageParams eliminates Go map iteration nondeterminism and keeps v2 output stable across runs and in parity tests. Implementation mirrors the v2.1 path and looks correct.

🧹 Nitpick comments (11)
go/sdl/groupBuilder_v2_1.go (1)

100-131: Deterministic storage params ordering is correct

Sorting svc.Params.Storage keys before building params.Storage removes map‑iteration nondeterminism and aligns with parity/determinism needs. Logic is sound and nil‑safe; any future refactor could share this helper with the v2 builder, but it’s not required now.

make/test.mk (1)

51-63: Parity test workflow is solid; align TS path with existing TS_ROOT

The new generate-sdl-fixtures and test-sdl-parity targets wire the Go and TS parity tests together cleanly, and the sequencing (fixtures → Go tests → TS tests) makes sense.

For consistency with test-ts/test-coverage-ts, consider using $(TS_ROOT) instead of a hard-coded ts path:

-	@cd ts && npm test -- --testPathPattern=parity.spec.ts
+	@cd $(TS_ROOT) && npm test -- --testPathPattern=parity.spec.ts

This avoids future drift if the TS root directory is ever renamed.

go/sdl/cmd/generate-sdl-fixtures/main.go (1)

13-13: Consider making the fixtures root path configurable.

The hard-coded relative path "../../../testdata/sdl" assumes the command is executed from a specific directory (likely go/sdl/cmd/generate-sdl-fixtures). If the tool is run from a different location or installed elsewhere, it will fail.

Consider accepting the fixtures root as a command-line argument with a default value:

+import (
+	"flag"
+	// ... existing imports
+)
+
 func main() {
-	fixturesRoot := "../../../testdata/sdl"
+	fixturesRoot := flag.String("fixtures", "../../../testdata/sdl", "Path to SDL fixtures root directory")
+	flag.Parse()
+
 	versions := []string{"v2.0", "v2.1"}
 
 	for _, version := range versions {
-		versionDir := filepath.Join(fixturesRoot, version)
+		versionDir := filepath.Join(*fixturesRoot, version)
testdata/sdl/v2.0/persistent-storage/groups.json (1)

1-87: Add missing trailing newline.

The file is missing a trailing newline at EOF. While some build systems are forgiving, most linters and Git conventions expect JSON files to end with a newline character for POSIX compliance.

Add a newline after the closing bracket at line 87.

]
+
testdata/sdl/v2.0/simple/groups.json (1)

1-67: Add missing trailing newline.

Per the file structure, the JSON is valid but ends without a trailing newline. Add a newline after the closing bracket for POSIX compliance.

]
+
testdata/sdl/v2.0/storage-classes/groups.json (1)

1-87: Add missing trailing newline.

The file is missing a trailing newline at EOF, violating POSIX conventions and typical linter expectations.

]
+
specs/sdl/sdl-input.schema.yaml (1)

10-24: Consider using integer type for counters instead of number

Fields like deployment.*.*.count (and similar count-like fields elsewhere) are conceptually integers. Using "type": "integer" instead of "type": "number" would prevent fractional values from passing schema validation while keeping current fixtures valid.

Also applies to: 41-65

specs/sdl/groups.schema.yaml (1)

45-65: Tighten groups schema for count and price.denom

To better reflect the data model and avoid subtle mistakes:

  • resources[*].count should be an integer (non-negative) rather than a generic number.
  • price currently requires amount but not denom; in practice both are always present, so making denom required too would strengthen validation without affecting existing fixtures.
go/sdl/parity_test.go (1)

17-46: Optional: reuse compiled JSON Schemas across fixtures

validateAgainstSchema reads and compiles the schema file for every manifest/groups validation. With many fixtures this adds unnecessary overhead. Consider caching compiled schemas keyed by schemaPath (e.g., package-level map[string]*gojsonschema.Schema protected by sync.Mutex/sync.Once) so each schema file is loaded and compiled only once per test run.

Also applies to: 94-118

ts/src/sdl/SDL/parity.spec.ts (1)

81-101: Optional: cache compiled schemas to avoid recompiling on every fixture

validateAgainstSchema reads and compiles the YAML schema on every call. For many fixtures this adds overhead in test runs. You could cache the compiled Ajv validators by schemaPath (e.g., a simple Map<string, ValidateFunction>) so each schema file is parsed/compiled only once per test run. This follows Ajv's recommended pattern of compiling once and reusing the validate function rather than recompiling on each invocation.

specs/sdl/manifest.schema.yaml (1)

1-3: Add $schema and $id metadata for schema documentation and tooling.

This is a new public schema file. It lacks standard JSON Schema metadata fields that aid documentation, versioning, and integration with schema validators.

Add $schema (to declare the JSON Schema version) and $id (for schema identification and URL resolution) at the root level:

+$schema: 'https://json-schema.org/draft/2020-12/schema'
+$id: 'https://akash.network/schemas/sdl/manifest.schema.json'
+title: Akash SDL Manifest Schema
+description: Validates the structure of a compiled SDL manifest, including services, resources, and deployment configurations.
 items:
   additionalProperties: false
   properties:

Also applies to: 328-328

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 01f2e37 and c669081.

⛔ Files ignored due to path filters (3)
  • go/sdl/go.sum is excluded by !**/*.sum
  • ts/package-lock.json is excluded by !**/package-lock.json
  • ts/src/sdl/SDL/__snapshots__/SDL.spec.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (50)
  • .github/workflows/tests.yaml (1 hunks)
  • .gitignore (1 hunks)
  • go/sdl/cmd/generate-sdl-fixtures/main.go (1 hunks)
  • go/sdl/go.mod (2 hunks)
  • go/sdl/groupBuilder_v2.go (1 hunks)
  • go/sdl/groupBuilder_v2_1.go (1 hunks)
  • go/sdl/parity_test.go (1 hunks)
  • make/test.mk (1 hunks)
  • specs/sdl/groups.schema.yaml (1 hunks)
  • specs/sdl/manifest.schema.yaml (1 hunks)
  • specs/sdl/sdl-input.schema.yaml (1 hunks)
  • specs/sdl/validation-limitations.md (1 hunks)
  • testdata/sdl/invalid/credentials-missing-host.yaml (1 hunks)
  • testdata/sdl/invalid/endpoint-not-used.yaml (1 hunks)
  • testdata/sdl/invalid/invalid-port.yaml (1 hunks)
  • testdata/sdl/invalid/missing-deployment.yaml (1 hunks)
  • testdata/sdl/invalid/missing-image.yaml (1 hunks)
  • testdata/sdl/invalid/missing-version.yaml (1 hunks)
  • testdata/sdl/invalid/negative-cpu.yaml (1 hunks)
  • testdata/sdl/invalid/persistent-without-mount.yaml (1 hunks)
  • testdata/sdl/v2.0/gpu-basic/groups.json (1 hunks)
  • testdata/sdl/v2.0/gpu-basic/input.yaml (1 hunks)
  • testdata/sdl/v2.0/gpu-basic/manifest.json (1 hunks)
  • testdata/sdl/v2.0/http-options/groups.json (1 hunks)
  • testdata/sdl/v2.0/http-options/input.yaml (1 hunks)
  • testdata/sdl/v2.0/http-options/manifest.json (1 hunks)
  • testdata/sdl/v2.0/ip-endpoint/groups.json (1 hunks)
  • testdata/sdl/v2.0/ip-endpoint/input.yaml (1 hunks)
  • testdata/sdl/v2.0/ip-endpoint/manifest.json (1 hunks)
  • testdata/sdl/v2.0/multiple-services/groups.json (1 hunks)
  • testdata/sdl/v2.0/multiple-services/input.yaml (1 hunks)
  • testdata/sdl/v2.0/multiple-services/manifest.json (1 hunks)
  • testdata/sdl/v2.0/persistent-storage/groups.json (1 hunks)
  • testdata/sdl/v2.0/persistent-storage/input.yaml (1 hunks)
  • testdata/sdl/v2.0/persistent-storage/manifest.json (1 hunks)
  • testdata/sdl/v2.0/simple/groups.json (1 hunks)
  • testdata/sdl/v2.0/simple/input.yaml (1 hunks)
  • testdata/sdl/v2.0/simple/manifest.json (1 hunks)
  • testdata/sdl/v2.0/storage-classes/groups.json (1 hunks)
  • testdata/sdl/v2.0/storage-classes/input.yaml (1 hunks)
  • testdata/sdl/v2.0/storage-classes/manifest.json (1 hunks)
  • testdata/sdl/v2.1/credentials/groups.json (1 hunks)
  • testdata/sdl/v2.1/credentials/input.yaml (1 hunks)
  • testdata/sdl/v2.1/credentials/manifest.json (1 hunks)
  • testdata/sdl/v2.1/ip-endpoint/groups.json (1 hunks)
  • testdata/sdl/v2.1/ip-endpoint/input.yaml (1 hunks)
  • testdata/sdl/v2.1/ip-endpoint/manifest.json (1 hunks)
  • ts/package.json (1 hunks)
  • ts/src/sdl/SDL/SDL.ts (9 hunks)
  • ts/src/sdl/SDL/parity.spec.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
ts/src/sdl/SDL/parity.spec.ts (1)
ts/src/sdl/SDL/SDL.ts (3)
  • validate (128-138)
  • validate (246-261)
  • SDL (88-1315)
go/sdl/cmd/generate-sdl-fixtures/main.go (1)
go/sdl/sdl.go (1)
  • ReadFile (83-89)
go/sdl/parity_test.go (2)
go/sdl/sdl.go (1)
  • ReadFile (83-89)
go/testutil/deployment.go (1)
  • DeploymentGroups (54-62)
ts/src/sdl/SDL/SDL.ts (2)
ts/src/sdl/index.ts (1)
  • SdlValidationError (28-28)
ts/src/sdl/types.ts (1)
  • v3Sdl (100-105)
🪛 GitHub Actions: lint
go/sdl/cmd/generate-sdl-fixtures/main.go

[error] 87-87: gofmt formatting issue detected. File is not properly formatted. Run 'gofmt -w' or 'go fmt ./...' to fix.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: coverage
🔇 Additional comments (42)
.gitignore (1)

27-34: Well-organized additions supporting the testing infrastructure.

The new entries appropriately capture test/build artifacts and coverage outputs introduced by the SDL parity testing changes. The naming conventions align with existing patterns, and the logical grouping with comments enhances clarity.

testdata/sdl/invalid/missing-image.yaml (1)

1-30: Invalid SDL fixture for missing image looks correct

SDL structure is consistent with other v2.0 fixtures, and omitting services.web.image should exercise the intended invalid case.

testdata/sdl/invalid/invalid-port.yaml (1)

1-32: Invalid‑port SDL fixture is well‑formed for the negative test

The SDL is structurally valid but uses port: 99999, which is out of the allowed port range, making it a good fixture to assert that validation rejects out‑of‑range ports.

testdata/sdl/v2.0/ip-endpoint/manifest.json (1)

1-103: ip‑endpoint manifest fixture appears structurally consistent

The manifest structure (group → service → resources/endpoints → expose with ip and endpointSequenceNumber) looks consistent with the v2.0/ip‑endpoint scenario and suitable for parity tests. Assuming this JSON was generated by the fixture tool, it should accurately represent the expected manifest; if it was edited manually, it’s worth re‑generating once to ensure it matches generator output and the companion groups.json.

testdata/sdl/invalid/negative-cpu.yaml (1)

1-32: Negative‑CPU SDL fixture correctly targets invalid resource values

Using cpu.units: -100m in an otherwise valid deployment is an appropriate way to exercise validation for disallowing negative CPU values.

ts/package.json (1)

69-81: AJV is correctly placed as a devDependency

AJV (^8.12.0) is used only in test files (ts/src/sdl/SDL/parity.spec.ts), confirming it should remain in devDependencies and not be added to dependencies. No runtime code imports or depends on AJV.

testdata/sdl/invalid/credentials-missing-host.yaml (1)

1-35: LGTM! Invalid fixture correctly tests missing host credential validation.

This fixture appropriately tests the credential validation pathway by intentionally omitting the required host field while providing username and password. The validation logic in ts/src/sdl/SDL/SDL.ts (lines 301-310) will correctly reject this SDL.

testdata/sdl/v2.0/http-options/manifest.json (1)

1-73: LGTM! HTTP options manifest fixture is well-structured.

The manifest correctly defines HTTP options with appropriate retry policies and timeout configurations for parity testing.

testdata/sdl/v2.0/storage-classes/manifest.json (1)

1-117: LGTM! Storage classes manifest correctly demonstrates multiple storage volumes.

The fixture properly defines storage with varying attributes (persistent flags, storage classes) and the storage params section follows the alphabetically sorted order (cache, data, logs) as enforced by the updated SDL.ts logic.

testdata/sdl/invalid/endpoint-not-used.yaml (1)

1-35: LGTM! Invalid fixture correctly tests unused endpoint detection.

This fixture appropriately validates that endpoints defined but never referenced in service exposures are detected as errors. The validateEndpointsUtility method in SDL.ts will correctly flag this scenario.

testdata/sdl/v2.0/gpu-basic/manifest.json (1)

1-76: LGTM! GPU manifest fixture correctly represents GPU configuration.

The fixture properly defines GPU resources with NVIDIA RTX 3080 specification, suitable for testing GPU-enabled deployment parity.

ts/src/sdl/SDL/SDL.ts (5)

2-2: LGTM! Named import improves clarity.

Using the named load import from js-yaml is more explicit and follows better practices than relying on default exports.


114-121: LGTM! Version field validation enhances robustness.

Adding an explicit check for the required version field with a clear error message improves SDL validation. The type cast to v3Sdl appropriately follows the validation.


285-299: LGTM! Service image and port validations strengthen SDL integrity.

Both validation methods correctly enforce essential constraints:

  • Empty image names are rejected
  • Port values are constrained to valid range (1-65535)

The error messages are clear and helpful.


848-864: LGTM! Proper null handling and deterministic storage ordering.

The method correctly handles undefined/null params by returning null, and ensures deterministic storage parameter ordering through alphabetical sorting.


899-916: LGTM! Conditional params assignment prevents unnecessary fields.

Computing params via v3ManifestServiceParams and only assigning when not null prevents adding empty params fields to the manifest, resulting in cleaner output.

.github/workflows/tests.yaml (1)

57-76: LGTM! SDL parity CI job is properly configured.

The new job correctly mirrors the setup steps from the coverage job and executes the SDL parity tests. The setup includes both Node.js and Go environments as required for cross-language parity validation.

testdata/sdl/v2.1/credentials/manifest.json (1)

1-75: LGTM! Credentials manifest correctly includes all required fields.

This fixture appropriately tests the valid credentials scenario with all required fields (host, email, username, password) present, complementing the invalid credentials fixtures.

testdata/sdl/v2.0/gpu-basic/groups.json (1)

1-65: GPU basic group fixture looks structurally consistent

The JSON structure (requirements/resources/price) is consistent with other group fixtures, and the numeric values (CPU/memory/storage/GPU/price) look coherent for a minimal GPU case.

testdata/sdl/v2.0/multiple-services/groups.json (1)

1-130: Multiple-services group fixture is coherent and well-formed

Requirements, resource variants, and pricing are internally consistent and align with the structure used in the other group fixtures in this PR.

testdata/sdl/invalid/missing-deployment.yaml (1)

1-27: Invalid “missing deployment” fixture matches its intent

YAML is structurally valid, and omitting the deployment block accurately represents the intended invalid case for tests.

testdata/sdl/invalid/persistent-without-mount.yaml (1)

1-35: Persistent-without-mount invalid fixture is clearly defined

The compute/storage and deployment sections are well-formed, and the missing mount for a persistent: true volume cleanly expresses the invalid scenario the tests will exercise.

testdata/sdl/v2.0/http-options/input.yaml (1)

1-50: HTTP-options SDL fixture is well-structured and expressive

The http_options block and its parameters (timeouts, retries, next_cases) are clearly defined, and the compute/placement/deployment sections follow the established SDL v2.0 pattern, making this a good parity fixture.

testdata/sdl/v2.0/ip-endpoint/input.yaml (1)

1-49: IP-endpoint SDL fixture cleanly exercises named IP exposure

The service/expose blocks correctly reference ip: myendpoint, and the matching endpoints.myendpoint definition (kind ip) plus standard compute/placement/deployment make this a solid parity case.

testdata/sdl/v2.1/credentials/groups.json (1)

1-59: Credentials group fixture matches existing group schema usage

The requirements, resource definition, and pricing fields align with the other v2.0/v2.1 group fixtures and look appropriate for a credentials-focused parity test.

testdata/sdl/v2.0/simple/input.yaml (1)

1-47: LGTM! Well-structured SDL v2.0 test fixture.

The fixture provides good coverage with multiple expose configurations (HTTP and UDP), placement attributes, and signedBy constraints with both anyOf and allOf.

testdata/sdl/v2.0/persistent-storage/manifest.json (1)

1-92: LGTM! Comprehensive manifest fixture for persistent storage.

The manifest correctly captures persistent storage configuration with volume attributes (persistent: true, class: beta2) and mount parameters. The structure aligns with the expected manifest schema for v2.0.

testdata/sdl/invalid/missing-version.yaml (1)

1-31: LGTM! Valid negative test case for missing version.

This fixture appropriately tests SDL validation by omitting the required version field while maintaining otherwise valid structure. This ensures error handling for missing version information is properly exercised.

testdata/sdl/v2.0/gpu-basic/input.yaml (1)

22-27: LGTM! Well-structured GPU resource configuration.

The GPU attributes correctly specify the vendor (nvidia) and model (rtx3080) in nested format, which aligns with SDL v2.0 GPU specifications. This provides good test coverage for GPU-enabled deployments.

testdata/sdl/v2.0/http-options/groups.json (1)

1-59: LGTM! Valid groups fixture with proper structure.

The groups data correctly defines requirements (signed_by, attributes), resources (compute specs, endpoints), and pricing. The 18-decimal precision for the amount field is standard for cryptocurrency denominations.

testdata/sdl/v2.1/ip-endpoint/input.yaml (1)

67-72: LGTM! Proper v2.1 IP endpoint configuration.

The endpoints section correctly defines IP endpoints (endpoint1, endpoint2) which are referenced by the services' expose configurations. This provides good test coverage for the v2.1 IP endpoint feature.

testdata/sdl/v2.0/persistent-storage/input.yaml (1)

13-17: LGTM! Correct persistent storage configuration.

The fixture properly links storage parameters (lines 14-17) with the compute profile's storage definition (lines 27-31). The storage volume is correctly marked as persistent with a storage class, and the mount point is properly specified.

Also applies to: 27-31

go/sdl/cmd/generate-sdl-fixtures/main.go (1)

60-60: File permissions 0644 are appropriate for generated test fixtures.

The codebase shows a consistent pattern: test and configuration files use 0644 permissions (owner read/write, group/others read-only), while sensitive files like key imports and governance data use 0o600. Since the manifest and groups JSON files are test fixtures intended to be committed and read by the build system, 0644 is the correct choice.

specs/sdl/validation-limitations.md (1)

1-12: All file references in the documentation are accurate. The schema file specs/sdl/sdl-input.schema.yaml and parser files go/sdl/v2.go and go/sdl/v2_1.go all exist at the paths specified in the documentation.

testdata/sdl/v2.1/ip-endpoint/manifest.json (1)

36-45: Verify if mixed endpoint structures between services are intentional.

The manifest defines asymmetric endpoint configurations: the "api" service (lines 36-45) has consistent endpoints with both kind and sequence_number, while the "web" service (lines 104-120) has mixed endpoints—some with only sequence_number, others with both fields. Confirm whether this variation represents intentional test coverage for different endpoint types or if it's an inconsistency requiring alignment.

Also applies to: 104-120

testdata/sdl/v2.1/credentials/input.yaml (1)

1-43: Schema reference and fixture consistency verified.

The relative schema path ../../../../specs/sdl/sdl-input.schema.yaml is correct and the schema file exists. The input.yaml file is consistent with its corresponding groups.json and manifest.json fixtures: the service "private-app" maps to the "akash" group, and all resource specifications (CPU 500m, memory 1Gi, storage 5Gi, pricing 250 uakt) and credentials match across all three files.

testdata/sdl/v2.1/ip-endpoint/groups.json (1)

1-114: ip-endpoint groups fixture shape looks consistent with schema

The group structure (requirements.signed_by/attributes, resources with cpu/memory/storage/gpu/endpoints, count, and price) matches the new groups.schema.yaml expectations and provides good coverage of endpoint kinds/sequence numbers. I don’t see structural or obvious value issues here.

testdata/sdl/v2.0/storage-classes/input.yaml (1)

1-59: Storage-classes SDL input aligns with the new SDL input schema

Service, profiles (compute/placement), and deployment sections — including per-volume storage attributes and the signedBy/pricing structure — all line up with sdl-input.schema.yaml. This fixture should be a solid basis for exercising storage-class handling.

testdata/sdl/v2.0/multiple-services/manifest.json (1)

1-193: multiple-services manifest fixture is internally consistent

Service definitions (images, env, counts), inter-service expose wiring, resource IDs, and endpoint/httpOptions structures are coherent and match what the v2.0 multiple-services SDL input describes. This looks like a good “golden” manifest for parity tests.

testdata/sdl/v2.0/multiple-services/input.yaml (1)

1-93: multiple-services SDL input matches schema and manifest/groups fixtures

The service graph (web → api → db), compute/placement profiles, pricing, and deployment counts are coherent and line up with the generated manifest/groups fixtures. The $schema header also correctly references sdl-input.schema.yaml.

specs/sdl/manifest.schema.yaml (2)

140-157: Clarify intentional structural difference between params.storage and resources.storage.

Storage appears in two locations with differing structures:

  • params.storage (lines 140-157): Array of objects with {name, mount, readOnly}
  • resources.storage (lines 277-313): Array of objects with {name, size, attributes}

These serve different purposes, but the schema does not document this. Confirm this is intentional and document the distinction.

Verify that these storage structures represent different semantic concepts (e.g., params storage = mount points, resources storage = resource specifications) and not a schema inconsistency.

Also applies to: 277-313


67-92: httpOptions is universally required and consistently applied across all protocols in valid manifests.

Verification of test fixtures confirms that all nine manifest files (v2.0 and v2.1) include httpOptions for every expose entry, regardless of protocol type (TCP, UDP, HTTP). No counter-examples exist where expose entries lack httpOptions. Non-HTTP protocols like TCP and UDP are fully supported and already accompanied by httpOptions in production fixtures, indicating this is an intentional schema design rather than an oversight. The concern that non-HTTP services would be rejected is unfounded—they are validated and expected to include httpOptions as part of the uniform expose specification.

@vertex451 vertex451 force-pushed the artem/sdl-parity-tests branch from c669081 to 37393d8 Compare December 2, 2025 16:18
@vertex451 vertex451 force-pushed the artem/sdl-parity-tests branch from 37393d8 to 204e70b Compare December 2, 2025 16:21
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
ts/src/sdl/SDL/parity.spec.ts (1)

163-169: Bug: finalValue is computed but immediately overwritten, making the clamping logic a no-op.

This was flagged in a previous review. Line 167 assigns finalValue (with the clamped endpointSequenceNumber), but line 169 immediately overwrites it with the original value, negating all finalValue adjustments.

       if (key === "endpointSequenceNumber" && typeof value === "number" && value > 1) {
         finalValue = 1;
       }

       normalized[normalizedKey] = finalValue;
-
-      normalized[normalizedKey] = value;
🧹 Nitpick comments (4)
specs/sdl/sdl-input.schema.yaml (2)

211-215: Fix indentation inconsistency for accept.items.

The items and type under accept have extra indentation compared to sibling properties.

             accept:
-                    items:
-                      type: string
-                    type: array
+                items:
+                  type: string
+                type: array

287-291: Consider adding services and profiles to required fields.

Currently only version and deployment are required. While semantic validation is deferred to runtime, a valid SDL structurally requires services and profiles sections. Adding them to required would catch malformed SDLs earlier during IDE validation.

 required:
   - version
   - deployment
+  - services
+  - profiles
ts/src/sdl/SDL/SDL.ts (1)

290-299: LGTM with minor suggestion.

Port validation logic is correct. Consider a clearer error message:

-        `Service "${serviceName}" has invalid port value. Port must be 0 < value <= 65535.`,
+        `Service "${serviceName}" has invalid port value. Port must be between 1 and 65535.`,
ts/src/sdl/SDL/parity.spec.ts (1)

404-413: Consider testing invalid fixtures against both beta2 and beta3.

Invalid fixtures are only tested with beta3. If validation differs between versions (e.g., the invalid-port.yaml uses version "2.0"), testing only beta3 may miss version-specific edge cases.

entries.forEach((filename) => {
  ["beta2", "beta3"].forEach((version) => {
    it(`should reject ${filename} (${version})`, () => {
      const input = fs.readFileSync(
        path.join(invalidDir, filename),
        "utf8",
      );
      expect(() => SDL.fromString(input, version as "beta2" | "beta3")).toThrow();
    });
  });
});
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c669081 and 204e70b.

⛔ Files ignored due to path filters (3)
  • go/sdl/go.sum is excluded by !**/*.sum
  • ts/package-lock.json is excluded by !**/package-lock.json
  • ts/src/sdl/SDL/__snapshots__/SDL.spec.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (50)
  • .github/workflows/tests.yaml (1 hunks)
  • .gitignore (1 hunks)
  • go/sdl/cmd/generate-sdl-fixtures/main.go (1 hunks)
  • go/sdl/go.mod (2 hunks)
  • go/sdl/groupBuilder_v2.go (1 hunks)
  • go/sdl/groupBuilder_v2_1.go (1 hunks)
  • go/sdl/parity_test.go (1 hunks)
  • make/test.mk (1 hunks)
  • specs/sdl/groups.schema.yaml (1 hunks)
  • specs/sdl/manifest.schema.yaml (1 hunks)
  • specs/sdl/sdl-input.schema.yaml (1 hunks)
  • specs/sdl/validation-limitations.md (1 hunks)
  • testdata/sdl/invalid/credentials-missing-host.yaml (1 hunks)
  • testdata/sdl/invalid/endpoint-not-used.yaml (1 hunks)
  • testdata/sdl/invalid/invalid-port.yaml (1 hunks)
  • testdata/sdl/invalid/missing-deployment.yaml (1 hunks)
  • testdata/sdl/invalid/missing-image.yaml (1 hunks)
  • testdata/sdl/invalid/missing-version.yaml (1 hunks)
  • testdata/sdl/invalid/negative-cpu.yaml (1 hunks)
  • testdata/sdl/invalid/persistent-without-mount.yaml (1 hunks)
  • testdata/sdl/v2.0/gpu-basic/groups.json (1 hunks)
  • testdata/sdl/v2.0/gpu-basic/input.yaml (1 hunks)
  • testdata/sdl/v2.0/gpu-basic/manifest.json (1 hunks)
  • testdata/sdl/v2.0/http-options/groups.json (1 hunks)
  • testdata/sdl/v2.0/http-options/input.yaml (1 hunks)
  • testdata/sdl/v2.0/http-options/manifest.json (1 hunks)
  • testdata/sdl/v2.0/ip-endpoint/groups.json (1 hunks)
  • testdata/sdl/v2.0/ip-endpoint/input.yaml (1 hunks)
  • testdata/sdl/v2.0/ip-endpoint/manifest.json (1 hunks)
  • testdata/sdl/v2.0/multiple-services/groups.json (1 hunks)
  • testdata/sdl/v2.0/multiple-services/input.yaml (1 hunks)
  • testdata/sdl/v2.0/multiple-services/manifest.json (1 hunks)
  • testdata/sdl/v2.0/persistent-storage/groups.json (1 hunks)
  • testdata/sdl/v2.0/persistent-storage/input.yaml (1 hunks)
  • testdata/sdl/v2.0/persistent-storage/manifest.json (1 hunks)
  • testdata/sdl/v2.0/simple/groups.json (1 hunks)
  • testdata/sdl/v2.0/simple/input.yaml (1 hunks)
  • testdata/sdl/v2.0/simple/manifest.json (1 hunks)
  • testdata/sdl/v2.0/storage-classes/groups.json (1 hunks)
  • testdata/sdl/v2.0/storage-classes/input.yaml (1 hunks)
  • testdata/sdl/v2.0/storage-classes/manifest.json (1 hunks)
  • testdata/sdl/v2.1/credentials/groups.json (1 hunks)
  • testdata/sdl/v2.1/credentials/input.yaml (1 hunks)
  • testdata/sdl/v2.1/credentials/manifest.json (1 hunks)
  • testdata/sdl/v2.1/ip-endpoint/groups.json (1 hunks)
  • testdata/sdl/v2.1/ip-endpoint/input.yaml (1 hunks)
  • testdata/sdl/v2.1/ip-endpoint/manifest.json (1 hunks)
  • ts/package.json (1 hunks)
  • ts/src/sdl/SDL/SDL.ts (9 hunks)
  • ts/src/sdl/SDL/parity.spec.ts (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • specs/sdl/validation-limitations.md
  • testdata/sdl/invalid/negative-cpu.yaml
🚧 Files skipped from review as they are similar to previous changes (37)
  • go/sdl/groupBuilder_v2.go
  • .github/workflows/tests.yaml
  • testdata/sdl/invalid/endpoint-not-used.yaml
  • testdata/sdl/invalid/missing-version.yaml
  • testdata/sdl/v2.0/persistent-storage/manifest.json
  • go/sdl/parity_test.go
  • make/test.mk
  • testdata/sdl/v2.0/storage-classes/groups.json
  • go/sdl/go.mod
  • testdata/sdl/v2.0/multiple-services/groups.json
  • testdata/sdl/v2.1/ip-endpoint/manifest.json
  • testdata/sdl/v2.0/ip-endpoint/manifest.json
  • testdata/sdl/v2.0/multiple-services/manifest.json
  • testdata/sdl/v2.0/simple/groups.json
  • testdata/sdl/v2.0/gpu-basic/groups.json
  • testdata/sdl/invalid/missing-deployment.yaml
  • testdata/sdl/v2.0/http-options/input.yaml
  • testdata/sdl/v2.0/simple/manifest.json
  • testdata/sdl/v2.0/http-options/groups.json
  • testdata/sdl/invalid/missing-image.yaml
  • testdata/sdl/v2.0/ip-endpoint/groups.json
  • ts/package.json
  • go/sdl/cmd/generate-sdl-fixtures/main.go
  • testdata/sdl/v2.0/gpu-basic/manifest.json
  • testdata/sdl/v2.0/storage-classes/manifest.json
  • specs/sdl/manifest.schema.yaml
  • testdata/sdl/v2.1/credentials/groups.json
  • testdata/sdl/v2.0/simple/input.yaml
  • testdata/sdl/invalid/credentials-missing-host.yaml
  • testdata/sdl/v2.1/ip-endpoint/input.yaml
  • testdata/sdl/v2.0/gpu-basic/input.yaml
  • testdata/sdl/v2.1/ip-endpoint/groups.json
  • testdata/sdl/invalid/persistent-without-mount.yaml
  • testdata/sdl/v2.1/credentials/manifest.json
  • specs/sdl/groups.schema.yaml
  • .gitignore
  • testdata/sdl/v2.0/http-options/manifest.json
🧰 Additional context used
🧬 Code graph analysis (1)
ts/src/sdl/SDL/SDL.ts (2)
ts/src/sdl/index.ts (1)
  • SdlValidationError (28-28)
ts/src/sdl/types.ts (1)
  • v3Sdl (100-105)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: coverage
  • GitHub Check: test
  • GitHub Check: go
🔇 Additional comments (14)
testdata/sdl/v2.0/persistent-storage/input.yaml (1)

3-48: SDL configuration is well-formed and consistent.

The YAML SDL v2.0 configuration correctly defines:

  • Persistent storage setup with Postgres database service
  • Compute profile with appropriate resource specifications (500m CPU, 1Gi RAM, 50Gi storage)
  • Placement constraints (region us-west, signed_by policy)
  • Pricing and deployment mappings

Cross-referenced against the companion groups.json fixture, resource specifications are consistent.

testdata/sdl/v2.0/persistent-storage/groups.json (1)

1-70: JSON fixture data is accurate and consistent with input.yaml.

Byte conversions are correct:

  • Memory: 1,073,741,824 bytes = 1 GiB ✓
  • Storage: 53,687,091,200 bytes = 50 GiB ✓

All resource specifications (CPU 500 units, storage class/attributes, pricing, count) align with the corresponding input.yaml file. The data structure appears well-suited for parity test validation.

testdata/sdl/v2.0/storage-classes/input.yaml (2)

1-1: The schema reference path is correct and resolves properly. The file specs/sdl/sdl-input.schema.yaml exists at the expected location, and the relative path ../../../../specs/sdl/sdl-input.schema.yaml correctly references the SDL input schema from the file's location at testdata/sdl/v2.0/storage-classes/input.yaml.


2-58: LGTM! Comprehensive storage-classes fixture.

The SDL v2.0 manifest provides a well-structured test fixture that exercises the storage-classes feature with parametric storage definitions, mixed storage attributes (persistent flags and class names), and proper deployment configuration. The mixed attribute patterns (cache/logs minimal vs. data with both persistent and class) are intentional for testing edge cases. The fixture validates successfully against the sdl-input.schema.yaml schema.

go/sdl/groupBuilder_v2_1.go (1)

115-121: LGTM! Deterministic ordering ensures parity testing reliability.

The implementation correctly addresses non-deterministic Go map iteration by collecting storage names, sorting them, and iterating in sorted order. This ensures consistent output across runs, which is essential for the parity tests between Go and TypeScript implementations.

testdata/sdl/v2.0/ip-endpoint/input.yaml (1)

1-49: LGTM!

Well-structured SDL v2.0 test fixture for IP endpoint functionality. The configuration correctly demonstrates:

  • Dual port exposure (80 and 443) with IP endpoint references
  • Proper endpoint declaration matching the IP references in the service expose section
  • Valid compute profile and placement configuration
testdata/sdl/v2.0/multiple-services/input.yaml (1)

1-93: LGTM!

Comprehensive multi-service SDL v2.0 test fixture demonstrating:

  • Service dependency chains (web → api → db)
  • Multiple compute profiles with varying resource allocations
  • Both anyOf and allOf signedBy constraints
  • Inter-service expose configurations

The hardcoded credentials (lines 29, 18) are appropriate for test fixtures.

testdata/sdl/invalid/invalid-port.yaml (1)

1-32: LGTM!

Valid negative test fixture with intentionally invalid port value (99999 > 65535). This tests the port validation logic in validateServicePorts which enforces port > 0 && port <= 65535.

testdata/sdl/v2.1/credentials/input.yaml (1)

1-43: LGTM!

Well-structured SDL v2.1 test fixture for private registry credentials. Correctly demonstrates the credentials block structure (host, email, username, password) for private container registry authentication.

ts/src/sdl/SDL/SDL.ts (3)

113-122: LGTM!

Good addition of version field validation in fromString. This catches malformed SDLs early and provides a clear error message.


834-864: LGTM!

Good addition of deterministic storage key ordering via .sort() in both v2ManifestServiceParams and v3ManifestServiceParams. This ensures consistent output for parity testing between Go and TypeScript implementations.


899-917: LGTM!

Clean refactor of params handling. The conditional assignment (if (params !== null)) prevents null params from polluting the manifest output.

ts/src/sdl/SDL/parity.spec.ts (2)

24-79: LGTM!

makeSchemaLenient appropriately relaxes JSON Schema constraints for flexible parity testing. The recursive handling of nested structures (properties, items, anyOf, oneOf, allOf) is thorough.


303-315: Verify: v2.0 fixtures use v3Manifest method.

The tests call sdl.v3Manifest(true) for v2.0/beta2 fixtures instead of sdl.v2Manifest(). The normalization handles GPU filtering for beta2, but verify this is the intended parity comparison approach.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
specs/sdl/manifest.schema.yaml (1)

1-6: Consider adding standard JSON Schema root declarations.

The schema starts directly with items: without a root-level type: array declaration or $schema identifier. While this may work with some validators, adding these improves compatibility and clarity:

+$schema: "https://json-schema.org/draft/2020-12/schema"
+type: array
 items:
   additionalProperties: false
   properties:
     name:
       type: string
ts/src/sdl/SDL/parity.spec.ts (2)

68-76: Redundant processing for array items.

Lines 68-76 re-check additionalProperties and required on itemsSchema, but makeSchemaLenient was already called on lenient.items at line 53, which would have handled these. This block is defensive but unnecessary.

-  if (lenient.type === "array" && lenient.items) {
-    const itemsSchema = lenient.items as Record<string, unknown>;
-    if (itemsSchema.additionalProperties === false) {
-      itemsSchema.additionalProperties = true;
-    }
-    if (itemsSchema.required && Array.isArray(itemsSchema.required)) {
-      delete itemsSchema.required;
-    }
-  }
-
   return lenient;

81-101: Schema is recompiled on every validation call.

ajv.compile() is called each time validateAgainstSchema is invoked. Since the same schemas are validated multiple times across fixtures, consider caching compiled validators.

+const schemaCache = new Map<string, ReturnType<typeof ajv.compile>>();
+
 function validateAgainstSchema(actual: unknown, schemaPath: string): { valid: boolean; errors: string[] } {
   if (!fs.existsSync(schemaPath)) {
     return { valid: true, errors: [] };
   }

+  let validate = schemaCache.get(schemaPath);
+  if (!validate) {
     const schemaContent = fs.readFileSync(schemaPath, "utf8");
     const schema = load(schemaContent) as JSONSchema;
     const lenientSchema = makeSchemaLenient(schema);
-  const validate = ajv.compile(lenientSchema as Record<string, unknown>);
+    validate = ajv.compile(lenientSchema as Record<string, unknown>);
+    schemaCache.set(schemaPath, validate);
+  }
   const valid = validate(actual);
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 204e70b and 2565b0b.

📒 Files selected for processing (2)
  • specs/sdl/manifest.schema.yaml (1 hunks)
  • ts/src/sdl/SDL/parity.spec.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: proto
  • GitHub Check: go
  • GitHub Check: sdl-parity
  • GitHub Check: test
  • GitHub Check: coverage
🔇 Additional comments (7)
specs/sdl/manifest.schema.yaml (2)

295-314: Good use of $defs for reusable credentials schema.

The credentials schema is now properly defined once in $defs and referenced via $ref at lines 26 and 105, eliminating the previous duplication.


33-95: LGTM!

The expose schema is well-structured with proper nesting for httpOptions, required fields, and nullable patterns using anyOf with type: 'null'.

ts/src/sdl/SDL/parity.spec.ts (5)

103-171: LGTM!

The normalizeKeys function correctly handles key transformations and value normalization. The previous issue with finalValue being overwritten has been addressed—now only finalValue is assigned to normalized[normalizedKey].


179-188: Numeric string normalization may have edge cases.

The logic at lines 181-186 only normalizes strings containing . and checks for specific trailing zero patterns. Integer strings (e.g., "100") won't be converted, and strings like "1.0" might not match the .000000000000000000 pattern.

If this is intentional to handle specific Go/TS output differences, consider adding a brief comment explaining the expected scenarios.


251-280: LGTM!

The loadFixtures function correctly discovers and validates fixture directories, ensuring only complete fixtures (with input.yaml, manifest.json, and groups.json) are returned.


402-411: Invalid fixtures only tested against beta3.

The invalid fixtures test uses hardcoded "beta3" version. If v2.0/beta2 has different validation rules, consider also testing invalid fixtures against beta2, or documenting that invalid fixtures are version-specific.


295-329: LGTM!

The v2.0 test suite correctly loads fixtures, normalizes both actual and expected outputs, compares them, and validates against schemas. The error logging on validation failure aids debugging.

…sdl-parity-tests

Signed-off-by: Artem Shcherbatiuk <artem@akash.network>
Co-authored-by: Artem Shcherbatiuk <vertex451@gmail.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (7)
specs/sdl/sdl-input.schema.yaml (1)

386-388: Consider adding services to required fields.

The required array includes version and deployment, but services is not required. Since deployments reference services by name, an SDL without services would likely fail at runtime. Adding services here would catch this earlier during schema validation.

 required:
   - version
   - deployment
+  - services
ts/src/sdl/SDL/SDL.ts (1)

1305-1313: Consider extracting duplicate amount formatting logic.

This amount formatting logic is duplicated in both v2Groups (lines 1305-1312) and v3Groups (lines 1210-1217). Consider extracting to a helper method:

private formatAmount(amount: number | string | undefined): string {
  return typeof amount === "number"
    ? amount.toFixed(18)
    : (amount?.toString() || "0");
}
specs/sdl/groups-output.schema.yaml (1)

1-5: Consider adding $schema declaration and metadata.

Unlike sdl-input.schema.yaml, this schema lacks the $schema declaration and descriptive metadata. Adding these would improve documentation and IDE support:

$schema: http://json-schema.org/draft-07/schema#
title: Akash SDL Groups Output Schema
description: Schema for validating SDL groups output structure.
items:
  # ... rest of schema
specs/sdl/manifest-output.schema.yaml (1)

1-6: Consider adding $schema declaration and metadata.

Similar to the groups output schema, adding metadata would improve documentation:

$schema: http://json-schema.org/draft-07/schema#
title: Akash SDL Manifest Output Schema
description: Schema for validating SDL manifest output structure.
items:
  # ... rest of schema
ts/src/sdl/SDL/parity.spec.ts (3)

22-35: Consider caching compiled schema validators.

ajv.compile(schema) is called for each validation, recompiling the same schemas multiple times (once per fixture × 3 schemas). AJV caches compiled validators internally by schema identity, but since the schema is re-read and re-parsed each time, caching is bypassed.

Consider preloading and compiling schemas once:

const schemaCache = new Map<string, AjvModule.ValidateFunction>();

function validateAgainstSchema(name: string, data: unknown, schemaPath: string): void {
  let validate = schemaCache.get(schemaPath);
  if (!validate) {
    const schemaContent = fs.readFileSync(schemaPath, "utf8");
    const schema = load(schemaContent);
    validate = ajv.compile(schema as Record<string, unknown>);
    schemaCache.set(schemaPath, validate);
  }
  // ... rest of validation
}

71-122: Consider parameterizing v2.0 and v2.1 test blocks.

The two describe blocks are nearly identical. You could reduce duplication with parameterized tests:

describe.each([
  { version: "v2.0", sdlVersion: "beta2" as const },
  { version: "v2.1", sdlVersion: "beta3" as const },
])("$version", ({ version, sdlVersion }) => {
  const fixtures = loadFixtures(version);

  fixtures.forEach((fixture) => {
    it(fixture.name, () => {
      // ... test body using sdlVersion
    });
  });
});

However, the current explicit form is also fine for readability.


124-142: Early return silently skips invalid SDL tests.

If testdata/sdl/invalid doesn't exist, the describe block executes but defines no tests. This might hide a missing test fixture issue. Consider either:

  1. Making the directory required (throw an error)
  2. Using describe.skip with a message
  3. At minimum, logging a warning
describe("invalid SDLs rejected", () => {
  const invalidDir = path.join(fixturesRoot, "invalid");

  if (!fs.existsSync(invalidDir)) {
    it.skip("invalid fixtures directory not found", () => {});
    return;
  }
  // ...
});
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2565b0b and f30f8d6.

📒 Files selected for processing (7)
  • go/sdl/parity_test.go (1 hunks)
  • specs/sdl/README.md (1 hunks)
  • specs/sdl/groups-output.schema.yaml (1 hunks)
  • specs/sdl/manifest-output.schema.yaml (1 hunks)
  • specs/sdl/sdl-input.schema.yaml (1 hunks)
  • ts/src/sdl/SDL/SDL.ts (19 hunks)
  • ts/src/sdl/SDL/parity.spec.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • specs/sdl/README.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • go/sdl/parity_test.go
🧰 Additional context used
🧬 Code graph analysis (1)
ts/src/sdl/SDL/parity.spec.ts (1)
ts/src/sdl/SDL/SDL.ts (3)
  • validate (129-139)
  • validate (318-333)
  • SDL (89-1428)
🪛 GitHub Check: ts
ts/src/sdl/SDL/SDL.ts

[failure] 1212-1212:
Trailing spaces not allowed


[failure] 1307-1307:
Trailing spaces not allowed

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: test
  • GitHub Check: coverage
  • GitHub Check: sdl-parity
  • GitHub Check: go
  • GitHub Check: proto
🔇 Additional comments (8)
specs/sdl/sdl-input.schema.yaml (2)

117-128: Verify the storage conditional validation logic.

The second if/then block appears to enforce that when persistent is false, the class property must NOT exist. This seems inverted from the typical constraint (usually you'd want to restrict when persistent: true with class: ram). The first block (lines 105-116) already handles the class: ram case.

Could you confirm this is the intended behavior? If it's meant to prevent non-persistent storage from having a class attribute, consider adding a comment explaining this constraint.


182-184: Verify denom pattern case sensitivity.

The pattern ^(uakt|ibc/.*)$ uses lowercase ibc. In the Cosmos ecosystem, IBC denoms are typically uppercase (e.g., ibc/27394FB092D2ECCD...). If both cases are valid, consider making the pattern case-insensitive:

-                    pattern: '^(uakt|ibc/.*)$'
+                    pattern: '^(uakt|[iI][bB][cC]/.*)$'

Or simply use uppercase if that's the canonical form.

ts/src/sdl/SDL/SDL.ts (3)

247-316: LGTM - convertToGoFormat handles key transformations comprehensively.

The method handles Go-specific key naming conventions (snake_case, special cases like HTTPOptionshttpOptions), empty array to null conversion for all_of/any_of, and properly skips undefined values. The recursion for arrays and nested objects is correct.


906-918: LGTM - Sorted storage keys ensure deterministic output.

Sorting storage keys before iteration ensures consistent ordering between Go and TypeScript implementations, which is essential for parity testing.


1170-1179: LGTM - GPU default handling matches Go behavior.

Always including GPU in groups output (defaulting to units "0" when absent) ensures parity with Go's output format.

specs/sdl/groups-output.schema.yaml (1)

49-50: LGTM - Schema structure is comprehensive.

The groups output schema properly validates the nested structure including resources with CPU, GPU, memory, storage, and endpoints. The strict additionalProperties: false ensures no unexpected fields pass validation.

specs/sdl/manifest-output.schema.yaml (1)

295-314: LGTM - Good use of $defs for reusable credentials schema.

The credentials definition is properly extracted and referenced in two places, reducing duplication and ensuring consistency.

ts/src/sdl/SDL/parity.spec.ts (1)

37-69: LGTM - Fixture loading with helpful error messages.

The loadFixtures function provides clear error messages guiding developers to run make generate-sdl-fixtures when fixtures are missing. This is good developer experience.

@vertex451 vertex451 requested a review from stalniy December 4, 2025 14:52
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
ts/src/sdl/SDL/SDL.ts (3)

247-316: Consider a key transformation map for maintainability.

The convertToGoFormat method handles key transformations with a long chain of conditionals. While functional, this approach can be error-prone as the number of special cases grows. Lines 279 and 281 map endpointSequenceNumber and externalPort to themselves—verify these keys don't require snake_case transformation for Go compatibility.

Consider replacing the conditional chain with a transformation map:

private readonly GO_KEY_MAP: Record<string, string> = {
  sequenceNumber: "sequence_number",
  signedBy: "signed_by",
  allOf: "all_of",
  anyOf: "any_of",
  quantity: "size",
  HTTPOptions: "httpOptions",
  IP: "ip"
  // add other mappings
};

private convertToGoFormat(obj: unknown): unknown {
  // ... existing null/array/primitive checks ...
  
  for (const key in objRecord) {
    // ... existing undefined checks ...
    
    let convertedKey = this.GO_KEY_MAP[key] || key;
    // Then apply fallback transformations (camelCase, snake_case) if not in map
    // ...
  }
}

This makes the transformations explicit and easier to audit.


1028-1061: Update comment to reflect deduplication behavior.

The comment at line 1029 states "Collect all IPs (including duplicates)" but line 1050 explicitly deduplicates via new Set(endpointNames). The implementation correctly deduplicates, but the comment is misleading.

Update the comment to:

-    // Collect all IPs (including duplicates) to match Go behavior
+    // Collect all IPs from service endpoints, then deduplicate and sort for stable sequence assignment

1210-1218: Consider extracting amount formatting to reduce duplication.

The amount formatting logic (lines 1210-1214) is duplicated in v2Groups (lines 1305-1309). Extracting this to a helper method would improve maintainability and ensure consistent precision handling.

Add a helper method:

private formatAmountForGo(amountValue: number | string | undefined): string {
  return typeof amountValue === "number"
    ? amountValue.toFixed(18)
    : (amountValue?.toString() || "0");
}

Then replace both occurrences with:

const amountStr = this.formatAmountForGo(pricing.amount as number | string | undefined);
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f30f8d6 and f0cc996.

📒 Files selected for processing (1)
  • ts/src/sdl/SDL/SDL.ts (19 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
ts/src/sdl/SDL/SDL.ts (2)
ts/src/sdl/index.ts (1)
  • SdlValidationError (28-28)
ts/src/sdl/types.ts (3)
  • v3Manifest (4-4)
  • v3ComputeResources (218-224)
  • v3DeploymentGroup (270-285)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: sdl-parity
  • GitHub Check: coverage
  • GitHub Check: test
  • GitHub Check: proto
  • GitHub Check: go
🔇 Additional comments (15)
ts/src/sdl/SDL/SDL.ts (15)

2-2: LGTM: Updated to named import from js-yaml.

The change from YAML.load(...) to load(...) aligns with modern js-yaml API usage.


57-57: LGTM: Clean type definition for output format switching.

The OutputFormat type provides clear intent for toggling between TypeScript and Go-compatible output formats.


129-139: LGTM: Updated YAML parsing to use load().

The change aligns with the updated import and maintains existing validation logic.


318-333: LGTM: Extracted validation methods improve modularity.

The new calls to validateServiceImage and validateServicePorts make the validation logic more organized and testable.


357-361: LGTM: Runtime validation ensures non-empty image names.

This validation complements the JSON schema checks used in tests and provides runtime protection against empty or whitespace-only image names.


362-371: LGTM: Port range validation is correct.

The validation ensures ports fall within the valid TCP/UDP range (1-65535), providing runtime protection consistent with the JSON schema constraints.


906-918: LGTM: Storage key sorting ensures deterministic output.

Sorting storage keys before iteration ensures consistent, reproducible manifest output that matches Go implementation behavior.


920-936: LGTM: Proper null handling and deterministic storage ordering.

The method correctly returns null for undefined/null params and sorts storage keys for deterministic output. The error check at line 928 is appropriate for catching malformed configurations.


961-989: LGTM: Conditional params assignment matches Go's optional field handling.

Only assigning params when non-null ensures the output matches Go's behavior of omitting optional fields that are null/undefined.


998-1010: LGTM: Format parameter enables Go-compatible output.

The method cleanly supports both TypeScript and Go output formats by conditionally applying convertToGoFormat and propagating the format parameter to dependent methods.


1080-1096: LGTM: Dynamic key naming supports format compatibility.

Switching between "quantity" and "size" based on the asString parameter ensures the output matches expected formats for both TypeScript and manifest representations.


1099-1110: LGTM: Consistent key switching for storage units.

The dynamic key naming matches the pattern used in resourceUnitMemory and ensures format compatibility.


1170-1179: LGTM: GPU always included to match Go behavior.

The code correctly defaults to GPU with units: "0" when not present, ensuring output parity with the Go implementation. The comment clearly explains this design decision.


1194-1287: LGTM: Format parameter and precision handling ensure Go parity.

The method correctly handles the format parameter and applies 18-decimal precision to amounts (lines 1212-1214), which is critical for matching Go's numeric output. The convertToGoFormat conversion is appropriately applied at the end.


1190-1192: The groups() method passes the format parameter only to v3Groups(), not v2Groups() (line 1191).

This is a factual difference in how the two code paths handle the format parameter. If v2 is deprecated and no longer in active use, this design is appropriate. However, if v2 deployments are still supported or tested for parity with v3, the v2Groups() method should also accept and use the format parameter to maintain feature parity.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (2)
go/sdl/schema_validator.go (1)

114-120: Consider using errors.Join or structured error for validation failures.

The current approach concatenates error strings into a slice and formats with %v. This works but loses structure for downstream error handling.

+import "strings"
+
 	if !result.Valid() {
-		var errors []string
+		var errMsgs []string
 		for _, desc := range result.Errors() {
-			errors = append(errors, desc.String())
+			errMsgs = append(errMsgs, desc.String())
 		}
-		return fmt.Errorf("schema validation failed: %v", errors)
+		return fmt.Errorf("schema validation failed: %s", strings.Join(errMsgs, "; "))
 	}
ts/src/sdl/SDL/SDL.ts (1)

276-281: Redundant key mappings preserve original values unnecessarily.

Lines 278-281 map keys to themselves (sequenceNumbersequenceNumber, externalPortexternalPort), which has no effect. These could be removed unless they're placeholders for future mappings.

       if (key === "sequenceNumber") {
         convertedKey = "sequence_number";
-      } else if (key === "endpointSequenceNumber") {
-        convertedKey = "endpointSequenceNumber";
-      } else if (key === "externalPort") {
-        convertedKey = "externalPort";
       } else if (key === "signedBy") {

If these are intentionally preserved to document "no change needed", consider adding a comment.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f0cc996 and 420a7a3.

📒 Files selected for processing (5)
  • go/sdl/cmd/generate-sdl-fixtures/main.go (1 hunks)
  • go/sdl/parity_test.go (1 hunks)
  • go/sdl/schema_validator.go (1 hunks)
  • go/sdl/sdl.go (2 hunks)
  • ts/src/sdl/SDL/SDL.ts (19 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • go/sdl/cmd/generate-sdl-fixtures/main.go
  • go/sdl/parity_test.go
🧰 Additional context used
🧬 Code graph analysis (2)
ts/src/sdl/SDL/SDL.ts (1)
ts/src/sdl/types.ts (4)
  • v3Sdl (100-105)
  • v3Manifest (4-4)
  • v3ComputeResources (218-224)
  • v3DeploymentGroup (270-285)
go/sdl/schema_validator.go (1)
go/sdl/sdl.go (1)
  • ReadFile (83-89)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: sdl-parity
  • GitHub Check: test
  • GitHub Check: coverage
  • GitHub Check: go
  • GitHub Check: proto
🔇 Additional comments (6)
go/sdl/sdl.go (1)

92-136: Schema validation integration is consistent and well-structured.

The pattern of capturing schemaErr upfront and calling checkSchemaValidationResult at each exit point (both error and success) provides comprehensive coverage for comparing schema validation results against Go's runtime validation. All error paths are covered.

One consideration: since schemaErr is captured once at the start but checkSchemaValidationResult is called at different validation stages, any logged mismatch may not distinguish which validation stage disagreed with the schema. This is likely acceptable for parity testing purposes, but worth noting if debugging becomes difficult.

ts/src/sdl/SDL/SDL.ts (5)

362-371: Port validation logic is correct.

The validation correctly ensures ports are in the valid range (1-65535). The error message accurately describes the constraint.


1170-1179: GPU default handling ensures Go parity.

The logic to always include GPU with a default units: { val: "0" } when not present ensures consistent output structure matching Go's behavior. The comment is helpful.


906-936: Sorted storage keys ensure deterministic manifest output.

The changes to sort storage keys (Object.keys(params?.storage ?? {}).sort()) in both v2 and v3 manifest service params functions ensure consistent ordering across runs, which is essential for parity testing.


1053-1058: Verify if duplicate IP overwrite behavior matches Go implementation.

The code collects all IPs including duplicates (line 39) but assigns sequence numbers via object key assignment (line 1057), which means duplicate IPs will have only their last sequence number retained. The comments indicate this matches Go behavior, but this should be confirmed against the Go implementation to ensure the overwrite semantics are intentional rather than a bug.


1210-1217: toFixed(18) with very large numbers can produce scientific notation, but pricing amounts in fixtures are well within safe range.

JavaScript's toFixed(18) does produce scientific notation for values ≥ 1e21 (e.g., (1e21).toFixed(18) returns "1e+21"), but all pricing amounts in SDL fixtures are substantially smaller (ranging from 25 to 1,000,000), making this a non-issue in practice. The current implementation is safe for the actual use cases in this codebase.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
go/sdl/cmd/generate-sdl-fixtures/main.go (2)

13-15: Make fixtures root and version discovery less cwd‑dependent

fixturesRoot := "../../testdata/sdl" and the hard‑coded versions := []string{"v2.0", "v2.1"} assume a specific working directory and require code edits when new versions are added.

Consider:

  • Deriving fixturesRoot relative to the executable (or accepting it as a flag/env), so go run ./go/sdl/cmd/generate-sdl-fixtures from repo root still works reliably.
  • Optionally discovering versions by listing subdirectories under fixturesRoot instead of keeping a manual slice, unless you explicitly want to gate which versions are generated.

49-81: Permissions and overall generation loop look good, with a minor mode nit

The manifest/groups generation flow and error handling look solid and idempotent: each failure is logged and the loop continues with other fixtures.

One minor nit: os.WriteFile(..., 0600) will create new JSON fixtures as user‑read/write only. For repo fixtures that are meant to be shared, 0644 is more typical; consider switching to that unless you intentionally want stricter permissions.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 420a7a3 and 8e4864a.

📒 Files selected for processing (4)
  • go/provider/client/client.go (1 hunks)
  • go/sdl/cmd/generate-sdl-fixtures/main.go (1 hunks)
  • go/sdl/go.mod (2 hunks)
  • go/util/jwt/jwt_test.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
go/sdl/cmd/generate-sdl-fixtures/main.go (2)
go/sdl/sdl.go (1)
  • ReadFile (83-89)
go/testutil/deployment.go (1)
  • DeploymentGroups (54-62)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: proto
  • GitHub Check: test
  • GitHub Check: coverage
  • GitHub Check: sdl-parity
  • GitHub Check: go
🔇 Additional comments (3)
go/provider/client/client.go (1)

99-103: LGTM!

Clean stylistic improvement. Omitting names for unused receiver and parameters is idiomatic Go and equivalent to using _ placeholders.

go/util/jwt/jwt_test.go (1)

189-189: LGTM! Clean simplification.

The variadic append is more idiomatic and concise than the previous nested loop approach while preserving identical behavior.

go/sdl/go.mod (1)

8-8: Consider the maintenance posture of gojsonschema and its dependencies.

The gojsonschema v1.2.0 dependency is Go 1.25-compatible. However, the library hasn't had a release since October 2019 (5+ years), and its transitive dependencies (gojsonpointer and gojsonreference) were last maintained in January 2018 and are now effectively abandoned. While no CVEs are published, the lack of maintenance creates a future security posture risk. If production use requires more actively-maintained alternatives, consider evaluating other JSON schema validators like google/jsonschema-go or santhosh-tekuri/jsonschema.

Also applies to: 13-13, 166-167

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8e4864a and 357719f.

⛔ Files ignored due to path filters (1)
  • go/cli/go.sum is excluded by !**/*.sum
📒 Files selected for processing (1)
  • go/cli/go.mod (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: go
  • GitHub Check: proto
  • GitHub Check: sdl-parity
  • GitHub Check: coverage
  • GitHub Check: test

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
go/sdl/sdl-input.schema.yaml (1)

259-262: Inconsistent indentation for accept property.

The accept property's children have 8 extra spaces of indentation compared to sibling properties (as, port, proto). While valid YAML, this visual inconsistency may cause confusion during maintenance.

              accept:
-                    items:
-                      type: string
-                    type: array
+                items:
+                  type: string
+                type: array
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c28073f and c6f2a2b.

📒 Files selected for processing (4)
  • go/sdl/schema_validator.go (1 hunks)
  • go/sdl/sdl-input.schema.yaml (1 hunks)
  • make/test.mk (1 hunks)
  • ts/src/sdl/SDL/SDL.ts (19 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • make/test.mk
🧰 Additional context used
🧬 Code graph analysis (1)
ts/src/sdl/SDL/SDL.ts (3)
ts/src/sdl/index.ts (1)
  • SdlValidationError (28-28)
ts/src/sdl/types.ts (5)
  • v2Sdl (93-98)
  • v3Sdl (100-105)
  • v3Manifest (4-4)
  • v3ComputeResources (218-224)
  • v3DeploymentGroup (270-285)
ts/src/network/index.ts (1)
  • NetworkId (2-2)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: go
  • GitHub Check: proto
  • GitHub Check: coverage
  • GitHub Check: sdl-parity
  • GitHub Check: test
🔇 Additional comments (8)
go/sdl/sdl-input.schema.yaml (1)

1-390: LGTM! Comprehensive schema for SDL input validation.

The schema covers all essential SDL structures with appropriate constraints including version enumeration, port validation, GPU vendor/model configuration, and storage attribute handling. The documented separation of structural (schema) vs semantic (Go runtime) validation is a good approach.

go/sdl/schema_validator.go (1)

1-203: LGTM! Well-structured schema validation implementation.

The implementation addresses previous review concerns:

  • Thread-safe logger access with sync.RWMutex
  • Robust fallback chain (embedded → env var → file system) with actionable error messages
  • Compile-time interface assertion for noOpLogger
  • Lazy, one-time schema compilation with sync.Once
ts/src/sdl/SDL/SDL.ts (6)

249-318: Approve Go format conversion logic.

The convertToGoFormat method handles the necessary transformations for Go output parity:

  • Key name conversions (camelCase ↔ snake_case)
  • Special case handling (HTTPOptions → httpOptions, quantity → size)
  • Empty allOf/anyOf arrays converted to null
  • Recursive processing with undefined filtering

359-373: LGTM! Validation methods for service image and ports.

The new validations enforce:

  • Non-empty, non-whitespace-only image names
  • Port values in the valid range (1-65535)

These complement the schema-level validations and provide clearer runtime errors.


1192-1194: v2Groups doesn't receive format parameter.

The groups() method passes format to v3Groups but not to v2Groups. If Go-format output is needed for v2 deployments, this will silently use TypeScript format instead.

If this is intentional (v2 is legacy and doesn't need Go parity), consider adding a comment. Otherwise, v2Groups should accept and use the format parameter.

  groups(format: OutputFormat = "typescript") {
-   return this.version === "beta2" ? this.v2Groups() : this.v3Groups(format);
+   return this.version === "beta2" ? this.v2Groups(format) : this.v3Groups(format);
  }

1030-1063: Approve rewritten endpoint sequence computation.

The implementation correctly:

  • Collects all IPs (including duplicates) to match Go behavior
  • Skips global endpoints without IP
  • Sorts for stable, deterministic ordering
  • Assigns incrementing sequence numbers

The approach intentionally matches Go's behavior where duplicate IPs result in later assignments overwriting earlier ones.


1172-1181: Approve default GPU handling for Go parity.

Always including GPU with units "0" when not specified matches Go's output behavior. The comment clearly documents this design decision.


1212-1219: Approve amount formatting with 18 decimal precision.

Using toFixed(18) for numeric amounts matches Go's precision requirements for parity testing. The fallback to toString() or "0" handles edge cases appropriately.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
go/cli/go.mod (1)

215-216: Remove transitive xeipuuv dependencies from go/cli/go.mod.

This is a duplicate of the previous review feedback: these packages are only used in go/sdl (schema_validator.go, parity_test.go) and are transitive through the direct pkg.akt.dev/go/sdl => ../sdl dependency. Remove lines 215–216 and run go mod tidy to restore only truly necessary dependencies.

🧹 Nitpick comments (2)
ts/src/sdl/SDL/SDL.ts (2)

1000-1012: Update JSDoc to document the format parameter.

The implementation correctly supports format selection, but the JSDoc comment for v3Manifest should document the new format parameter (purpose, valid values, default).


1192-1194: Update JSDoc for the format parameter.

The format parameter support is well-implemented with consistent threading through the methods and proper 18-decimal precision for amount values. Consider updating the JSDoc comments for groups() and v3Groups() to document the new format parameter.

Also applies to: 1196-1289

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c6f2a2b and 331a50c.

📒 Files selected for processing (2)
  • go/cli/go.mod (1 hunks)
  • ts/src/sdl/SDL/SDL.ts (19 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: sdl-parity
  • GitHub Check: coverage
  • GitHub Check: test
  • GitHub Check: proto
  • GitHub Check: go
🔇 Additional comments (9)
ts/src/sdl/SDL/SDL.ts (9)

2-2: LGTM!

The switch to js-yaml's load function and the addition of the OutputFormat type are both clean and appropriate for supporting Go/TypeScript output parity.

Also applies to: 57-57


114-125: LGTM!

The null/undefined guard and version-based casting correctly address the previous review comments. The parsing logic is now more robust and type-safe.


359-373: LGTM!

The new validation methods correctly enforce non-empty image names and valid port ranges (1-65535), addressing the previous review comments about adding runtime validation.


908-920: LGTM!

The deterministic sorting of storage keys and explicit null handling ensure consistent, predictable output for parity testing. The changes maintain backward compatibility while improving clarity.

Also applies to: 922-938, 973-973, 986-988


1082-1099: LGTM!

The dynamic key selection (quantity vs size) based on the asString parameter correctly supports format compatibility and is consistent with the convertToGoFormat logic.

Also applies to: 1101-1112


1172-1181: LGTM!

Always including the GPU field (defaulting to units "0" when absent) ensures consistent structure and matches Go behavior as documented in the comment.


1307-1315: LGTM!

The 18-decimal precision formatting is consistent with v3Groups and ensures Go-compatible numeric representation.


1030-1063: Review comment is incorrect—TypeScript implementation properly matches Go behavior.

The Go implementation in go/sdl/v2.go (lines 519–548) and go/sdl/v2_1.go (lines 332–373) demonstrates that duplicate endpoint IPs are intentionally collected and allowed to overwrite in the result map. The assignment loop iterates through the sorted endpointNames array (which contains duplicates) and assigns sequence numbers without deduplication:

for _, name := range endpointNames {
    endpointSeqNumber++
    seqNo := endpointSeqNumber
    res[name] = seqNo
}

This produces the exact same behavior as the TypeScript code—duplicates overwrite previous assignments. The TypeScript implementation at lines 1054–1060 correctly mirrors this Go behavior. No changes are required.

Likely an incorrect or invalid review comment.


249-318: Parity tests already verify the conversion logic—no action needed unless refactoring is desired.

The convertToGoFormat method is already covered by comprehensive parity tests (make test-sdl-parity) that compare TypeScript output against Go fixtures for v2.0 and v2.1 SDLs. The field name conversions are correct and match the Go struct JSON serialization (e.g., sequenceNumbersequence_number, while endpointSequenceNumber and externalPort remain unchanged per their Go struct JSON tags).

If maintainability is a concern, extracting a convertFieldName helper could reduce the 70-line method's complexity, but this is optional and not required for correctness.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (4)
ts/src/sdl/SDL/parity.spec.ts (2)

23-36: Consider caching compiled schemas for better performance.

The validateAgainstSchema function compiles the schema on every call. For repeated validations against the same schema (e.g., inputSchemaPath), consider caching the compiled validators.

+const schemaCache = new Map<string, ReturnType<typeof ajv.compile>>();
+
 function validateAgainstSchema(name: string, data: unknown, schemaPath: string): void {
-  const schemaContent = fs.readFileSync(schemaPath, "utf8");
-  const schema = load(schemaContent);
-  const validate = ajv.compile(schema as Record<string, unknown>);
+  let validate = schemaCache.get(schemaPath);
+  if (!validate) {
+    const schemaContent = fs.readFileSync(schemaPath, "utf8");
+    const schema = load(schemaContent);
+    validate = ajv.compile(schema as Record<string, unknown>);
+    schemaCache.set(schemaPath, validate);
+  }
   const valid = validate(data);

125-143: Silent skip when invalid fixtures directory is missing.

Unlike the Go implementation which uses t.Skip() with a message, this silently returns when the directory doesn't exist. Consider adding visibility for debugging:

   describe("invalid SDLs rejected", () => {
     const invalidDir = path.join(fixturesRoot, "invalid");
 
     if (!fs.existsSync(invalidDir)) {
+      it.skip("Invalid fixtures directory does not exist yet", () => {});
       return;
     }
go/sdl/parity_test.go (1)

161-171: Consider filtering by file extension in invalid SDL tests.

The loop processes all non-directory entries. If non-YAML files (e.g., .gitkeep, README) exist in the invalid directory, they would be tested and likely fail unexpectedly.

 	for _, entry := range entries {
 		if entry.IsDir() {
 			continue
 		}
+		if !strings.HasSuffix(entry.Name(), ".yaml") && !strings.HasSuffix(entry.Name(), ".yml") {
+			continue
+		}
 
 		fixturePath := filepath.Join(invalidDir, entry.Name())

Note: Add "strings" to imports if applying this change.

go/sdl/schema_validator.go (1)

122-133: Improve schema validation error formatting for readability (optional)

Right now all schema errors are rendered via %v on a string slice, which tends to produce a single dense line. Joining them with newlines or commas would make logs and test failures easier to read, especially for complex SDLs.

-	if !result.Valid() {
-		var errors []string
-		for _, desc := range result.Errors() {
-			errors = append(errors, desc.String())
-		}
-		return fmt.Errorf("schema validation failed: %v", errors)
-	}
+	if !result.Valid() {
+		var msgs []string
+		for _, desc := range result.Errors() {
+			msgs = append(msgs, desc.String())
+		}
+		return fmt.Errorf("schema validation failed:\n - %s", strings.Join(msgs, "\n - "))
+	}

(Remember to add strings to the imports if you adopt this.)

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d399ef9 and ba8c189.

📒 Files selected for processing (21)
  • go/sdl/parity_test.go (1 hunks)
  • go/sdl/schema_validator.go (1 hunks)
  • make/test.mk (1 hunks)
  • testdata/sdl/invalid/credentials-missing-host.yaml (1 hunks)
  • testdata/sdl/invalid/endpoint-not-used.yaml (1 hunks)
  • testdata/sdl/invalid/invalid-port.yaml (1 hunks)
  • testdata/sdl/invalid/missing-deployment.yaml (1 hunks)
  • testdata/sdl/invalid/missing-image.yaml (1 hunks)
  • testdata/sdl/invalid/missing-version.yaml (1 hunks)
  • testdata/sdl/invalid/negative-cpu.yaml (1 hunks)
  • testdata/sdl/invalid/persistent-without-mount.yaml (1 hunks)
  • testdata/sdl/v2.0/gpu-basic/input.yaml (1 hunks)
  • testdata/sdl/v2.0/http-options/input.yaml (1 hunks)
  • testdata/sdl/v2.0/ip-endpoint/input.yaml (1 hunks)
  • testdata/sdl/v2.0/multiple-services/input.yaml (1 hunks)
  • testdata/sdl/v2.0/persistent-storage/input.yaml (1 hunks)
  • testdata/sdl/v2.0/simple/input.yaml (1 hunks)
  • testdata/sdl/v2.0/storage-classes/input.yaml (1 hunks)
  • testdata/sdl/v2.1/credentials/input.yaml (1 hunks)
  • testdata/sdl/v2.1/ip-endpoint/input.yaml (1 hunks)
  • ts/src/sdl/SDL/parity.spec.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • testdata/sdl/invalid/invalid-port.yaml
🚧 Files skipped from review as they are similar to previous changes (9)
  • testdata/sdl/invalid/missing-image.yaml
  • testdata/sdl/v2.1/credentials/input.yaml
  • testdata/sdl/invalid/missing-version.yaml
  • testdata/sdl/invalid/persistent-without-mount.yaml
  • testdata/sdl/v2.1/ip-endpoint/input.yaml
  • testdata/sdl/v2.0/http-options/input.yaml
  • testdata/sdl/invalid/negative-cpu.yaml
  • testdata/sdl/v2.0/storage-classes/input.yaml
  • testdata/sdl/v2.0/ip-endpoint/input.yaml
🧰 Additional context used
🧬 Code graph analysis (2)
go/sdl/parity_test.go (2)
go/sdl/sdl.go (1)
  • ReadFile (83-89)
go/testutil/deployment.go (1)
  • DeploymentGroups (54-62)
ts/src/sdl/SDL/parity.spec.ts (1)
ts/src/sdl/SDL/SDL.ts (1)
  • SDL (89-1430)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: coverage
  • GitHub Check: sdl-parity
  • GitHub Check: test
  • GitHub Check: go
🔇 Additional comments (16)
testdata/sdl/invalid/endpoint-not-used.yaml (1)

32-34: Test fixture correctly represents the endpoint-not-used invalid case.

The endpoint myendpoint is properly defined but intentionally not referenced by any service or deployment, making this a valid test case for validating SDL rejection of unused endpoints.

testdata/sdl/v2.0/gpu-basic/input.yaml (3)

12-27: GPU resource specification looks correct.

The gpu-compute profile properly defines:

  • CPU units in millicores (1000m = 1 core)
  • Memory and storage in standard units (Gi)
  • GPU count with vendor/model attributes (NVIDIA RTX3080)

This is a realistic test case for GPU workloads.


1-1: Schema reference path is correct and schema file exists.

The yaml-language-server directive at line 1 correctly references the SDL input schema. The schema file exists at go/sdl/sdl-input.schema.yaml, and the relative path ../../../../go/sdl/sdl-input.schema.yaml from this fixture location properly resolves to it.


3-43: Test fixture is properly paired and schema-compliant.

The fixture structure is well-formed and properly organized. Verification confirms:

  • manifest.json exists and validates against specs/sdl/manifest-output.schema.yaml
  • groups.json exists and validates against specs/sdl/groups-output.schema.yaml
  • ✓ GPU configuration (RTX3080, 1 unit) correctly maps from input to both outputs
  • ✓ Resource specifications (CPU: 1000m, Memory: 2Gi, Storage: 10Gi) accurately converted
  • ✓ Placement attributes and pricing preserved in generated outputs

The AI-generated test data is accurate and ready for parity testing.

testdata/sdl/v2.0/persistent-storage/input.yaml (1)

1-48: Well-structured persistent storage fixture.

The SDL v2.0 persistent storage test fixture correctly demonstrates a database service with persistent volume, placement constraints, and signedBy policies. The hardcoded POSTGRES_PASSWORD=secret is acceptable for test fixture purposes.

testdata/sdl/invalid/missing-deployment.yaml (1)

1-27: Valid invalid-SDL fixture for missing deployment case.

The fixture correctly omits the deployment section while providing valid services and profiles, which should trigger validation errors in both implementations.

make/test.mk (1)

50-63: Well-structured Makefile targets for SDL parity testing.

The targets establish a clear workflow: generate-sdl-fixtures creates the expected outputs, then test-sdl-parity runs both Go and TypeScript tests against them. The dependency on $(AKASH_TS_NODE_MODULES) ensures npm dependencies are available.

testdata/sdl/v2.0/simple/input.yaml (1)

1-47: Good coverage of multiple expose rules and signedBy constraints.

The fixture exercises both TCP and UDP expose rules, hostname acceptance, and signedBy constraints with anyOf and allOf. The placeholder values for signedBy are acceptable for testing structural parity.

testdata/sdl/invalid/credentials-missing-host.yaml (1)

1-35: Valid invalid-SDL fixture for missing credentials host.

The fixture correctly defines a credentials block with username and password but omits the required host field, which should trigger validation errors in both implementations.

ts/src/sdl/SDL/parity.spec.ts (2)

72-97: v2.0 parity tests are well-structured.

The test flow correctly validates input against schema, parses with beta2, compares manifest/groups against fixtures, and validates outputs against their schemas. The use of toEqual for deep comparison is appropriate.


99-123: v2.1 parity tests mirror v2.0 structure correctly.

The v2.1 tests use beta3 for parsing, matching the expected version-to-network mapping. The structure is consistent with v2.0 tests.

go/sdl/parity_test.go (3)

19-27: Good use of sync.Once for schema caching.

Caching the compiled schemas with sync.Once ensures schemas are compiled only once across all test runs, improving performance without sacrificing thread safety.


37-67: testParity logic is comprehensive and well-structured.

The function correctly iterates fixtures, validates existence of required files, and runs both schema validation and fixture comparison as subtests. The t.Fatalf for missing fixtures provides clear actionable guidance.


118-149: Fixture validation uses proper JSON unmarshaling for comparison.

Unmarshaling both expected and actual to any before comparison ensures JSON equivalence regardless of Go struct field ordering, which is correct for parity testing.

testdata/sdl/v2.0/multiple-services/input.yaml (1)

3-92: Multi‑service SDL fixture looks consistent and realistic

The SDL models a clear web→api→db dependency chain, scoped exposure rules, and distinct compute/placement/deployment profiles, which is ideal for exercising parity between implementations. I don’t see structural issues in this fixture for the stated test purpose.

go/sdl/schema_validator.go (1)

14-88: Embedded schema + lazy compilation design looks solid

Using go:embed with a byte slice and compiling the YAML schema once via sync.Once is clean and thread-safe. Error paths for missing/invalid schema (load, YAML parse, JSON marshal, compile) are preserved via schemaCompileError, so callers see a stable failure once initialization fails. No concurrency or correctness issues spotted in this part.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
ts/src/sdl/SDL/SDL.ts (1)

1030-1063: Duplicate IPs cause sequence number assignments to be overwritten with non-contiguous numbering.

The Go implementation has identical logic without deduplication. When the same IP appears multiple times in the sorted array, successive assignments overwrite previous ones, resulting in gaps in sequence numbers. For example, with duplicates ["10.0.0.1", "10.0.0.1", "10.0.0.2"], the final map would be {"10.0.0.1": 2, "10.0.0.2": 3} instead of the intended {"10.0.0.1": 1, "10.0.0.2": 2}.

Apply deduplication before assignment:

-    // Make the assignment stable (matching Go implementation)
-    endpointNames.sort();
+    // Make the assignment stable with deduplication
+    const uniqueNames = [...new Set(endpointNames)].sort();

     // Assign sequence numbers: start at 0, so first is 1 (matching Go)
     const res: Record<string, number> = {};
     let endpointSeqNumber = 0;
-    for (const name of endpointNames) {
+    for (const name of uniqueNames) {
       endpointSeqNumber++;
       res[name] = endpointSeqNumber;
     }
🧹 Nitpick comments (1)
ts/src/sdl/SDL/parity.spec.ts (1)

13-14: Replace any with more specific type for Ajv validator.

Static analysis flagged the use of any in the Ajv type cast. Use a more specific type for better type safety.

Apply this change:

-const Ajv = AjvModule as typeof AjvModule & { new (options?: { allErrors?: boolean }): any };
+const Ajv = AjvModule as typeof AjvModule & { new (options?: { allErrors?: boolean }): AjvModule };
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ba8c189 and c0e07c4.

📒 Files selected for processing (13)
  • go/sdl/schema_validator.go (1 hunks)
  • testdata/sdl/v2.0/placement/groups.json (1 hunks)
  • testdata/sdl/v2.0/placement/input.yaml (1 hunks)
  • testdata/sdl/v2.0/placement/manifest.json (1 hunks)
  • testdata/sdl/v2.0/port-ranges/groups.json (1 hunks)
  • testdata/sdl/v2.0/port-ranges/input.yaml (1 hunks)
  • testdata/sdl/v2.0/port-ranges/manifest.json (1 hunks)
  • testdata/sdl/v2.0/pricing/groups.json (1 hunks)
  • testdata/sdl/v2.0/pricing/input.yaml (1 hunks)
  • testdata/sdl/v2.0/pricing/manifest.json (1 hunks)
  • ts/src/sdl/SDL/SDL.ts (18 hunks)
  • ts/src/sdl/SDL/parity.spec.ts (1 hunks)
  • ts/src/sdl/types.ts (1 hunks)
✅ Files skipped from review due to trivial changes (3)
  • testdata/sdl/v2.0/placement/groups.json
  • testdata/sdl/v2.0/port-ranges/manifest.json
  • testdata/sdl/v2.0/pricing/groups.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • go/sdl/schema_validator.go
🧰 Additional context used
🧬 Code graph analysis (2)
ts/src/sdl/SDL/parity.spec.ts (2)
ts/src/sdk/provider/auth/jwt/validate-payload.ts (1)
  • schema (8-394)
ts/src/sdl/SDL/SDL.ts (5)
  • validate (131-141)
  • validate (320-335)
  • SDL (89-1427)
  • manifest (1014-1016)
  • groups (1192-1194)
ts/src/sdl/SDL/SDL.ts (1)
ts/src/sdl/types.ts (5)
  • v2Sdl (93-98)
  • v3Sdl (100-105)
  • v3Manifest (4-4)
  • v3ComputeResources (218-224)
  • v3DeploymentGroup (270-285)
🪛 GitHub Check: ts
ts/src/sdl/SDL/parity.spec.ts

[failure] 13-13:
Unexpected any. Specify a different type

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: coverage
  • GitHub Check: test
  • GitHub Check: sdl-parity
  • GitHub Check: proto
  • GitHub Check: go
🔇 Additional comments (17)
testdata/sdl/v2.0/placement/input.yaml (2)

1-1: No issues found. The schema reference path ../../../../go/sdl/sdl-input.schema.yaml correctly resolves from the fixture location to go/sdl/sdl-input.schema.yaml, and the file exists with proper permissions. IDE YAML validation will work as intended.


12-32: Profile and pricing configuration is already validated by the parity test.

The placement fixture is automatically tested by TestParityV2_0() in parity_test.go, which performs deep equality comparison between the generated manifest and groups against the expected fixture files. This covers all the verification points mentioned: the anyOf constraint in signedBy, the pricing denom uakt and amount 1000, resource specifications, and count consistency. All values in input.yaml correctly match groups.json and manifest.json.

testdata/sdl/v2.0/placement/manifest.json (2)

1-70: Confirm AI-generated test fixture accuracy.

Per the PR description, this test data was generated using AI. While the JSON structure appears valid and memory/storage byte conversions are correct (512 MiB → 536870912 bytes), the CPU unit transformation and overall parity with the corresponding input.yaml and groups.json requires manual validation to ensure test expectations are accurate.

#!/bin/bash
# Verify schema validation and fixture consistency

# Check if the schema file referenced in input.yaml exists
echo "=== Checking schema file existence ==="
SCHEMA_PATH="go/sdl/sdl-input.schema.yaml"
if [ -f "$SCHEMA_PATH" ]; then
  echo "Schema found at: $SCHEMA_PATH"
  echo "Schema exists: YES"
else
  echo "Schema not found at: $SCHEMA_PATH"
  echo "This may cause IDE validation errors in input.yaml"
fi

# Verify JSON validity of manifest
echo -e "\n=== Validating manifest.json structure ==="
if command -v jq &> /dev/null; then
  if jq empty testdata/sdl/v2.0/placement/manifest.json 2>/dev/null; then
    echo "Manifest JSON is valid"
  else
    echo "Manifest JSON has validation errors"
    jq empty testdata/sdl/v2.0/placement/manifest.json
  fi
fi

13-16: No unit conversion issue detected.

The CPU value "500m" in input.yaml is correctly converted to "500" in the manifest. Akash's cpuQuantity parser strips the "m" suffix and stores the integer value directly, where each unit represents 1/1000 of a CPU (so 500 units = 0.5 cores). This matches the standard Kubernetes milliCPU notation and is consistent across all test fixtures.

ts/src/sdl/types.ts (1)

279-283: LGTM! Nullable types align with schema and Go output.

The nullable adjustments for attributes, allOf, and anyOf correctly match the Go implementation's behavior and the JSON schemas defined in specs/sdl. This ensures type safety when these fields are absent or explicitly null.

testdata/sdl/v2.0/pricing/input.yaml (1)

1-41: LGTM! Valid SDL v2.0 pricing test fixture.

The fixture correctly defines a two-provider deployment scenario with different pricing configurations. The schema reference enables IDE validation, and the structure aligns with v2.0 SDL specifications.

testdata/sdl/v2.0/port-ranges/input.yaml (1)

1-43: LGTM! Valid port-ranges test fixture.

The fixture correctly defines multiple port exposures including TCP and UDP protocols, providing good coverage for port-range scenarios in parity testing.

testdata/sdl/v2.0/pricing/manifest.json (1)

1-138: LGTM! Valid manifest fixture for pricing test.

The fixture correctly represents the expected manifest output for a two-provider deployment, with both providers exposing identical service configurations. The structure is consistent with the manifest-output.schema.yaml.

ts/src/sdl/SDL/SDL.ts (7)

114-125: LGTM! Version-aware type casting correctly implemented.

The addition of the null/undefined check and version-based casting to v2Sdl or v3Sdl properly addresses previous review comments. This ensures type consistency between the parsed data and the internal representation.


249-318: LGTM! Comprehensive Go format conversion implemented.

The convertToGoFormat method correctly handles the transformations needed for Go parity:

  • Recursive conversion of nested structures
  • Key name normalization (camelCase ↔ snake_case)
  • Empty array → null conversion for signedBy fields
  • GPU field preservation matching Go behavior

359-373: LGTM! Essential service validations added.

The new validateServiceImage and validateServicePorts methods add critical validation:

  • Ensuring non-empty image names prevents runtime failures
  • Port range validation (1-65535) prevents invalid service configurations

908-938: LGTM! Deterministic storage key ordering implemented.

The sorting of storage keys in both v2 and v3 manifest service params ensures consistent, reproducible output across runs. This is essential for manifest version hashing and reliable parity testing.


1000-1012: LGTM! Format parameter enables Go parity while preserving compatibility.

The addition of the format parameter with a default value of "typescript" maintains backward compatibility for existing consumers while enabling Go-format output for parity tests. The conditional conversion at the end ensures efficient processing.


1172-1181: LGTM! GPU field always included matching Go behavior.

The implementation correctly ensures GPU is always present in groups output with units defaulting to "0" when not specified. This matches the Go implementation's behavior and is necessary for schema conformance.


1212-1220: LGTM! Consistent 18-decimal precision for amounts.

Both v3Groups and v2Groups correctly format amounts with 18 decimal places using toFixed(18), matching Go's decimal precision. This ensures consistent numeric representation across implementations and prevents floating-point precision issues in parity tests.

Also applies to: 1306-1314

testdata/sdl/v2.0/port-ranges/groups.json (1)

38-50: The duplicate endpoint entries are intentional and correct. The input defines three ports (8080 TCP, 10000 UDP, 20000 UDP), and the groups.json output correctly includes three endpoint entries—one for each port. The two UDP ports both have kind: 1, but they are separate endpoint entries because they represent distinct port exposures. This matches the behavior of the authoritative Go implementation (sdl.DeploymentGroups()), confirming the fixture is accurately generated.

ts/src/sdl/SDL/parity.spec.ts (1)

85-97: The version parameter controls SDL parsing but not manifest/groups output format.

Lines 90-91 always call sdl.v3Manifest(true, "go") and sdl.v3Groups("go") regardless of the version parameter ("beta2" | "beta3"). While the version parameter correctly controls SDL creation via SDL.fromString(inputBytes, version), the output methods bypass version-aware dispatch.

This behavior appears intentional because:

  • Go format is a v3-only feature (v2Manifest lacks format parameter and convertToGoFormat)
  • The groups() method supports format parameter but manifest() does not, creating asymmetry

However, this design means v2.0 (beta2) fixtures are output as v3 format, which may warrant clarification:

  • Is this intentional for parity testing?
  • Or should the test use version-aware dispatch (sdl.manifest(true) and sdl.groups("go") when available)?

Consider adding a comment explaining why v3 output is always used for both beta2 and beta3 fixtures despite the version parameter being provided.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
ts/src/sdl/SDL/parity.spec.ts (2)

13-14: Consider simplifying AJV instantiation.

The double cast through unknown is verbose. A common pattern for handling ESM/CJS interop:

const Ajv = (AjvModule as { default?: typeof AjvModule }).default || AjvModule;
const ajv = new Ajv({ allErrors: true });

This avoids the @ts-expect-error and is more readable.


85-97: sdl is returned but unused by callers.

validateFixtures only destructures { manifest, groups }. Either remove sdl from the return object or use it in the caller.

-  return { sdl, manifest, groups };
+  return { manifest, groups };
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c0e07c4 and a083463.

📒 Files selected for processing (2)
  • go/sdl/schema_validator.go (1 hunks)
  • ts/src/sdl/SDL/parity.spec.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: test
  • GitHub Check: sdl-parity
  • GitHub Check: coverage
  • GitHub Check: proto
  • GitHub Check: go
🔇 Additional comments (12)
go/sdl/schema_validator.go (8)

14-19: Excellent use of embed for schema portability.

The compile-time embedding approach eliminates the previous runtime path resolution fragility and makes binaries fully portable. The documentation clearly explains the benefits.


21-31: Well-designed validator with proper synchronization.

The combination of sync.Once for lazy compilation, RWMutex for logger access, and a no-op logger default ensures thread-safe operation with minimal contention.


33-54: Thread-safe logger injection with nil safety.

All previous concerns addressed: the mutex ensures thread safety, nil loggers are safely handled, and the documentation clearly explains usage and thread-safety guarantees.


56-63: LGTM: Straightforward embedded schema access.

The empty-check provides a clear error if embedding fails, though in practice the build would fail if the file were missing.


65-94: Solid lazy compilation with comprehensive error handling.

The sync.Once pattern ensures thread-safe, one-time compilation. The YAML→JSON conversion via intermediate Go data structures is the standard approach for gojsonschema, and error caching is appropriate since the embedded schema cannot change at runtime.


96-106: Complete no-op logger implementation with interface assertion.

The compile-time assertion at line 106 (addressing a previous comment) ensures interface compliance, and the no-op behavior is correctly implemented throughout.


108-142: Standard validation pipeline with clear error reporting.

The YAML→JSON conversion correctly handles arbitrary input structures, and validation errors are comprehensively collected and formatted for actionable feedback.


144-172: Effective mismatch detection for observability.

The logic correctly identifies divergence between schema and Go validation, using read locks for performant concurrent access. The asymmetric field naming (schema_error vs go_error) appropriately highlights which validator failed.

ts/src/sdl/SDL/parity.spec.ts (4)

1-8: LGTM!

Imports are appropriate for the parity test requirements.


16-36: LGTM!

Clean interface definition and effective schema caching pattern.


51-83: LGTM!

Good error messages with actionable hints for missing fixtures.


99-108: LGTM!

Clean fixture validation with proper deep equality comparison.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
ts/src/sdl/SDL/parity.spec.ts (1)

123-140: LGTM! Test organization is clear and addresses previous feedback.

The explicit it.skip when the invalid fixtures directory is missing properly addresses the previous review concern about silent failures. The test structure is clean and maintainable.

One optional enhancement: The invalid SDL tests currently expect any throw (line 137). If specific error types or messages are important for parity, consider asserting on SdlValidationError specifically. However, for broad rejection testing, the current approach is acceptable.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a083463 and bd7411b.

⛔ Files ignored due to path filters (1)
  • ts/src/sdl/SDL/__snapshots__/SDL.spec.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (3)
  • ts/src/sdl/SDL/SDL.spec.ts (10 hunks)
  • ts/src/sdl/SDL/parity.spec.ts (1 hunks)
  • ts/test/fixtures/sdl/groups-basic-snapshot.json (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
ts/src/sdl/SDL/parity.spec.ts (1)
ts/src/sdl/SDL/SDL.ts (5)
  • validate (131-141)
  • validate (320-335)
  • SDL (89-1427)
  • manifest (1014-1016)
  • groups (1192-1194)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: proto
  • GitHub Check: test
  • GitHub Check: sdl-parity
  • GitHub Check: go
  • GitHub Check: coverage
🔇 Additional comments (6)
ts/src/sdl/SDL/SDL.spec.ts (1)

613-682: LGTM! Test expectations updated consistently.

The changes correctly update test expectations to match the new SDL schema:

  • Field names: quantitysize for memory and storage
  • Added GPU blocks with zero units for completeness
  • Price precision increased to 18 decimal places
  • allOf set to null instead of empty array

These changes are consistent with the type system updates described in the PR.

ts/test/fixtures/sdl/groups-basic-snapshot.json (1)

1-63: LGTM! Fixture updated to match new schema.

The snapshot correctly reflects the updated SDL output format:

  • Empty arrays changed to null for attributes, allOf, and anyOf (semantically clearer)
  • GPU units simplified from object representation to string "0"
  • Field renames: quantitysize for memory
  • Price precision increased to 18 decimal places

All changes are consistent with the schema updates across the codebase.

ts/src/sdl/SDL/parity.spec.ts (4)

23-49: LGTM! Well-implemented schema validation with caching.

The schema compilation caching is a good performance optimization, and the error formatting provides clear diagnostics with path and keyword information. The fallback to "(root)" for empty instance paths properly addresses the previous review feedback.


51-83: LGTM! Robust fixture loading with clear error messages.

The function properly validates fixture structure and provides actionable error messages (e.g., suggesting make generate-sdl-fixtures when files are missing). This will prevent silent failures in CI and help developers diagnose issues quickly.


85-108: LGTM! Clean separation of validation concerns.

The two-function approach cleanly separates:

  1. Schema validation: Ensures input/output conform to JSON schemas
  2. Parity validation: Ensures TS implementation matches Go expectations

The deliberate lack of error handling in validateSchemas allows SDL parsing errors to surface naturally, which is appropriate for these tests.


13-14: Documentation for AJV instantiation is already present; no action needed.

The code includes an explanatory comment (@ts-expect-error - AjvModule has non-standard export, cast needed for instantiation) that adequately documents why the complex type casting is necessary. This pattern is a standard workaround for AJV v8.12.0's CommonJS/ESM interop behavior and is the sole instance in the codebase. No additional changes required.

@vertex451 vertex451 force-pushed the artem/sdl-parity-tests branch from bd7411b to eb3c877 Compare December 9, 2025 15:27
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
ts/src/sdl/SDL/SDL.ts (1)

1196-1288: Critical bug in v3Groups: endpoints property accessed at wrong nesting level and uses += for array concatenation

The aggregation path (line 1278) incorrectly accesses group!.dgroup.resources[location].endpoints, but endpoints are actually nested at group!.dgroup.resources[location].resource.endpoints. This is evident from the creation path (line 1267) where the resource object is wrapped as { resource: res, ... } and res.endpoints was set at line 1260.

Additionally, using the += operator on arrays causes string concatenation instead of merging. For example, concatenating an undefined or array value with the endpoints array produces a corrupted string like "undefined[object Object]" rather than properly combining arrays.

This bug corrupts endpoint data whenever multiple services share the same placement and profile. Update the aggregation logic to access the nested resource.endpoints property and use array spreading instead of +=:

        } else {
          const endpoints = this.v3ServiceResourceEndpoints(service) || [];
          const resource: any = group!.dgroup.resources[location].resource;
          const existing = (resource.endpoints as any[]) || [];

          group!.dgroup.resources[location].count += svcdepl.count;
          resource.endpoints = [...existing, ...endpoints];
        }
🧹 Nitpick comments (2)
go/node/client/v1beta3/tx.go (1)

535-551: Broadened sequence-resync trigger is good; consider also handling ErrInvalidSequence ABCI code

Expanding the syncSequence condition to resync when rErr matches either sdkerrors.ErrWrongSequence or sdkerrors.ErrInvalidSequence is a solid improvement and should reduce stuck sequence issues. The ABCI-code branch, however, still only checks txResp.Code == sdkerrors.ErrWrongSequence.ABCICode(). If Cosmos can surface ErrInvalidSequence via a distinct ABCI code, you may also want to treat that as a resync signal:

-	if (rErr != nil && (sdkerrors.ErrWrongSequence.Is(rErr) || sdkerrors.ErrInvalidSequence.Is(rErr))) || (valid && (txResp.Code == sdkerrors.ErrWrongSequence.ABCICode())) {
+	if (rErr != nil && (sdkerrors.ErrWrongSequence.Is(rErr) || sdkerrors.ErrInvalidSequence.Is(rErr))) ||
+		(valid && (txResp.Code == sdkerrors.ErrWrongSequence.ABCICode() || txResp.Code == sdkerrors.ErrInvalidSequence.ABCICode())) {

This is optional and only needed if such a code path actually exists.

It would be worth double-checking against the Cosmos SDK error definitions (and your existing tests) whether ErrInvalidSequence is ever surfaced via ABCI code in your flows.

go/sdl/sdl.go (1)

92-135: Schema validation is observational only; consider documenting fail‑open behavior

Read always runs validateInputAgainstSchema but never returns schemaErr—it only logs mismatches via checkSchemaValidationResult. This means production callers will accept SDLs that fail the schema as long as Go validation passes. That seems intentional but is a policy choice worth calling out (e.g., in package docs) so future changes don’t accidentally flip behavior to fail‑closed without explicit decision.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bd7411b and 92f1cb2.

⛔ Files ignored due to path filters (2)
  • go/cli/go.sum is excluded by !**/*.sum
  • ts/src/sdl/SDL/__snapshots__/SDL.spec.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (43)
  • go/cli/go.mod (1 hunks)
  • go/node/client/v1beta3/tx.go (1 hunks)
  • go/provider/client/client.go (1 hunks)
  • go/sdl/cmd/generate-sdl-fixtures/main.go (1 hunks)
  • go/sdl/go.mod (2 hunks)
  • go/sdl/parity_test.go (1 hunks)
  • go/sdl/schema_validator.go (1 hunks)
  • go/sdl/sdl-input.schema.yaml (1 hunks)
  • go/sdl/sdl.go (2 hunks)
  • go/util/jwt/jwt_test.go (1 hunks)
  • make/test.mk (1 hunks)
  • specs/sdl/README.md (1 hunks)
  • testdata/sdl/invalid/credentials-missing-host.yaml (1 hunks)
  • testdata/sdl/invalid/endpoint-not-used.yaml (1 hunks)
  • testdata/sdl/invalid/invalid-port.yaml (1 hunks)
  • testdata/sdl/invalid/missing-deployment.yaml (1 hunks)
  • testdata/sdl/invalid/missing-image.yaml (1 hunks)
  • testdata/sdl/invalid/missing-version.yaml (1 hunks)
  • testdata/sdl/invalid/negative-cpu.yaml (1 hunks)
  • testdata/sdl/invalid/persistent-without-mount.yaml (1 hunks)
  • testdata/sdl/v2.0/gpu-basic/input.yaml (1 hunks)
  • testdata/sdl/v2.0/http-options/input.yaml (1 hunks)
  • testdata/sdl/v2.0/ip-endpoint/input.yaml (1 hunks)
  • testdata/sdl/v2.0/multiple-services/input.yaml (1 hunks)
  • testdata/sdl/v2.0/persistent-storage/input.yaml (1 hunks)
  • testdata/sdl/v2.0/placement/groups.json (1 hunks)
  • testdata/sdl/v2.0/placement/input.yaml (1 hunks)
  • testdata/sdl/v2.0/placement/manifest.json (1 hunks)
  • testdata/sdl/v2.0/port-ranges/groups.json (1 hunks)
  • testdata/sdl/v2.0/port-ranges/input.yaml (1 hunks)
  • testdata/sdl/v2.0/port-ranges/manifest.json (1 hunks)
  • testdata/sdl/v2.0/pricing/groups.json (1 hunks)
  • testdata/sdl/v2.0/pricing/input.yaml (1 hunks)
  • testdata/sdl/v2.0/pricing/manifest.json (1 hunks)
  • testdata/sdl/v2.0/simple/input.yaml (1 hunks)
  • testdata/sdl/v2.0/storage-classes/input.yaml (1 hunks)
  • testdata/sdl/v2.1/credentials/input.yaml (1 hunks)
  • testdata/sdl/v2.1/ip-endpoint/input.yaml (1 hunks)
  • ts/src/sdl/SDL/SDL.spec.ts (10 hunks)
  • ts/src/sdl/SDL/SDL.ts (18 hunks)
  • ts/src/sdl/SDL/parity.spec.ts (1 hunks)
  • ts/src/sdl/types.ts (1 hunks)
  • ts/test/fixtures/sdl/groups-basic-snapshot.json (3 hunks)
✅ Files skipped from review due to trivial changes (1)
  • testdata/sdl/invalid/credentials-missing-host.yaml
🚧 Files skipped from review as they are similar to previous changes (25)
  • testdata/sdl/invalid/invalid-port.yaml
  • testdata/sdl/v2.0/port-ranges/manifest.json
  • ts/test/fixtures/sdl/groups-basic-snapshot.json
  • testdata/sdl/v2.0/placement/input.yaml
  • make/test.mk
  • go/sdl/cmd/generate-sdl-fixtures/main.go
  • testdata/sdl/v2.0/port-ranges/groups.json
  • ts/src/sdl/SDL/SDL.spec.ts
  • specs/sdl/README.md
  • testdata/sdl/invalid/missing-image.yaml
  • go/sdl/sdl-input.schema.yaml
  • testdata/sdl/v2.0/placement/groups.json
  • testdata/sdl/v2.1/ip-endpoint/input.yaml
  • testdata/sdl/v2.0/placement/manifest.json
  • go/cli/go.mod
  • testdata/sdl/v2.0/http-options/input.yaml
  • testdata/sdl/invalid/endpoint-not-used.yaml
  • testdata/sdl/invalid/negative-cpu.yaml
  • testdata/sdl/v2.0/pricing/manifest.json
  • testdata/sdl/v2.1/credentials/input.yaml
  • testdata/sdl/v2.0/simple/input.yaml
  • testdata/sdl/v2.0/storage-classes/input.yaml
  • testdata/sdl/v2.0/port-ranges/input.yaml
  • testdata/sdl/invalid/missing-deployment.yaml
  • testdata/sdl/v2.0/pricing/groups.json
🧰 Additional context used
🧬 Code graph analysis (3)
go/sdl/sdl.go (1)
go/testutil/deployment.go (1)
  • DeploymentGroups (54-62)
go/sdl/parity_test.go (3)
go/testutil/base.go (1)
  • Name (28-30)
go/sdl/sdl.go (1)
  • ReadFile (83-89)
go/testutil/deployment.go (1)
  • DeploymentGroups (54-62)
ts/src/sdl/SDL/parity.spec.ts (1)
ts/src/sdl/SDL/SDL.ts (5)
  • validate (131-141)
  • validate (320-335)
  • SDL (89-1427)
  • manifest (1014-1016)
  • groups (1192-1194)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: coverage
  • GitHub Check: test
  • GitHub Check: go
🔇 Additional comments (14)
go/util/jwt/jwt_test.go (1)

188-190: Slice expansion change is correct and simplifies control flow

Using append(res, s.initClaims(testCases[i])...) correctly flattens the returned []jwtTestCase into res and is equivalent to the previous manual loop over the slice, while being clearer and less error‑prone. No behavioral issues here.

testdata/sdl/v2.0/persistent-storage/input.yaml (1)

3-47: Well-structured SDL v2.0 test fixture.

The YAML configuration is syntactically correct and well-organized. It effectively demonstrates a practical persistent-storage scenario with:

  • Postgres service with port exposure and parameterized storage mounts
  • Compute profile with CPU, memory, and persistent volume declarations
  • Placement profile with regional attributes and pricing tiers
  • Deployment referencing the defined profiles

This is appropriate for parity testing.

go/sdl/go.mod (1)

7-16: gojsonschema/log additions look consistent with new SDL schema validation

Directly requiring cosmossdk.io/log and github.com/xeipuuv/gojsonschema plus pinning gojsonpointer/gojsonreference as indirects is aligned with the new schema validator usage and keeps versions explicit; nothing stands out as problematic.

Please ensure go mod tidy and CI’s module step remain clean after these changes and that there are no unexpected version conflicts with other Cosmos SDK components.

Also applies to: 166-167

testdata/sdl/v2.0/pricing/input.yaml (1)

1-41: Multi-provider pricing fixture looks structurally sound

The v2.0 SDL input is well-formed: shared services.app, a single profiles.compute.app-compute used by both placement.provider-a/provider-b pricing blocks, and matching entries under deployment.app. This should be a good parity case for cross-provider pricing differences.

testdata/sdl/v2.0/ip-endpoint/input.yaml (1)

1-49: IP endpoint fixture cleanly models multi-port, single-endpoint usage

This v2.0 SDL cleanly expresses two global TCP ports both bound to the same named IP endpoint (myendpoint), with matching endpoints.myendpoint.kind: ip and a plausible placement/signedBy section. It should exercise endpoint resolution and manifest/group generation nicely.

go/provider/client/client.go (1)

99-103: Receiver rename on defaultCertQuerier is purely cosmetic

Dropping the blank receiver name (_) from (*defaultCertQuerier) GetAccountCertificate(...) is a style-only change; method set and behavior are unchanged, and the default implementation still consistently reports on-chain certs as unavailable.

A quick go test ./... in modules that depend on this client should confirm there are no interface-satisfaction regressions around CertQuerier usage.

testdata/sdl/invalid/persistent-without-mount.yaml (1)

1-35: Invalid fixture correctly captures “persistent volume without service mount”

This SDL marks storage as persistent: true under profiles.compute.web.resources.storage but never declares a matching service-level mount, which is exactly the invalid scenario you want to exercise. It should be a useful negative test for both Go and TS SDL validators.

ts/src/sdl/types.ts (1)

270-285: Nullability for v3DeploymentGroup requirements matches schema/fixtures

Allowing requirements.attributes and requirements.signedBy.allOf/anyOf to be null better mirrors the actual group-output shape and schemas where these fields are explicitly nullable when no constraints exist. This should eliminate type/fixture mismatches; just ensure consuming code treats these as null-or-array rather than assuming [].

A quick tsc run and parity test pass (TS + Go) will confirm there are no remaining call sites that assume these properties are always arrays.

testdata/sdl/invalid/missing-version.yaml (1)

1-31: Missing-version invalid fixture is appropriate and minimal

This fixture omits the top-level version while keeping the rest of the SDL minimal but valid-looking, which is ideal for asserting that both implementations hard-reject SDLs without an explicit version.

testdata/sdl/v2.0/gpu-basic/input.yaml (1)

3-43: GPU v2.0 fixture looks schema‑compatible and parity‑ready

Service, GPU, placement, and pricing structure all match the expected v2.0 SDL shape and GPU validation rules; good coverage for basic GPU parity.

ts/src/sdl/SDL/parity.spec.ts (1)

10-140: Parity harness is well‑structured and aligned with Go tests

Shared schema compilation, fixture loading, and the v2.0/v2.1 + invalid‑SDL flows mirror the Go parity tests and exercise the new v3Manifest("go")/v3Groups("go") paths effectively.

go/sdl/parity_test.go (1)

29-149: Go parity tests thoroughly cover fixtures and schemas

The parity tests correctly enforce three things per fixture: input schema validity, Go SDL processing (manifest + groups) validity, and JSON equality against generated fixtures. The structure looks robust and maintainable.

testdata/sdl/v2.0/multiple-services/input.yaml (1)

3-92: Multi‑service fixture nicely exercises complex SDL shapes

This SDL covers multiple services, service dependencies, internal exposes, and multi‑profile pricing under a single placement, which is ideal for stressing manifest/groups parity.

go/sdl/schema_validator.go (1)

14-172: Embedded schema validator is thread‑safe and production‑friendly

Embedding sdl-input.schema.yaml, caching compilation with sync.Once, and guarding logger access with an RWMutex gives a safe, self‑contained validator. The validateInputAgainstSchema and checkSchemaValidationResult APIs integrate cleanly with Read without changing its public contract.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
go/sdl/parity_test.go (1)

54-60: Consider using t.Errorf to report all missing fixtures at once.

Using t.Fatalf stops the test run immediately on the first missing fixture. If multiple fixtures are missing, the developer must fix them one at a time. Consider collecting all missing fixtures and reporting them together.

+	var missingFixtures []string
 	for _, entry := range entries {
 		if !entry.IsDir() {
 			continue
 		}

 		fixtureName := entry.Name()
 		fixtureDir := filepath.Join(fixturesDir, fixtureName)
-		inputPath := filepath.Join(fixtureDir, "input.yaml")
 		manifestPath := filepath.Join(fixtureDir, "manifest.json")
 		groupsPath := filepath.Join(fixtureDir, "groups.json")

 		if _, err := os.Stat(manifestPath); os.IsNotExist(err) {
-			t.Fatalf("manifest.json not generated for %s (run: make generate-sdl-fixtures)", fixtureName)
+			missingFixtures = append(missingFixtures, fixtureName+"/manifest.json")
 		}

 		if _, err := os.Stat(groupsPath); os.IsNotExist(err) {
-			t.Fatalf("groups.json not generated for %s (run: make generate-sdl-fixtures)", fixtureName)
+			missingFixtures = append(missingFixtures, fixtureName+"/groups.json")
 		}
+	}
+
+	if len(missingFixtures) > 0 {
+		t.Fatalf("Missing fixtures (run: make generate-sdl-fixtures): %v", missingFixtures)
+	}
+
+	for _, entry := range entries {
+		if !entry.IsDir() {
+			continue
+		}
+
+		fixtureName := entry.Name()
+		fixtureDir := filepath.Join(fixturesDir, fixtureName)
+		inputPath := filepath.Join(fixtureDir, "input.yaml")
+		manifestPath := filepath.Join(fixtureDir, "manifest.json")
+		groupsPath := filepath.Join(fixtureDir, "groups.json")

 		t.Run(fixtureName, func(t *testing.T) {
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 92f1cb2 and d5bcc0c.

📒 Files selected for processing (20)
  • go/sdl/parity_test.go (1 hunks)
  • go/sdl/schema_validator.go (1 hunks)
  • specs/sdl/README.md (1 hunks)
  • testdata/sdl/invalid/count-zero.yaml (1 hunks)
  • testdata/sdl/invalid/empty-image.yaml (1 hunks)
  • testdata/sdl/invalid/invalid-endpoint-name.yaml (1 hunks)
  • testdata/sdl/invalid/invalid-protocol.yaml (1 hunks)
  • testdata/sdl/invalid/ip-without-global.yaml (1 hunks)
  • testdata/sdl/invalid/missing-memory-size.yaml (1 hunks)
  • testdata/sdl/invalid/missing-pricing.yaml (1 hunks)
  • testdata/sdl/invalid/port-too-high.yaml (1 hunks)
  • testdata/sdl/invalid/port-zero.yaml (1 hunks)
  • testdata/sdl/invalid/ram-persistent-true.yaml (1 hunks)
  • testdata/sdl/invalid/unknown-field.yaml (1 hunks)
  • testdata/sdl/schema-only-invalid/email-too-short.yaml (1 hunks)
  • testdata/sdl/schema-only-invalid/invalid-denom.yaml (1 hunks)
  • testdata/sdl/schema-only-invalid/invalid-gpu-vendor.yaml (1 hunks)
  • testdata/sdl/schema-only-invalid/invalid-version.yaml (1 hunks)
  • testdata/sdl/schema-only-invalid/password-too-short.yaml (1 hunks)
  • ts/src/sdl/SDL/SDL.ts (18 hunks)
✅ Files skipped from review due to trivial changes (1)
  • testdata/sdl/schema-only-invalid/password-too-short.yaml
🚧 Files skipped from review as they are similar to previous changes (1)
  • specs/sdl/README.md
🧰 Additional context used
🧬 Code graph analysis (1)
ts/src/sdl/SDL/SDL.ts (3)
ts/src/sdl/index.ts (1)
  • SdlValidationError (28-28)
ts/src/sdl/types.ts (5)
  • v2Sdl (93-98)
  • v3Sdl (100-105)
  • v3Manifest (4-4)
  • v3ComputeResources (218-224)
  • v3DeploymentGroup (270-285)
ts/src/network/index.ts (1)
  • NetworkId (2-2)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: go
  • GitHub Check: proto
  • GitHub Check: sdl-parity
  • GitHub Check: coverage
  • GitHub Check: test
🔇 Additional comments (42)
testdata/sdl/invalid/port-too-high.yaml (3)

8-8: Port value correctly triggers validation error.

Port 99999 appropriately exceeds the valid TCP port range (1–65535), making this a well-chosen invalid test case. The YAML structure is valid and complete.


1-1: The schema reference path is correct and resolves properly. The file go/sdl/sdl-input.schema.yaml exists at the expected location.


1-33: Verify companion fixture files exist.

Per the PR description, each SDL test fixture should include input.yaml, manifest.json (Go parser output), and groups.json (Go parser output). Confirm that the corresponding manifest.json and groups.json files exist for this test case, or clarify if input-only fixtures are intentional for invalid test cases.

Additionally, since the PR notes that input and output fixtures were AI-generated, manually verify or regenerate the expected error outputs in manifest.json and groups.json to ensure they correctly represent how both parsers should reject this invalid port value.

#!/bin/bash
# Description: Check for companion fixture files for this test case

test_case="testdata/sdl/invalid/port-too-high"
echo "Checking for fixture files:"
ls -la "${test_case}".* 2>/dev/null || echo "No companion files found"
ls -la "${test_case}"/ 2>/dev/null || echo "No directory found"

# List all files in the invalid directory to understand structure
echo -e "\n--- Files in testdata/sdl/invalid/ ---"
fd -t f . testdata/sdl/invalid/ | head -20
testdata/sdl/invalid/ram-persistent-true.yaml (2)

3-36: YAML structure is valid and correctly represents an invalid SDL scenario.

The fixture properly defines a v2.0 SDL with a RAM storage declared as persistent (lines 24–25), which should be rejected by both parsers since RAM storage is inherently ephemeral. The structure aligns with the PR's goal of testing parity in rejecting invalid configurations.

Verify that both Go and TypeScript SDL parsers reject this configuration with consistent error messages. If reference output files (manifest.json, groups.json, or error logs) are expected for this fixture, confirm they are present.


1-1: The schema reference path is correct and the schema file exists at the expected location. The file go/sdl/sdl-input.schema.yaml is reachable from the fixture file using the relative path ../../../go/sdl/sdl-input.schema.yaml, and the file is 12K in size with proper permissions.

testdata/sdl/invalid/count-zero.yaml (1)

1-33: LGTM!

This fixture correctly tests the zero-count deployment validation. The SDL structure is valid except for count: 0 at line 32, which should be rejected by the parser validation added in SDL.ts (lines 128-137).

testdata/sdl/schema-only-invalid/invalid-version.yaml (1)

1-33: LGTM!

This fixture correctly tests schema validation of unsupported SDL version "3.0". The placement in schema-only-invalid directory indicates the schema should reject this while the Go parser accepts it, which is appropriate for testing schema strictness.

testdata/sdl/invalid/invalid-protocol.yaml (1)

1-34: LGTM!

This fixture correctly tests rejection of invalid protocol value "HTTP". The parseServiceProto method in SDL.ts only accepts "TCP", "UDP", or undefined, so this should trigger the "ErrUnsupportedServiceProtocol" error.

testdata/sdl/invalid/empty-image.yaml (1)

1-33: LGTM!

This fixture correctly tests rejection of empty image strings. The validateServiceImage method (lines 377-380) validates that the image is non-empty using image.trim().length > 0.

testdata/sdl/invalid/missing-memory-size.yaml (1)

1-33: LGTM!

This fixture correctly tests rejection of missing memory size. The memory block at line 18-19 only has attributes: {} without a size field, which should be caught during resource validation.

testdata/sdl/invalid/port-zero.yaml (1)

1-33: LGTM!

This fixture correctly tests rejection of port value 0. The validateServicePorts method (lines 382-391) validates that port > 0 && port <= 65535, so port 0 should be rejected.

testdata/sdl/schema-only-invalid/invalid-gpu-vendor.yaml (1)

1-39: LGTM!

This fixture tests GPU vendor configuration that the schema should reject while Go accepts. The GPU attributes structure (lines 24-27) uses a specific vendor/model nesting pattern that exercises schema validation boundaries.

ts/src/sdl/SDL/SDL.ts (7)

114-143: LGTM!

The fromString method improvements address previous review feedback:

  • Null/undefined check for parsed YAML (line 117) prevents cryptic errors on empty input
  • Top-level key validation (lines 121-126) catches typos early
  • Deployment count validation (lines 128-137) enforces count >= 1
  • Version-based type casting (lines 139-141) now correctly respects the version parameter

267-336: LGTM!

The convertToGoFormat helper correctly handles recursive conversion of TypeScript objects to Go-compatible format. The key renaming logic (lines 294-321) properly handles:

  • Snake_case conversions (signedBysigned_by, allOfall_of)
  • Special cases (quantitysize, HTTPOptionshttpOptions)
  • Empty arrays to null for all_of/any_of (lines 327-330) to match Go output

377-391: LGTM!

The new validation methods correctly enforce:

  • Non-empty image names with whitespace trimming (line 379)
  • Valid port range 0 < port <= 65535 (line 387-388)

These runtime validations complement the schema validations discussed in past reviews.


1048-1081: LGTM!

The endpoint sequence number computation correctly implements Go parity:

  • Collects all IPs including duplicates (line 1049 comment)
  • Skips global endpoints without IP (lines 1056-1058) matching Go's to.Global && len(to.IP) == 0
  • Sorts for stable ordering (line 1070)
  • Assigns incremental sequence numbers starting at 1

Note: The duplicate IP overwrite behavior is intentional to match Go (per past review discussion).


1190-1202: LGTM!

The GPU handling now always includes GPU in groups output to match Go behavior. The default { units: { val: "0" } } (lines 1196-1198) ensures deterministic output when GPU is not specified.


1230-1238: LGTM!

The amount formatting with toFixed(18) (line 1233) ensures Go-compatible precision for pricing amounts. The type checking handles numbers, strings, and undefined values appropriately.


1018-1030: LGTM!

The v3Manifest method correctly accepts and propagates the format parameter, applying convertToGoFormat when format === "go" (line 1029).

testdata/sdl/schema-only-invalid/invalid-denom.yaml (1)

1-33: LGTM!

This fixture correctly tests schema-only validation with an invalid denomination value (invalidtoken). The SDL structure is complete and will pass Go parsing while being rejected by the schema validator.

testdata/sdl/invalid/missing-pricing.yaml (1)

1-31: LGTM!

This fixture correctly tests the missing pricing validation scenario. The placement section lacks the required pricing block, which should cause both Go parser and schema validation to fail.

testdata/sdl/invalid/unknown-field.yaml (1)

1-34: LGTM!

This fixture correctly tests rejection of unknown fields at the root level. The unknown_field property should trigger validation failure if the schema enforces additionalProperties: false.

testdata/sdl/invalid/ip-without-global.yaml (1)

1-36: LGTM!

This fixture correctly tests the invalid scenario where an IP endpoint is referenced without the required global: true setting. The exposure target at line 14 only specifies ip: myip without the necessary global flag.

testdata/sdl/invalid/invalid-endpoint-name.yaml (1)

1-37: LGTM!

This fixture correctly tests endpoint name validation. The endpoint name Invalid-Name violates naming conventions (uppercase letters not typically allowed), and the reference at line 15 ensures the validation covers both definition and usage.

testdata/sdl/schema-only-invalid/email-too-short.yaml (1)

1-38: LGTM!

This fixture correctly tests schema-only validation for email length constraints. The email a@b is syntactically valid but likely fails a minLength requirement in the schema, while the Go parser should accept it.

go/sdl/parity_test.go (8)

16-17: LGTM!

The constants for fixture and schema paths are appropriately defined with relative paths from the test package location.


19-27: LGTM!

Good use of sync.Once for lazy schema compilation. This avoids redundant compilation across test runs while properly preserving any initialization errors.


69-90: LGTM!

The validation flow is well-structured: input schema validation → SDL parsing → manifest/groups schema validation. Using require.NoError appropriately fails fast on validation errors.


92-116: LGTM!

Good use of lazy initialization with sync.Once for schema compilation. The marshal-then-validate pattern works correctly with gojsonschema.


118-149: LGTM!

The JSON round-trip comparison approach correctly normalizes types for deep equality checking. This ensures the generated output matches the expected fixtures regardless of field ordering.


151-172: LGTM!

The test correctly ensures all files in the invalid/ directory cause ReadFile to return an error. The directory existence check with t.Skip allows incremental development.


174-201: LGTM!

Good test design that validates the distinction between schema-only validation and Go parser acceptance. The assertions at lines 195 and 198 correctly verify that schema rejects but Go accepts these inputs.


203-243: LGTM!

The helper functions are well-implemented. The YAML-to-JSON conversion correctly bridges YAML schema definitions with the JSON-only gojsonschema library, and error aggregation provides useful debugging output.

go/sdl/schema_validator.go (9)

1-12: LGTM! Clean imports.

The package imports are appropriate for the schema validation functionality, with correct use of the embed package for compile-time schema inclusion.


14-18: Excellent resolution of the previous schema path issue.

Using go:embed eliminates the fragile runtime.Caller() approach that was flagged in earlier reviews. The schema is now reliably embedded at compile time, making this production-safe.


20-26: Well-structured with proper synchronization primitives.

The struct correctly uses sync.Once for lazy schema compilation and sync.RWMutex for thread-safe logger access, addressing the race condition concern from previous reviews.


32-53: Excellent implementation of thread-safe logger configuration.

This properly addresses both previous critical concerns:

  • Thread-safety is ensured with sync.RWMutex
  • Nil logger is gracefully handled by falling back to noOpLogger

The documentation clearly explains usage and thread-safety guarantees.


55-62: LGTM! Defensive programming with clear error message.

The empty check provides a safety net with an informative error message, though it should never occur since the schema is embedded at compile time.


64-93: LGTM! Proper lazy initialization pattern.

The sync.Once ensures thread-safe, one-time compilation with error caching. The YAML-to-JSON conversion is necessary for compatibility with gojsonschema, and error wrapping follows best practices.


95-105: Perfect implementation with compile-time safety.

The no-op logger correctly implements all log.Logger methods, and the compile-time assertion on line 105 ensures interface compliance, addressing the previous minor issue.


107-141: LGTM! Clean validation implementation.

The validation logic correctly:

  • Parses YAML input
  • Converts to JSON for gojsonschema compatibility
  • Collects and formats all validation errors
  • Uses proper error wrapping throughout

143-171: LGTM! Correct mismatch detection with thread-safe logging.

The implementation correctly:

  • Detects mismatches between schema and Go validation
  • Uses RLock to safely retrieve the logger pointer
  • Logs only when there's a mismatch (avoiding noise)
  • Provides clear, distinct warning messages for each mismatch type

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
ts/src/sdl/SDL/SDL.ts (2)

1210-1305: v3Groups: bug in location check prevents proper resource merging; endpoint sort lacks comparator

Two issues in the v3Groups core loop:

  1. location falsy check breaks merging for first resource index
const location = group!.boundComputes[placementName][svcdepl.profile];

if (!location) {
  // create new resource, then:
  group!.boundComputes[placementName][svcdepl.profile] = group!.dgroup.resources.length - 1;
} else {
  // merge into existing resource
}

When the first resource for a given (placementName, profile) is stored at index 0, location will be 0, which is falsy. On the next occurrence of the same (placementName, profile), !location is still true, so a new resource is created instead of merging with index 0. Only when the index becomes 1 or higher does the else branch ever trigger.

This prevents deterministic aggregation of counts/endpoints for the first matching resource and will diverge from the Go behavior that actually reuses the same resource slot.

Use an explicit undefined check:

- const location = group!.boundComputes[placementName][svcdepl.profile];
-
- if (!location) {
+ const location = group!.boundComputes[placementName][svcdepl.profile];
+
+ if (location === undefined) {
  // ...
-  group!.boundComputes[placementName][svcdepl.profile] = group!.dgroup.resources.length - 1;
+  group!.boundComputes[placementName][svcdepl.profile] = group!.dgroup.resources.length - 1;
 } else {
   const endpoints = this.v3ServiceResourceEndpoints(service);
-  group!.dgroup.resources[location].count += svcdepl.count;
-  group!.dgroup.resources[location].endpoints = (group!.dgroup.resources[location].endpoints as any).concat(endpoints);
-  group!.dgroup.resources[location].endpoints.sort();
+  group!.dgroup.resources[location].count += svcdepl.count;
+  const existing = (group!.dgroup.resources[location].endpoints || []) as any[];
+  group!.dgroup.resources[location].endpoints = existing.concat(endpoints);
+  // Optionally: provide an explicit comparator if Go sorts endpoints
+  // group!.dgroup.resources[location].endpoints.sort((a, b) =>
+  //   a.kind - b.kind || a.sequence_number - b.sequence_number
+  // );
 }
  1. Sorting endpoints without a comparator is a no-op on objects

group!.dgroup.resources[location].endpoints.sort(); operates on an array of endpoint objects. Without a comparator, JavaScript sorts by stringifying elements (all are "[object Object]"), which is effectively a no-op for order and may not match Go’s sort semantics if Go sorts endpoints deterministically.

If Go does sort endpoints, add an explicit comparator that matches that ordering (e.g., by kind then sequence_number). If Go doesn’t sort them, the call to .sort() here is misleading and can be removed.


114-152: Align fromString and deprecated validate around empty/invalid YAML handling

fromString correctly guards against load(yaml) returning undefined (lines 2–4 check !parsed), but the deprecated validate method assumes load(yaml) always succeeds and immediately accesses data.profiles without a null check (line 40). For empty or whitespace-only YAML, load() returns undefined, causing a TypeError instead of the structured SdlValidationError that fromString produces.

Although validate is deprecated, consider adding the same guard to prevent confusing error messages:

static validate(yaml: string) {
  console.warn("SDL.validate is deprecated. Use SDL.constructor directly.");
- const data = load(yaml) as v3Sdl;
+ const parsed = load(yaml) as any;
+ if (!parsed) {
+   throw new SdlValidationError("SDL invalid: no content");
+ }
+ const data = parsed as v3Sdl;
  // existing GPU/storage validation…
}
🧹 Nitpick comments (2)
ts/src/sdl/SDL/SDL.ts (2)

639-668: quantity vs size key toggling is correct but duplicates conversion logic

Switching between "quantity" and "size" based on asString in serviceResourceMemory / serviceResourceStorage and resourceUnitMemory / resourceUnitStorage aligns with how you later post-process manifests (e.g., manifestSortedJSON and Go-format output).

However, you now have two independent mechanisms converting "quantity""size": the explicit replaceAll in manifestSortedJSON and the quantitysize rule in convertToGoFormat. To reduce drift, consider centralizing this transformation via convertToGoFormat (or a small shared helper) and removing the string-level replaceAll, so there is a single source of truth for this mapping.

Also applies to: 1100-1130


1048-1081: computeEndpointSequenceNumbers signature vs usage

The implementation now very explicitly matches the Go behavior (including duplicates and skip-logic for global endpoints without IPs), which is good for parity.

Given that you pass this.data (typed as v2Sdl | v3Sdl) into computeEndpointSequenceNumbers, but the parameter is declared as sdl: v2Sdl, it may be clearer to widen the type to v2Sdl | v3Sdl (or a shared SDL interface) so the signature reflects actual usage and avoids future confusion when v3-specific fields are added.

- computeEndpointSequenceNumbers(sdl: v2Sdl) {
+ computeEndpointSequenceNumbers(sdl: v2Sdl | v3Sdl) {

This is purely a typing / readability tweak; runtime behavior is already correct.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d5bcc0c and b22a845.

📒 Files selected for processing (1)
  • ts/src/sdl/SDL/SDL.ts (18 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: proto
  • GitHub Check: go
  • GitHub Check: sdl-parity
  • GitHub Check: test
  • GitHub Check: coverage
🔇 Additional comments (3)
ts/src/sdl/SDL/SDL.ts (3)

338-353: New service-level validations (image/ports) look good

The added checks for non-empty image and port range 0 < port <= 65535 integrate cleanly into validate() and mirror the schema-level constraints. This should catch a useful class of invalid SDLs early without changing any successful flows.

Also applies to: 377-392


1018-1034: v3Manifest/v3Groups integration for Go format looks consistent

Using v3Groups(format) to derive stable serviceIds and then conditionally wrapping the final manifest via convertToGoFormat keeps the ID assignment consistent between TypeScript and Go-formatted outputs. Because convertToGoFormat does not alter the id field, reading groups[pIdx].resources[sIdx].resource.id continues to work even when format === "go".

No functional issues spotted here.


1171-1201: GPU field is already always-emitted in groups fixtures—no behavior change

The test fixture at ts/test/fixtures/sdl/groups-basic-snapshot.json already shows the gpu field present in group resources even when GPU is not configured, with units.val = "0". This confirms the current behavior is not a new change; the gpu field has been consistently emitted. No downstream code in the codebase treats gpu as optional in groups output. The concern about changing the TS-facing shape is not applicable here since the shape already includes gpu.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
ts/src/sdl/SDL/SDL.ts (1)

1049-1081: computeEndpointSequenceNumbers: behavior matches Go, including duplicates

The new implementation (skipping global endpoints without IP, collecting all others, sorting, then assigning sequence numbers) is clear and intentionally mirrors the Go behavior per the comments. Note that duplicate IPs will still end up with the highest sequence number assigned to that IP, which is fine for parity but might be surprising semantically; your inline comment documents this adequately.

🧹 Nitpick comments (4)
ts/src/sdl/SDL/SDL.ts (4)

115-152: fromString/validate: stronger YAML handling looks good; consider version consistency check

Switching to load(yaml) with the explicit !parsed || !parsed.version guard and the top-level key allow‑list / deployment.*.count >= 1 checks is a solid improvement in early failure behavior and error messages. One follow‑up you may want (not required for this PR) is to assert that parsed.version is compatible with the NetworkVersion parameter, so users don’t accidentally pass a 2.0 SDL while asking for "beta3" semantics and only discover it later via more obscure errors.


927-956: Manifest service params: sorted storage keys and null handling are sensible

Sorting params.storage keys for both v2 and v3 manifest params gives deterministic output, which is important for parity tests, and returning null from v3ManifestServiceParams when params is absent keeps the manifest lean. The additional runtime guard for params.storage in the mappers is also reasonable defensive code.

Also applies to: 991-1006


1018-1034: Manifest/groups format parameterization: API shape is mostly consistent

Adding OutputFormat and threading it through v3Manifest and v3Groups makes the Go‑compatible output path explicit and keeps the default "typescript" behavior backward‑compatible. One thing to keep in mind is that manifest() itself doesn’t expose the format yet (unlike groups(format)), so callers that need Go‑format manifests must reach for v3Manifest(…, "go") directly; if you want full symmetry later, you might extend manifest(asString, format?) in a future iteration.

Also applies to: 1210-1215


1100-1129: Resource unit key switch and GPU defaulting look intentional for parity

Switching memory/storage units to use "quantity" when asString is true and "size" otherwise, plus always emitting a gpu unit (defaulting to "0" when missing), lines up with the described Go JSON shapes for groups. This does mean that in groups, price.amount and GPU units are now consistently stringified, so any existing TS consumers that expected numbers should be treated as accepting string | number (or updated) if they aren’t already.

Also applies to: 1171-1202

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b22a845 and 2c891b2.

📒 Files selected for processing (1)
  • ts/src/sdl/SDL/SDL.ts (18 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: go
  • GitHub Check: proto
  • GitHub Check: coverage
  • GitHub Check: sdl-parity
  • GitHub Check: test
🔇 Additional comments (4)
ts/src/sdl/SDL/SDL.ts (4)

259-265: Constructor data type widened to v2Sdl | v3Sdl aligns with fromString

Updating the constructor to accept v2Sdl | v3Sdl matches the fromString casting and avoids the earlier mismatch between stored data and version-specific methods. This looks correct and keeps the internal API coherent.


267-336: convertToGoFormat: key/shape normalization aligns with Go output

The recursive normalization (Uint8Array → string, camelCase ↔ snake_case, quantitysize, empty all_of/any_ofnull) is consistent with the Go JSON expectations and the parity goals of this PR. The implementation is straightforward and guarded against undefined values, so I don’t see functional issues here.


338-352: New image/port validations integrate cleanly into validate()

Wiring validateServiceImage and validateServicePorts into validate() adds useful sanity checks (non‑empty image strings and port range enforcement) without changing existing flows, and the failure messages are clear. This should catch a good class of SDL mistakes early while staying consistent with other SdlValidationError checks.

Also applies to: 377-391


1324-1351: v2Groups price formatting and requirements normalization look correct

Stringifying pricing.amount to 18‑decimal strings, normalizing attributes into sorted key/value pairs, and explicitly setting signedBy.allOf/anyOf to null when absent should help the v2 groups output line up with the Go fixtures. The shape is consistent with the v3Groups requirements block, which is good for maintainability.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
go/sdl/parity_test.go (1)

54-60: Consider using t.Skipf instead of t.Fatalf for missing fixtures.

Using t.Fatalf stops all subsequent tests if fixtures are missing. If this is intentional (fixtures should always exist in CI), this is fine. Otherwise, t.Skipf would allow other tests to continue.

 		if _, err := os.Stat(manifestPath); os.IsNotExist(err) {
-			t.Fatalf("manifest.json not generated for %s (run: make generate-sdl-fixtures)", fixtureName)
+			t.Skipf("manifest.json not generated for %s (run: make generate-sdl-fixtures)", fixtureName)
 		}

 		if _, err := os.Stat(groupsPath); os.IsNotExist(err) {
-			t.Fatalf("groups.json not generated for %s (run: make generate-sdl-fixtures)", fixtureName)
+			t.Skipf("groups.json not generated for %s (run: make generate-sdl-fixtures)", fixtureName)
 		}
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e6a1969 and 6dd94ad.

📒 Files selected for processing (20)
  • go/sdl/parity_test.go (1 hunks)
  • go/sdl/schema_validator.go (1 hunks)
  • specs/sdl/README.md (1 hunks)
  • testdata/sdl/invalid/count-zero.yaml (1 hunks)
  • testdata/sdl/invalid/empty-image.yaml (1 hunks)
  • testdata/sdl/invalid/invalid-endpoint-name.yaml (1 hunks)
  • testdata/sdl/invalid/invalid-protocol.yaml (1 hunks)
  • testdata/sdl/invalid/ip-without-global.yaml (1 hunks)
  • testdata/sdl/invalid/missing-memory-size.yaml (1 hunks)
  • testdata/sdl/invalid/missing-pricing.yaml (1 hunks)
  • testdata/sdl/invalid/port-too-high.yaml (1 hunks)
  • testdata/sdl/invalid/port-zero.yaml (1 hunks)
  • testdata/sdl/invalid/ram-persistent-true.yaml (1 hunks)
  • testdata/sdl/invalid/unknown-field.yaml (1 hunks)
  • testdata/sdl/schema-only-invalid/email-too-short.yaml (1 hunks)
  • testdata/sdl/schema-only-invalid/invalid-denom.yaml (1 hunks)
  • testdata/sdl/schema-only-invalid/invalid-gpu-vendor.yaml (1 hunks)
  • testdata/sdl/schema-only-invalid/invalid-version.yaml (1 hunks)
  • testdata/sdl/schema-only-invalid/password-too-short.yaml (1 hunks)
  • ts/src/sdl/SDL/SDL.ts (18 hunks)
✅ Files skipped from review due to trivial changes (2)
  • testdata/sdl/schema-only-invalid/invalid-denom.yaml
  • testdata/sdl/schema-only-invalid/invalid-version.yaml
🚧 Files skipped from review as they are similar to previous changes (10)
  • testdata/sdl/invalid/ip-without-global.yaml
  • testdata/sdl/invalid/count-zero.yaml
  • specs/sdl/README.md
  • go/sdl/schema_validator.go
  • testdata/sdl/invalid/empty-image.yaml
  • testdata/sdl/invalid/unknown-field.yaml
  • testdata/sdl/schema-only-invalid/password-too-short.yaml
  • testdata/sdl/invalid/ram-persistent-true.yaml
  • testdata/sdl/invalid/missing-pricing.yaml
  • testdata/sdl/schema-only-invalid/invalid-gpu-vendor.yaml
🧰 Additional context used
🧬 Code graph analysis (1)
go/sdl/parity_test.go (2)
go/sdl/sdl.go (1)
  • ReadFile (83-89)
go/testutil/deployment.go (1)
  • DeploymentGroups (54-62)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: test
  • GitHub Check: sdl-parity
  • GitHub Check: coverage
  • GitHub Check: go
🔇 Additional comments (23)
testdata/sdl/invalid/invalid-endpoint-name.yaml (1)

1-37: Verify endpoint name validation is the isolated failure point.

The YAML structure is valid and well-formed. The fixture is correctly placed in the invalid/ directory and references the endpoint consistently (line 5 definition, line 15 reference). The endpoint name Invalid-Name with uppercase characters appears to be the intended invalid element.

Please confirm that:

  1. Endpoint names must use lowercase-only format (reject Invalid-Name due to uppercase)
  2. Both the schema validator and Go parser reject this fixture specifically for the endpoint name violation
  3. No other unintended invalid elements exist in the fixture
testdata/sdl/invalid/port-too-high.yaml (2)

1-1: Schema reference path resolves correctly.

The YAML schema reference at line 1 (../../../go/sdl/sdl-input.schema.yaml) correctly resolves to the schema file located at go/sdl/sdl-input.schema.yaml in the repository. The relative path navigation from testdata/sdl/invalid/ up three levels to the repository root and then into go/sdl/ is valid.


8-9: Validate port range constraint is properly defined in schema.

Port 99999 exceeds the valid TCP/UDP port range (0–65535), making this a correct invalid test case. The sdl-input.schema.yaml properly defines and enforces this constraint with maximum: 65535 on both port and as fields in the service expose configuration, ensuring this fixture is correctly rejected by schema validation.

testdata/sdl/invalid/missing-memory-size.yaml (1)

18-19: LGTM - Correctly tests missing memory size.

The fixture properly demonstrates the invalid case with attributes: {} instead of a required size field in the memory block. This will validate that both Go and schema parsers reject SDLs missing memory size specification.

testdata/sdl/invalid/invalid-protocol.yaml (1)

10-10: LGTM - Correctly tests invalid protocol value.

The fixture uses HTTP as the protocol value, which is invalid since the proto field expects transport-layer protocols like tcp or udp, not application-layer protocols.

testdata/sdl/invalid/port-zero.yaml (1)

8-8: LGTM - Correctly tests invalid port zero.

Port 0 is invalid for service exposure, and this fixture properly validates that the parsers reject this edge case.

testdata/sdl/schema-only-invalid/email-too-short.yaml (1)

7-11: LGTM - Correctly tests schema-only email validation.

The fixture properly demonstrates a case where the schema is stricter than the Go parser. The email a@b is syntactically valid but fails minimum length requirements in the schema, while the Go parser accepts it.

go/sdl/parity_test.go (7)

16-17: Consider using constants consistently for paths.

Both constants use relative paths, which is correct for test fixtures. The separation of fixture root from schema root is good for clarity.


19-27: LGTM - Good use of sync.Once for schema caching.

The lazy initialization pattern with sync.Once is appropriate for expensive schema compilation and works correctly with parallel test execution.


69-116: LGTM - Clean validation flow.

The validation sequence (input schema → parse → output schemas) is well-structured, and the sync.Once pattern correctly handles schema compilation caching with proper error checking.


118-149: LGTM - Robust fixture comparison.

The JSON round-trip comparison pattern correctly normalizes formatting differences between expected and actual outputs. The duplicate SDL parsing (once in validateSchemas, once here) provides good test isolation.


151-172: LGTM - Correct invalid SDL rejection testing.

The test properly verifies that the Go parser rejects all files in the invalid/ directory. The graceful skip when the directory doesn't exist is appropriate.


174-201: LGTM - Correctly tests schema-only validation divergence.

The test properly validates that schema validation is stricter than the Go parser for certain edge cases, with clear assertion messages explaining the expected behavior.


203-243: LGTM - Well-implemented helper functions.

The YAML-to-JSON conversion for schema loading is necessary since gojsonschema expects JSON. Error wrapping with %w and the collection of validation errors into a descriptive slice are good practices.

ts/src/sdl/SDL/SDL.ts (9)

2-2: LGTM! Import refactored and format type added.

The switch to the named load export from js-yaml is correct, and the new OutputFormat type properly supports dual-mode output for Go/TypeScript parity.

Also applies to: 57-57


114-142: Excellent validation improvements!

The enhanced fromString method now includes:

  • Null/undefined guard (addresses past issue)
  • Allowed-keys validation to catch typos
  • Deployment count validation (must be >= 1)
  • Version-aware type casting to v2Sdl or v3Sdl (fixes past type mismatch)

These changes significantly improve input validation and type safety.


267-336: Well-structured Go format converter.

The convertToGoFormat method comprehensively handles the TypeScript-to-Go JSON transformation:

  • Properly converts empty arrays to null for all_of/any_of (as noted in past review)
  • Handles key renaming (camelCase ↔ snake_case)
  • Recursively processes nested structures
  • Preserves GPU fields per Go's behavior

The implementation correctly addresses the earlier comment about anyOf/any_of empty-array handling.


377-391: Good addition of focused validation helpers.

The new validateServiceImage and validateServicePorts methods provide clear, early validation of service configuration with descriptive error messages.


940-956: Good deterministic storage parameter handling.

The sorted storageKeys ensures consistent output order. The null-check and defensive error on line 948 are appropriate for type safety.


1018-1030: Excellent format parameter integration and past issue resolution.

The format parameter is properly threaded through v3Manifestv3GroupsconvertToGoFormat, enabling dual-mode output. Key improvements:

  • Line 1230-1238: Amount formatting with toFixed(18) ensures Go-compatible precision
  • Lines 1243-1248: Sorted attributes guarantee deterministic output
  • Lines 1257-1259: signedBy fields default to null for empty arrays (addresses past comment)
  • Line 1275: location === undefined check fixes the critical past issue where index 0 was treated as falsy
  • Lines 1289-1296: Proper array concatenation replaces the broken += operator (addresses past major issue)
  • Lines 1190-1199: GPU always included with units "0" when absent, matching Go behavior

All referenced past issues have been correctly addressed.

Also applies to: 1210-1212, 1214-1304


1307-1398: v2Groups updated with same deterministic improvements.

Amount precision, attribute sorting, and signedBy null-defaults mirror the v3Groups improvements—good consistency across versions.


640-640: Good format-aware key selection for memory/storage.

Dynamically choosing "quantity" vs "size" based on the asString parameter ensures output compatibility with Go's expected field names.

Also applies to: 653-653, 1102-1102, 1121-1121


1048-1081: No action required—endpoint sequence numbering is implemented correctly.

The current implementation matches the Go behavior exactly. When duplicate IPs exist in expose.to definitions (e.g., two services sharing the same IP endpoint), the code collects all occurrences, sorts them, and assigns sequence numbers by iterating the sorted list. Duplicate IPs in the collection cause the final map value to be overwritten with the later sequence number—this is intentional, not a bug.

The Go test TestV2Parse_SharedIP validates this behavior: it confirms that two services referencing the same IP "meow" both receive the same SequenceNumber, which is the expected and correct outcome. The comment in the code accurately describes the behavior as "matching Go implementation."

Deduplicating with a Set before assignment would change the sequence number assignments and break parity with the Go implementation. The previous review suggestion about deduplication was likely a refactoring consideration, not a correctness requirement—your decision to not apply it was correct.

@vertex451 vertex451 requested a review from troian December 16, 2025 13:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Investigate possibility to compile GoLang SDL builder to webassembly

3 participants