test(desktop): pin the RelayAgentInfo Tauri payload to snake_case - #6245
Open
rmichelena wants to merge 1 commit into
Open
test(desktop): pin the RelayAgentInfo Tauri payload to snake_case#6245rmichelena wants to merge 1 commit into
rmichelena wants to merge 1 commit into
Conversation
RelayAgentInfo is the payload of list_relay_agents and revalidate_relay_agents, and the frontend maps it by hand: fromRawRelayAgent in desktop/src/shared/api/tauri.ts reads owner_pubkey, agent_type, channel_ids, respond_to and respond_to_allowlist and rewrites them to camelCase. That contract rests entirely on this struct not carrying #[serde(rename_all = "camelCase")]. Adding it is the reflex when a Rust struct feeds a TypeScript client, and here it fails silently: RawRelayAgent marks all five fields optional, so the mapper does not throw -- it yields null / [] / null -- and nothing in the repo asserts the wire shape, so no test goes red. Every relay agent would arrive owner-less, typeless, channel-less and with respondTo null, which relayAgentIsSharedWithUser treats as deny. The directory would stop being mentionable on a green build. The blast radius grew recently: owner_pubkey and channel_ids joined this struct after the mapper was written, so it is five hand-mapped fields now. The test asserts both halves -- the snake_case keys are present with the expected values, and the camelCase spellings are absent -- because only the second half catches an added rename_all. Checked by breaking it, not by watching it pass: with rename_all = "camelCase" the test fails on the owner_pubkey assertion, and passes again once removed. It sits with the existing serde-contract tests in the same file (respond_to_serde_is_kebab_case, and the needs_restart / restart_diff snake_case assertion), and runs in CI through just desktop-tauri-test. Split out of block#5483, closed as superseded by block#6086, block#6182 and block#6224 -- those implemented the directory work it proposed, but nothing carried this contract across. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Roberto Michelena <77797875+rmichelena@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Add one regression test pinning that
RelayAgentInfocrosses the Tauri boundary withsnake_case keys, and that the camelCase spellings never appear on the wire.
No behaviour change — this is a test-only PR closing a silent-failure hole.
Why
RelayAgentInfois the payload oflist_relay_agentsandrevalidate_relay_agents, andthe frontend maps it by hand.
fromRawRelayAgent(desktop/src/shared/api/tauri.ts:655)reads
owner_pubkey,agent_type,channel_ids,respond_toandrespond_to_allowlistand rewrites them to camelCase for the
RelayAgenttype.That contract rests entirely on the struct not carrying
#[serde(rename_all = "camelCase")].Adding it is the reflex when a Rust struct feeds a TypeScript client, and here it fails
silently in both directions:
RawRelayAgent(tauri.ts:101) marks all five fields optional, so the mapper does notthrow — it produces
null/[]/null.The runtime result is every relay agent arriving owner-less, typeless, channel-less and
with
respondTo: null— whichrelayAgentIsSharedWithUser(
desktop/src/features/agents/lib/agentAutocompleteEligibility.ts:39) treats as deny.The whole relay directory would quietly stop being mentionable, with a green build.
The blast radius grew recently:
owner_pubkeyandchannel_idswere added to this structafter the original mapper, so it is five hand-mapped fields now, not three.
Validation
just desktop-tauri-testruns this (.github/workflows/ci.yml:204→cd desktop/src-tauri && cargo test --workspace, path-filtered ondesktop/src-tauri/**),so the test actually executes in CI rather than only existing.
Acceptance criterion checked by breaking it, not just by watching it pass: adding
#[serde(rename_all = "camelCase")]toRelayAgentInfomakes the test fail on the firstassertion (
owner_pubkeyabsent); removing it makes it pass again. A contract test thatdoes not go red when the contract breaks buys nothing.
cd desktop/src-tauri && cargo test --workspace relay_agent_info_wire_keys— 1 passedcargo fmtcleanNotes
The test lives next to the existing serde-contract tests in the same file
(
respond_to_serde_is_kebab_case, and theneeds_restart/restart_diffsnake_caseassertion at the end), so it follows the file's established shape rather than introducing
a new one.
Split out of #5483, which I closed as superseded by #6086 / #6182 / #6224 — those PRs
implemented the directory work it proposed, but nothing carried this contract test across.