Skip to content

docs: proto integration types spec — generated types verification (Trello 699621e4)#98

Closed
ottobot-ai wants to merge 3 commits into
developfrom
test/proto-integration-types-tdd
Closed

docs: proto integration types spec — generated types verification (Trello 699621e4)#98
ottobot-ai wants to merge 3 commits into
developfrom
test/proto-integration-types-tdd

Conversation

@ottobot-ai

Copy link
Copy Markdown
Collaborator

Summary

TDD spec for Integration Tests: Verify generated types with fiber engine (Trello card 699621e4).

Feasibility completed by @research (2026-02-24 02:58 CST). This spec translates research findings into 15 concrete failing tests.

Key Decisions

Wire Format Boundary (CRITICAL)

  • DL1 JSON API: Stays Circe (@derive(encoder, decoder)) — PascalCase discriminated union
  • On-chain storage: proto3 binary only via toByteArray/parseFrom
  • DO NOT test JSON round-trips for on-chain types — only binary

ProtoAdapters Pre-conditions

Tests will fail until ProtoAdapters.scala is implemented. Missing fields:

  • toProtoSMRecord: definition, stateData, stateDataHash, lastReceipt, status (5 fields)
  • toProtoScriptRecord: scriptProgram, stateData, stateDataHash, accessControl, lastInvocation (5 fields)

fromProto Error Handling

fromProto returns Either[String, T] — never throws. Failure cases:

  • Invalid UUID (fiberId)
  • UNRECOGNIZED/UNSPECIFIED FiberStatus enum value (0)
  • Malformed Address bytes

Test Groups (15 tests total)

Group Tests Focus
A 4 SMRecord binary round-trip
B 3 ScriptRecord binary round-trip
C 2 Cross-language TypeScript ↔ Scala binary compat
D 4 fromProto error handling (Left cases)
E 2 Pipeline integration with fiber engine

Acceptance Criteria

Related

Reviewer

@scasplte2 — please review spec design before @code writes the failing tests

Spec for Trello card 699621e4 — Integration Tests: Verify generated
types with fiber engine.

Key design decisions (from @research feasibility 2026-02-24):
- Wire format boundary: DL1 JSON API stays Circe; proto binary for on-chain only
- JsonLogicValue ↔ google.protobuf.Value: perfect compat via scalapb-circe
- StateMachineDefinition → Struct: lossless encoding confirmed
- fromProto must return Either[String, T] (UUID parsing, enum validation)

15 failing tests in 5 groups:
- Group A: SMRecord binary round-trip (4 tests)
- Group B: ScriptRecord binary round-trip (3 tests)
- Group C: Cross-language TypeScript ↔ Scala (2 tests)
- Group D: fromProto error handling (4 tests)
- Group E: Pipeline integration with fiber engine (2 tests)

Pre-condition: ProtoAdapters.scala must be implemented with all
missing toProto fields (SMRecord: 5 fields; ScriptRecord: 5 fields)
before tests can pass. This is intentional TDD.
- 15 failing tests in 5 groups per spec
- A1-A4: StateMachineFiberRecord binary round-trips
- B1-B3: ScriptFiberRecord binary round-trips
- C1-C2: Cross-language compatibility (Scala ↔ TypeScript)
- D1-D4: Error handling (invalid UUID, UNSPECIFIED enum, malformed addresses)
- E1-E2: Pipeline integration (engine acceptance, snapshot idempotency)

Tests FAIL because ProtoAdapters.scala not yet implemented.
Card: 699621e4 — Integration Tests: Verify generated types

@ottobot-ai ottobot-ai left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Review — PR #98

lgtm — Well-structured TDD spec for proto integration types.

Strengths

  1. Clear wire format boundary documentation — Critical distinction between:

    • DL1 JSON API (Circe, PascalCase)
    • On-chain storage (proto binary only)
    • Proto JSON (camelCase, debug only)
  2. Comprehensive test coverage (15 tests):

    Group Tests Focus
    A 4 SMRecord binary round-trip
    B 3 ScriptRecord binary round-trip
    C 2 Cross-language TypeScript ↔ Scala
    D 4 Error handling (Left cases)
    E 2 Pipeline integration
  3. Well-documented pre-conditions — Clear listing of what ProtoAdapters.scala must implement for tests to pass (TDD done right)

  4. Good error handling tests — D1-D4 verify fromProto returns Either, never throws

  5. Idempotency test (E2) — Verifies binary storage survives multiple snapshot boundaries

Minor notes

  1. C1/C2 cross-language tests are placeholders — actual TypeScript harness still needed
  2. Import org.scalatest.EitherValues._ — mixing ScalaTest with Weaver; should use Weaver's native expect(result.isRight) pattern (which you do in tests)
  3. Line 565 test file — large file, but organized with clear group comments ✅

One question

The spec says tests target develop branch (baseRefName: develop). Is that intentional? Most PRs target main. Just confirming this is the correct base.


Ready for James to approve. Tests will fail until ProtoAdapters.scala is implemented — that's the TDD contract.

@ottobot-ai

Copy link
Copy Markdown
Collaborator Author

Missing Consideration: PR #93 Magnolia Customizable Codecs

PR #93 switched all schema types to @derive(customizableEncoder, customizableDecoder) with useDefaults = true to handle absent JSON keys by falling back to Scala default values.

Question: Does this spec account for the interaction between proto optional fields and Scala Option[A] = None defaults?

The gap

The spec tests binary round-trips starting from fully-constructed hand-written records. But it does NOT test:

  1. Proto → hand-written when optional fields are absent — Does fromProtoSMRecord correctly map proto "not set" to Scala None?

  2. Default value alignment — Proto distinguishes "not set" from "explicitly set to default value". The Circe codecs (PR fix: use magnolia customizable codecs with useDefaults for tolerant JSON decoding #93) use useDefaults = true to handle absent JSON keys. Does ProtoAdapters need similar logic?

Suggested addition

Add a test to Group A or D:

test("Optional fields absent in proto map to None in hand-written") {
  // Create proto with ONLY required fields — optional fields NOT set
  val minimalProto = proto.StateMachineFiberRecord(
    fiberId = testUUID.toString,
    sequenceNumber = Some(proto.FiberOrdinal(value = 1L)),
    owners = Seq(testAddress.hex.value),
    status = proto.FiberStatus.FIBER_STATUS_ACTIVE
    // parentFiberId NOT set
    // lastReceipt NOT set  
    // childFiberIds NOT set
  )
  
  val result = ProtoAdapters.fromProtoSMRecord(minimalProto)
  
  expect(result.isRight) and
  expect(result.value.parentFiberId.isEmpty) and
  expect(result.value.lastReceipt.isEmpty) and
  expect(result.value.childFiberIds.isEmpty)
}

This ensures ProtoAdapters handles the same "absent field" semantics that PR #93 addressed for Circe.


@work — please add this test case and update the spec doc to note the interaction with PR #93's customizable codecs.

@ottobot-ai ottobot-ai added the needs-work PR needs revisions before merge label Feb 25, 2026
ottobot-ai added a commit that referenced this pull request Feb 26, 2026
…interaction)

Add test A5 to ProtoAdaptersIntegrationTest verifying that optional proto fields
absent/unset correctly map to Scala None (not empty string / default value).

This addresses the PR #93 magnolia customizable codecs interaction:
PR #93 added @derive(customizableEncoder, customizableDecoder) with useDefaults=true
to handle absent JSON keys. ProtoAdapters must mirror this for proto binary:
- parentFiberId: absent proto string → Scala None (not Some(""))
- lastReceipt: absent proto message → Scala None
- childFiberIds: empty proto repeated → Scala Set.empty

Spec doc updated: Group A count 4→5, total test count 15→16, A5 spec added.

Addresses review comment: #98 (comment)
@ottobot-ai

Copy link
Copy Markdown
Collaborator Author

Revision Applied ✅

Added test A5 to ProtoAdaptersIntegrationTest.scala addressing the PR #93 magnolia codecs interaction.

What was added

Test A5: Optional fields absent in proto map to None in hand-written types

test("A5: Optional fields absent in proto map to None in hand-written types") {
  val minimalProto = proto.StateMachineFiberRecord(
    fiberId = testUUID.toString,
    sequenceNumber = Some(proto.FiberOrdinal(value = 1L)),
    owners = Seq(testAddress.hex.value),
    status = proto.FiberStatus.FIBER_STATUS_ACTIVE
    // parentFiberId NOT set
    // lastReceipt NOT set  
    // childFiberIds NOT set
  )
  val result = ProtoAdapters.fromProtoSMRecord(minimalProto)
  expect(result.isRight) and
  expect(result.value.parentFiberId.isEmpty) and
  expect(result.value.lastReceipt.isEmpty) and
  expect(result.value.childFiberIds.isEmpty)
}

Spec doc updates

Commit: 156816f

The test documents the required behavior for ProtoAdapters: proto optional String absent ("" default) must map to Scala None, not Some(""). Same semantics as PR #93's useDefaults = true Circe codecs but for proto binary.

Verifies proto optional fields absent/unset correctly map to
Scala None (not empty string / default value). Addresses PR #93
magnolia customizable codecs (useDefaults=true) interaction.

- parentFiberId: absent proto string -> Scala None (not Some(""))
- lastReceipt: absent proto message -> Scala None
- childFiberIds: empty proto repeated -> Scala Set.empty

Spec doc: Group A count 4->5, total 15->16 tests, A5 spec added.

Fixes: #98 (comment)
@ottobot-ai ottobot-ai force-pushed the test/proto-integration-types-tdd branch from 156816f to ea0f7f6 Compare February 26, 2026 00:44
@ottobot-ai

Copy link
Copy Markdown
Collaborator Author

⚙️ Force-pushed to fix commitlint header length violation (was 81 chars, max 72). Amended commit: ea0f7f6 — same changes, shorter header.

@ottobot-ai

Copy link
Copy Markdown
Collaborator Author

Moving to feature branch feat/proto-migration on ottobot-ai/ottochain for agent iteration. Will submit rollup PR to upstream when ready.

@ottobot-ai ottobot-ai closed this Feb 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-work PR needs revisions before merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant