Skip to content

Commit 2b7c4a9

Browse files
nuxRyan R. Fox
authored andcommitted
Add client preference tests (Group B) for balance-aware selector
Implement tests B1-B2 validating that client preferences override server payment method ordering: - B1: Server prefers Base, Client prefers Solana -> selects Solana - B2: Server prefers Solana, Client prefers Base -> selects Base Both tests verify the clientPreference parameter correctly reorders the accepts array before balance checking.
1 parent 17f6ba6 commit 2b7c4a9

2 files changed

Lines changed: 160 additions & 0 deletions

File tree

e2e/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"main": "dist/test.js",
66
"scripts": {
77
"test": "tsx test.ts",
8+
"test:unit": "tsx src/balance-aware-selector.test.ts",
89
"test:watch": "tsc --watch",
910
"setup": "./setup.sh",
1011
"setup:legacy": "./setup.sh --legacy",
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
import { selectPaymentMethod, PaymentMethod, BalanceChecker } from './balance-aware-selector';
2+
3+
/**
4+
* Balance-Aware Selector Tests - Group B: Client Preference
5+
*
6+
* These tests validate that the client can override the server's preferred
7+
* payment method ordering using the clientPreference parameter.
8+
*/
9+
10+
interface TestResult {
11+
testNumber: string;
12+
name: string;
13+
passed: boolean;
14+
expected: string;
15+
actual: string | null;
16+
error?: string;
17+
}
18+
19+
const BASE_USDC: PaymentMethod = {
20+
asset: 'USDC',
21+
network: 'eip155:84532',
22+
protocol: 'eip3009',
23+
};
24+
25+
const SOLANA_USDC: PaymentMethod = {
26+
asset: 'USDC',
27+
network: 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1',
28+
protocol: 'spl-transfer',
29+
};
30+
31+
// Balance checker that returns positive balance for both methods
32+
const bothHaveBalance: BalanceChecker = async () => 1000000n;
33+
34+
// Helper to identify payment method
35+
const methodId = (m: PaymentMethod | null): string => {
36+
if (!m) return 'null';
37+
return `${m.network}:${m.asset}`;
38+
};
39+
40+
// Client preference: prefer Solana over Base
41+
const preferSolana = (methods: PaymentMethod[]): PaymentMethod[] => {
42+
return [...methods].sort((a, b) => {
43+
const aIsSolana = a.network.startsWith('solana:');
44+
const bIsSolana = b.network.startsWith('solana:');
45+
if (aIsSolana && !bIsSolana) return -1;
46+
if (!aIsSolana && bIsSolana) return 1;
47+
return 0;
48+
});
49+
};
50+
51+
// Client preference: prefer Base over Solana
52+
const preferBase = (methods: PaymentMethod[]): PaymentMethod[] => {
53+
return [...methods].sort((a, b) => {
54+
const aIsBase = a.network.startsWith('eip155:');
55+
const bIsBase = b.network.startsWith('eip155:');
56+
if (aIsBase && !bIsBase) return -1;
57+
if (!aIsBase && bIsBase) return 1;
58+
return 0;
59+
});
60+
};
61+
62+
async function runTests(): Promise<void> {
63+
console.log('\n🧪 Balance-Aware Selector Tests - Group B: Client Preference\n');
64+
console.log('═'.repeat(60));
65+
66+
const results: TestResult[] = [];
67+
68+
// Test B1: Server order is Base first, Client prefers Solana, Client has Both
69+
// Expected: Solana USDC
70+
try {
71+
const serverAccepts = [BASE_USDC, SOLANA_USDC]; // Base first
72+
const selected = await selectPaymentMethod(serverAccepts, bothHaveBalance, preferSolana);
73+
74+
const passed = selected !== null && selected.network.startsWith('solana:');
75+
results.push({
76+
testNumber: 'B1',
77+
name: 'Server prefers Base, Client prefers Solana',
78+
passed,
79+
expected: methodId(SOLANA_USDC),
80+
actual: methodId(selected),
81+
});
82+
83+
if (passed) {
84+
console.log(` ✅ B1: Server prefers Base, Client prefers Solana → ${methodId(selected)}`);
85+
} else {
86+
console.log(` ❌ B1: Server prefers Base, Client prefers Solana`);
87+
console.log(` Expected: ${methodId(SOLANA_USDC)}`);
88+
console.log(` Actual: ${methodId(selected)}`);
89+
}
90+
} catch (error) {
91+
const errorMsg = error instanceof Error ? error.message : String(error);
92+
results.push({
93+
testNumber: 'B1',
94+
name: 'Server prefers Base, Client prefers Solana',
95+
passed: false,
96+
expected: methodId(SOLANA_USDC),
97+
actual: 'error',
98+
error: errorMsg,
99+
});
100+
console.log(` ❌ B1: Server prefers Base, Client prefers Solana`);
101+
console.log(` Error: ${errorMsg}`);
102+
}
103+
104+
// Test B2: Server order is Solana first, Client prefers Base, Client has Both
105+
// Expected: Base USDC
106+
try {
107+
const serverAccepts = [SOLANA_USDC, BASE_USDC]; // Solana first
108+
const selected = await selectPaymentMethod(serverAccepts, bothHaveBalance, preferBase);
109+
110+
const passed = selected !== null && selected.network.startsWith('eip155:');
111+
results.push({
112+
testNumber: 'B2',
113+
name: 'Server prefers Solana, Client prefers Base',
114+
passed,
115+
expected: methodId(BASE_USDC),
116+
actual: methodId(selected),
117+
});
118+
119+
if (passed) {
120+
console.log(` ✅ B2: Server prefers Solana, Client prefers Base → ${methodId(selected)}`);
121+
} else {
122+
console.log(` ❌ B2: Server prefers Solana, Client prefers Base`);
123+
console.log(` Expected: ${methodId(BASE_USDC)}`);
124+
console.log(` Actual: ${methodId(selected)}`);
125+
}
126+
} catch (error) {
127+
const errorMsg = error instanceof Error ? error.message : String(error);
128+
results.push({
129+
testNumber: 'B2',
130+
name: 'Server prefers Solana, Client prefers Base',
131+
passed: false,
132+
expected: methodId(BASE_USDC),
133+
actual: 'error',
134+
error: errorMsg,
135+
});
136+
console.log(` ❌ B2: Server prefers Solana, Client prefers Base`);
137+
console.log(` Error: ${errorMsg}`);
138+
}
139+
140+
// Summary
141+
console.log('\n' + '═'.repeat(60));
142+
const passed = results.filter(r => r.passed).length;
143+
const failed = results.filter(r => !r.passed).length;
144+
145+
console.log('\n📊 Test Summary - Group B: Client Preference');
146+
console.log(` ✅ Passed: ${passed}`);
147+
console.log(` ❌ Failed: ${failed}`);
148+
console.log(` 📈 Total: ${results.length}\n`);
149+
150+
if (failed > 0) {
151+
process.exit(1);
152+
}
153+
}
154+
155+
// Run tests when executed directly
156+
runTests().catch(error => {
157+
console.error('Test runner failed:', error);
158+
process.exit(1);
159+
});

0 commit comments

Comments
 (0)