# All packages
bun test
# Just the core package
bun run test:core
# Just the SIWS verifier
bun run test:siws-verify
# A single test file
bun test packages/siws-verify/tests/verifySiws.test.ts
# A single test by name (substring match)
bun test --test-name-pattern "threads signedData"CI runs the same bun test command on every push and pull request — see
.github/workflows/ci.yml.
buildSiwsMessageformat: the message round-trips throughparseSiwsMessagewith every field intact. This is the canary for drift between the test helper'sbuildSiwsMessagecopy and the library's internal one — if either changes without the other, the round-trip breaks.parseSiwsMessagerejection cases: empty input, missing domain header, missing address line, and graceful handling of absent optional fields.signInWithStellarthreading: thesignedDatafield flows from the connector'ssignMessage()return value into theSignInResultunchanged. Also covers backward-compat (connector omitssignedData), error cases (connector doesn't support signMessage, signer address mismatch), and expiration defaults.
signMessagereturnssignedData = base64(utf8(message))— the raw bytes Freighter signs.- Message and opts are passed through to
@stellar/freighter-apiverbatim. - Accepts both response shapes Freighter has shipped (raw Buffer and pre-encoded base64 string).
- Throws
ConnectErroron emptysignedMessage. - Normalizes wallet errors (e.g. "user rejected") into
ConnectError.
signMessagesurfacesres.signed_messageassignedData(base64 of hex-decoded bytes) — the regression guard for the bug that motivated the unifiedsignedDatafix.- Does NOT fall back to the plaintext message as
signedData. - Passes
pubkeythrough to the Albedo intent. - Throws
ConnectErrorwhen Albedo doesn't returnsigned_message. - Normalizes thrown errors into
ConnectError.
signMessageusesresult.fullMessageassignedDatawhen present.- Falls back to
result.messagewhenfullMessageis missing (older xBull SDK). - Falls back to the input
messagewhen neither is in the response (defensive). - Passes address and networkPassphrase opts through to the bridge.
- Normalizes thrown errors into
ConnectError.
- Positive cases: each wallet profile (Freighter / Albedo / xBull)
with
signedDatapopulated → verification succeeds. - Envelope checks: rejects on invalid message, address mismatch, domain mismatch, nonce mismatch, expired message, invalid expiration date.
- Signature verification failures: tampered signature rejected for
each wallet profile; mismatched
signedData(connector reports the wrong bytes) rejected. - Backward compatibility:
signedDataomitted → fallback path passes for direct signers (Freighter), fails loudly for transformative signers (Albedo, xBull). - Custom
verifySignatureFn: callback receives all four fields (message,signedData,signature,address); sync and async callbacks both work; custom result surfaces asok=trueorok=false. - Claims shape: verifies the full claims object on success.
- Accepts base64-encoded signatures (Freighter/Ledger style).
- Accepts hex-encoded signatures (Albedo style), both lowercase and uppercase.
- Rejects truncated signatures (wrong byte count).
- Rejects garbage that is neither valid base64 nor valid hex.
- Regression guard: does not misfire on pure-alphanumeric base64 signatures of even length (the old regex heuristic could).
The tests use real ed25519 keypairs (via @stellar/stellar-sdk's
Keypair.random()) and sign real messages with them, then verify with
the actual verifySiws function. This is an end-to-end test of the
verifier's cryptography, not a mock-based unit test — a bug in the
signature decoding or verification logic will be caught.
Wallet SDKs (@stellar/freighter-api, @albedo-link/intent,
@creit.tech/xbull-wallet-connect) are mocked with bun:test's
mock.module() so the connector tests don't require a real wallet
extension. Each mock delegates to a per-test fakeApi object so tests
can control exactly what the wallet "returns" and assert how the
connector transforms it.
When you add a new connector, add a test file at
packages/core/tests/connectors/<name>.test.ts that covers at minimum:
signMessagereturnssignedData— base64 of the exact bytes the wallet signed. If the wallet signs the raw message, that'sbase64(utf8(message)). If it transforms the message first (like Albedo), surface the transformed bytes.signMessagepasses opts through to the underlying SDK.signMessagenormalizes SDK errors intoConnectError.- Add the wallet to the
PROFILESarray inpackages/siws-verify/tests/verifySiws.test.tswith abytesSignedByWalletfunction that simulates what the wallet signs. The existing positive/negative/backward-compat tests will then automatically cover the new wallet.