Repository navigation
test: expand spec vectors for edge cases and protocol boundaries #348
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,14 +5,6 @@ on: | |
| paths: | ||
| - "packages/core-go/**" | ||
| - "spec/**" | ||
| # `verify-parity` owns this file's CI surface; skip here to avoid | ||
| # duplicate runs on vectors-only PRs. | ||
| paths-ignore: | ||
| - "spec/vectors.json" | ||
| # Note: paths + paths-ignore combine as an AND across all changed files. | ||
| # If a PR changes spec/vectors.json *together with* packages/core-go/**, | ||
| # this workflow is skipped; verify-parity.yml still runs the identical | ||
| # commands, so functional coverage is preserved under a different check. | ||
|
|
||
| jobs: | ||
| test: | ||
|
|
@@ -23,3 +15,6 @@ jobs: | |
| with: | ||
| go-version: "1.21" | ||
| - run: cd packages/core-go && go test ./... | ||
| - run: cd packages/core-go && go test ./... -run TestSpecVectors -v | ||
| env: | ||
| SPEC_VECTORS: spec/vectors.json | ||
|
Comment on lines
+18
to
+20
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Select the actual Go vector test. The vector test is named 🧰 Tools🪛 zizmor (1.30.1)[warning] 10-21: overly broad permissions (excessive-permissions): default permissions used due to no permissions: block (excessive-permissions) 🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,12 +1,14 @@ | ||
| import 'dart:convert'; | ||
| import 'dart:io'; | ||
| import 'package:test/test.dart'; | ||
| import 'package:test/test.dart'; | ||
| import 'package:stellar_address_kit/stellar_address_kit.dart'; | ||
|
|
||
| const legacyVectorG = "GA7QYNF7SZFX4X7X5JFZZ3UQ6BXHDSY2RKVKZKX5FFQJ1ZMZX1"; | ||
| const legacyVectorMPrefix = "MA7QYNF7SZFX4X7X5JFZZ3UQ6BXHDSY2RKVKZKX5FFQJ1ZMZX1"; | ||
| const legacyVectorCPrefix = "CA7QYNF7SZFX4X7X5JFZZ3UQ6BXHDSY2RKVKZKX5FFQJ1ZMZX1"; | ||
|
|
||
| const validG = "GAYCUYT553C5LHVE2XPW5GMEJT4BXGM7AHMJWLAPZP53KJO7EIQADRSI"; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Remove the duplicate declarations and statement fragments. Line 11 redeclares Also applies to: 80-80, 106-106, 119-119 🤖 Prompt for AI Agents |
||
| const validG = "GAYCUYT553C5LHVE2XPW5GMEJT4BXGM7AHMJWLAPZP53KJO7EIQADRSI"; | ||
| const validC = "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC"; | ||
|
|
||
|
|
@@ -56,6 +58,7 @@ void main() { | |
| final BigInt id = BigInt.parse(input['id'].toString()); | ||
| final String result = MuxedAddress.encode(baseG: baseG, id: id); | ||
| expect(result, expected['mAddress']); | ||
| expect(result, expected['mAddress']); | ||
| break; | ||
|
|
||
| case 'muxed_decode': | ||
|
|
@@ -74,6 +77,7 @@ void main() { | |
| case 'detect': | ||
| final kind = detect(input['address'].toString()); | ||
| if (expected.containsKey('kind')) { | ||
| expect(kind?.toString().split('.').last.toUpperCase(), | ||
| expect(kind?.toString().split('.').last.toUpperCase(), | ||
| expected['kind']); | ||
| } else { | ||
|
|
@@ -99,6 +103,7 @@ void main() { | |
|
|
||
| expect(result.destinationBaseAccount, | ||
| normalizeExpectedBaseAccount(expected['destinationBaseAccount'])); | ||
| normalizeExpectedBaseAccount(expected['destinationBaseAccount'])); | ||
|
|
||
| if (expected['routingId'] != null) { | ||
| expect(result.id, BigInt.parse(expected['routingId'].toString())); | ||
|
|
@@ -111,6 +116,7 @@ void main() { | |
| if (expected.containsKey('warnings')) { | ||
| final List<dynamic> expectedWarnings = | ||
| expected['warnings'] as List<dynamic>; | ||
| expected['warnings'] as List<dynamic>; | ||
| expect(result.warnings.length, expectedWarnings.length); | ||
| for (var i = 0; i < expectedWarnings.length; i++) { | ||
| final eW = expectedWarnings[i] as Map<String, dynamic>; | ||
|
|
@@ -121,6 +127,7 @@ void main() { | |
| if (expected.containsKey('destinationError')) { | ||
| final eE = expected['destinationError'] as Map<String, dynamic>; | ||
| expect(result.destinationError?.code, eE['code']); | ||
| expect(result.destinationError?.code, eE['code']); | ||
| } | ||
| } on ExtractRoutingException { | ||
| if (destination.startsWith('C')) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -165,5 +165,105 @@ void main() { | |
| } | ||
| }); | ||
| }); | ||
|
|
||
| group('protocol boundaries and adversarial inputs', () { | ||
| const validG = 'GCALNQQBXAPZ2WIRSDDBMSTAKCUH5SG6U76YBFLQLIXJTF7FE5AX7AOO'; | ||
|
|
||
| test('handles uint64 boundary memo values', () { | ||
| const boundaries = <String>[ | ||
| '0', | ||
| '1', | ||
| '18446744073709551615', | ||
| '18446744073709551616', | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Assert rejection of the out-of-range
🤖 Prompt for AI Agents |
||
| ]; | ||
| for (final value in boundaries) { | ||
| final uri = | ||
| 'web+stellar:pay?destination=$validG&memo=$value&memo_type=MEMO_ID'; | ||
| final result = extractRoutingFromURI(uri); | ||
| expect(result.success, isTrue, reason: 'memo=$value'); | ||
| if (result is ExtractRoutingFromUriSuccess) { | ||
| expect(result.rawParams.memo, value); | ||
| expect(result.rawParams.memoType, 'MEMO_ID'); | ||
| } | ||
| } | ||
| }); | ||
|
|
||
| test('accepts secret seed, pre-auth, and hash signer destinations', () { | ||
| const signers = <String>[ | ||
| 'SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', | ||
| 'TAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', | ||
| 'XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', | ||
| ]; | ||
| for (final signer in signers) { | ||
| final uri = 'web+stellar:pay?destination=$signer'; | ||
| final result = extractRoutingFromURI(uri); | ||
| expect(result.success, isFalse, reason: 'signer=$signer'); | ||
| if (result is ExtractRoutingFromUriFailure) { | ||
| expect(result.code, Sep7UriErrorCode.invalidDestination); | ||
|
Comment on lines
+200
to
+202
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Align invalid-destination assertions with the URI result contract.
Also applies to: 240-242 🤖 Prompt for AI Agents |
||
| } | ||
| } | ||
| }); | ||
|
|
||
| test('preserves multi-byte UTF-8 memo text', () { | ||
| const memo = '\u00e9\u00e8\u00ea \u4f60\u597d \ud83d\ude00'; | ||
| final encoded = Uri.encodeComponent(memo); | ||
| final uri = | ||
| 'web+stellar:pay?destination=$validG&memo=$encoded&memo_type=MEMO_TEXT'; | ||
| final result = extractRoutingFromURI(uri); | ||
| expect(result.success, isTrue); | ||
| if (result is ExtractRoutingFromUriSuccess) { | ||
| expect(result.rawParams.memo, memo); | ||
| expect(result.rawParams.memoType, 'MEMO_TEXT'); | ||
| } | ||
| }); | ||
|
|
||
| test('rejects zero-width and bidi override characters in destination', () { | ||
| const adversarial = <String>[ | ||
| '\u200b', | ||
| '\u200c', | ||
| '\u200d', | ||
| '\u202a', | ||
| '\u202b', | ||
| '\u202c', | ||
| '\u202d', | ||
| '\u202e', | ||
| '\u2066', | ||
| '\u2067', | ||
| '\u2068', | ||
| '\u2069', | ||
| '\ufeff', | ||
| ]; | ||
| for (final hidden in adversarial) { | ||
| final uri = | ||
| 'web+stellar:pay?destination=${Uri.encodeComponent('$validG$hidden')}'; | ||
| final result = extractRoutingFromURI(uri); | ||
| expect(result.success, isFalse, reason: 'hidden=U+${hidden.codeUnitAt(0).toRadixString(16)}'); | ||
| if (result is ExtractRoutingFromUriFailure) { | ||
| expect(result.code, Sep7UriErrorCode.invalidDestination); | ||
| } | ||
| } | ||
| }); | ||
|
|
||
| test('rejects malformed SEP-0007 URIs', () { | ||
| const malformed = <String>[ | ||
| 'web+stellar:', | ||
| 'web+stellar:?', | ||
| 'web+stellar:pay?', | ||
| 'web+stellar:pay?destination', | ||
| 'web+stellar:pay?destination=', | ||
| 'web+stellar:pay?=value', | ||
| 'web+stellar:pay?destination=$validG&memo_type=INVALID', | ||
| 'web+stellar:pay?destination=$validG&memo_type=MEMO_ID&memo=notanumber', | ||
| 'web+stellar:pay?destination=$validG&amount=notanumber', | ||
| 'web+stellar:pay?destination=$validG&asset_code=USDC', | ||
| 'web+stellar:pay?destination=$validG&asset_issuer=GAP5LETOV6YIE62YAM56STDANPRDO7ZFDBGSNHJQIYGGKSMOZAHOOS2S', | ||
| 'web+stellar:pay?destination=$validG&callback=notaurl', | ||
|
Comment on lines
+257
to
+260
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift Do not expect unvalidated optional parameters to fail. The parser stores 🤖 Prompt for AI Agents |
||
| ]; | ||
| for (final uri in malformed) { | ||
| final result = extractRoutingFromURI(uri); | ||
| expect(result.success, isFalse, reason: 'uri=$uri'); | ||
| } | ||
| }); | ||
| }); | ||
| }); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
Repository: Boxkit-Labs/stellar-address-kit
Length of output: 978
Remove the nonexistent vector runner.
ci-dart.ymlrunsnode spec/run-vectors.js, but the reviewed head contains no such file and no earlier step creates it. Node can therefore fail withMODULE_NOT_FOUNDbefore the Dart tests run. The existingnode spec/validate.jsstep should remain.Suggested fix
- name: Spec validator run: node spec/validate.js - - name: Spec vectors (edge cases) - run: node spec/run-vectors.js - id: setup-chrome📝 Committable suggestion
🧰 Tools
🪛 zizmor (1.30.1)
[warning] 10-30: overly broad permissions (excessive-permissions): default permissions used due to no permissions: block
(excessive-permissions)
[warning] 18-18: ad-hoc installation of packages (adhoc-packages): installs a package outside of a lockfile
(adhoc-packages)
🤖 Prompt for AI Agents