performance.integration.test.ts 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776
  1. /**
  2. * Integration test for performance requirements
  3. *
  4. * This test verifies that the credential manager meets all performance requirements:
  5. * 1. Hot reload: <100ms for configuration file changes
  6. * 2. Signing operations: <50ms per signature
  7. * 3. Memory usage: <50MB total
  8. * 4. Concurrent operations: Support 50+ accounts
  9. * 5. Throughput: Handle high-frequency operations efficiently
  10. *
  11. * Tests MUST FAIL initially until implementation is provided.
  12. */
  13. import { describe, test, expect, beforeEach, afterEach } from '@jest/globals';
  14. import * as fs from 'fs/promises';
  15. import * as path from 'path';
  16. import * as os from 'os';
  17. // Import types (this import will fail until types are implemented)
  18. import type {
  19. ICredentialManager,
  20. SignResult,
  21. LoadResult,
  22. Account,
  23. Platform,
  24. ConfigFile
  25. } from '@/specs/001-credential-manager/contracts/credential-manager';
  26. describe('Performance Requirements Integration Tests', () => {
  27. let credentialManager: ICredentialManager;
  28. let tempDir: string;
  29. let configPath: string;
  30. // Performance test utilities
  31. const measureTime = async <T>(operation: () => Promise<T>): Promise<{ result: T; duration: number }> => {
  32. const startTime = Date.now();
  33. const result = await operation();
  34. const duration = Date.now() - startTime;
  35. return { result, duration };
  36. };
  37. const measureMemory = (): number => {
  38. if (process.memoryUsage) {
  39. return process.memoryUsage().heapUsed / (1024 * 1024); // MB
  40. }
  41. return 0;
  42. };
  43. beforeEach(async () => {
  44. // This will fail until CredentialManager is implemented
  45. const { CredentialManager } = await import('@/core/credential-manager/CredentialManager');
  46. credentialManager = new CredentialManager();
  47. // Create temporary directory
  48. tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'performance-test-'));
  49. configPath = path.join(tempDir, 'performance-config.json');
  50. });
  51. afterEach(async () => {
  52. // Clean up
  53. credentialManager.stopWatching();
  54. try {
  55. await fs.rm(tempDir, { recursive: true, force: true });
  56. } catch (error) {
  57. // Ignore cleanup errors
  58. }
  59. });
  60. describe('Hot Reload Performance (<100ms)', () => {
  61. test('should reload small configuration within 100ms', async () => {
  62. // Arrange
  63. const smallConfig: ConfigFile = {
  64. version: "1.0",
  65. accounts: [
  66. {
  67. id: "small-test-account",
  68. platform: Platform.PACIFICA,
  69. name: "Small Test Account",
  70. credentials: {
  71. type: "ed25519",
  72. privateKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
  73. }
  74. }
  75. ]
  76. };
  77. await fs.writeFile(configPath, JSON.stringify(smallConfig));
  78. await credentialManager.loadConfig(configPath);
  79. const reloadPromise = new Promise<{ accounts: Account[]; reloadTime: number }>((resolve) => {
  80. const startTime = Date.now();
  81. credentialManager.watchConfig(configPath, (accounts) => {
  82. const reloadTime = Date.now() - startTime;
  83. resolve({ accounts, reloadTime });
  84. });
  85. });
  86. // Wait for watcher to initialize
  87. await new Promise(resolve => setTimeout(resolve, 50));
  88. // Act - modify configuration
  89. const modifiedConfig: ConfigFile = {
  90. ...smallConfig,
  91. accounts: [
  92. ...smallConfig.accounts,
  93. {
  94. id: "added-account",
  95. platform: Platform.BINANCE,
  96. name: "Added Account",
  97. credentials: {
  98. type: "hmac",
  99. apiKey: "test-key",
  100. secretKey: "test-secret"
  101. }
  102. }
  103. ]
  104. };
  105. await fs.writeFile(configPath, JSON.stringify(modifiedConfig, null, 2));
  106. // Assert
  107. const { accounts, reloadTime } = await Promise.race([
  108. reloadPromise,
  109. new Promise<never>((_, reject) =>
  110. setTimeout(() => reject(new Error('Reload timeout')), 5000)
  111. )
  112. ]);
  113. expect(reloadTime).toBeLessThan(100); // Core requirement
  114. expect(accounts).toHaveLength(2);
  115. });
  116. test('should reload medium configuration (10 accounts) within 100ms', async () => {
  117. // Arrange - 10 accounts
  118. const mediumConfig: ConfigFile = {
  119. version: "1.0",
  120. accounts: Array(10).fill(null).map((_, index) => ({
  121. id: `medium-account-${index}`,
  122. platform: Platform.PACIFICA,
  123. name: `Medium Account ${index}`,
  124. credentials: {
  125. type: "ed25519",
  126. privateKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
  127. }
  128. }))
  129. };
  130. await fs.writeFile(configPath, JSON.stringify(mediumConfig));
  131. // Act & Assert
  132. const { result, duration } = await measureTime(() =>
  133. credentialManager.loadConfig(configPath)
  134. );
  135. expect(result.success).toBe(true);
  136. expect(duration).toBeLessThan(100);
  137. expect(result.loadTime).toBeLessThan(100);
  138. });
  139. test('should reload large configuration (50 accounts) within 100ms', async () => {
  140. // Arrange - 50 accounts (edge of requirement)
  141. const largeConfig: ConfigFile = {
  142. version: "1.0",
  143. accounts: Array(50).fill(null).map((_, index) => ({
  144. id: `large-account-${index}`,
  145. platform: index % 3 === 0 ? Platform.PACIFICA :
  146. index % 3 === 1 ? Platform.ASTER : Platform.BINANCE,
  147. name: `Large Account ${index}`,
  148. credentials: index % 3 === 0 ? {
  149. type: "ed25519",
  150. privateKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
  151. } : index % 3 === 1 ? {
  152. type: "eip191",
  153. privateKey: "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
  154. } : {
  155. type: "hmac",
  156. apiKey: `api-key-${index}`,
  157. secretKey: `secret-key-${index}`
  158. }
  159. }))
  160. };
  161. await fs.writeFile(configPath, JSON.stringify(largeConfig));
  162. // Act & Assert
  163. const { result, duration } = await measureTime(() =>
  164. credentialManager.loadConfig(configPath)
  165. );
  166. expect(result.success).toBe(true);
  167. expect(duration).toBeLessThan(100); // Critical requirement
  168. expect(result.loadTime).toBeLessThan(100);
  169. expect(result.accounts).toHaveLength(50);
  170. });
  171. test('should handle incremental updates efficiently', async () => {
  172. // Arrange - start with base configuration
  173. const baseConfig: ConfigFile = {
  174. version: "1.0",
  175. accounts: Array(20).fill(null).map((_, index) => ({
  176. id: `base-account-${index}`,
  177. platform: Platform.PACIFICA,
  178. name: `Base Account ${index}`,
  179. credentials: {
  180. type: "ed25519",
  181. privateKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
  182. }
  183. }))
  184. };
  185. await fs.writeFile(configPath, JSON.stringify(baseConfig));
  186. await credentialManager.loadConfig(configPath);
  187. const reloadTimes: number[] = [];
  188. // Setup reload measurement
  189. const reloadPromise = new Promise<void>((resolve) => {
  190. let reloadCount = 0;
  191. credentialManager.watchConfig(configPath, () => {
  192. reloadTimes.push(Date.now());
  193. reloadCount++;
  194. if (reloadCount === 5) resolve();
  195. });
  196. });
  197. // Wait for watcher
  198. await new Promise(resolve => setTimeout(resolve, 50));
  199. // Act - make incremental changes
  200. for (let i = 0; i < 5; i++) {
  201. const updatedConfig = {
  202. ...baseConfig,
  203. accounts: [
  204. ...baseConfig.accounts,
  205. {
  206. id: `incremental-account-${i}`,
  207. platform: Platform.BINANCE,
  208. name: `Incremental Account ${i}`,
  209. credentials: {
  210. type: "hmac",
  211. apiKey: `key-${i}`,
  212. secretKey: `secret-${i}`
  213. }
  214. }
  215. ]
  216. };
  217. const startTime = Date.now();
  218. await fs.writeFile(configPath, JSON.stringify(updatedConfig));
  219. // Wait for this reload to complete
  220. await new Promise(resolve => setTimeout(resolve, 120));
  221. }
  222. // Assert - each reload should be fast
  223. await reloadPromise;
  224. expect(reloadTimes).toHaveLength(5);
  225. });
  226. });
  227. describe('Signing Performance (<50ms)', () => {
  228. test('should sign Pacifica messages within 50ms', async () => {
  229. // Arrange
  230. const config: ConfigFile = {
  231. version: "1.0",
  232. accounts: [
  233. {
  234. id: "pacifica-perf-account",
  235. platform: Platform.PACIFICA,
  236. name: "Pacifica Performance Account",
  237. credentials: {
  238. type: "ed25519",
  239. privateKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
  240. }
  241. }
  242. ]
  243. };
  244. await fs.writeFile(configPath, JSON.stringify(config));
  245. await credentialManager.loadConfig(configPath);
  246. const message = new TextEncoder().encode("Performance test message");
  247. // Act & Assert - multiple signing operations
  248. for (let i = 0; i < 10; i++) {
  249. const { result, duration } = await measureTime(() =>
  250. credentialManager.sign("pacifica-perf-account", message)
  251. );
  252. expect(result.success).toBe(true);
  253. expect(duration).toBeLessThan(50); // Core requirement
  254. }
  255. });
  256. test('should sign Aster messages within 50ms', async () => {
  257. // Arrange
  258. const config: ConfigFile = {
  259. version: "1.0",
  260. accounts: [
  261. {
  262. id: "aster-perf-account",
  263. platform: Platform.ASTER,
  264. name: "Aster Performance Account",
  265. credentials: {
  266. type: "eip191",
  267. privateKey: "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
  268. }
  269. }
  270. ]
  271. };
  272. await fs.writeFile(configPath, JSON.stringify(config));
  273. await credentialManager.loadConfig(configPath);
  274. const message = new TextEncoder().encode("Aster performance test");
  275. // Act & Assert
  276. for (let i = 0; i < 10; i++) {
  277. const { result, duration } = await measureTime(() =>
  278. credentialManager.sign("aster-perf-account", message)
  279. );
  280. expect(result.success).toBe(true);
  281. expect(duration).toBeLessThan(50);
  282. }
  283. });
  284. test('should sign Binance messages within 50ms', async () => {
  285. // Arrange
  286. const config: ConfigFile = {
  287. version: "1.0",
  288. accounts: [
  289. {
  290. id: "binance-perf-account",
  291. platform: Platform.BINANCE,
  292. name: "Binance Performance Account",
  293. credentials: {
  294. type: "hmac",
  295. apiKey: "performance-api-key",
  296. secretKey: "performance-secret-key"
  297. }
  298. }
  299. ]
  300. };
  301. await fs.writeFile(configPath, JSON.stringify(config));
  302. await credentialManager.loadConfig(configPath);
  303. const message = new TextEncoder().encode("symbol=BTCUSDT&side=BUY&type=MARKET&quantity=0.1");
  304. // Act & Assert
  305. for (let i = 0; i < 10; i++) {
  306. const { result, duration } = await measureTime(() =>
  307. credentialManager.sign("binance-perf-account", message)
  308. );
  309. expect(result.success).toBe(true);
  310. expect(duration).toBeLessThan(50);
  311. }
  312. });
  313. test('should handle concurrent signing requests efficiently', async () => {
  314. // Arrange - multiple accounts
  315. const config: ConfigFile = {
  316. version: "1.0",
  317. accounts: [
  318. {
  319. id: "concurrent-pacifica",
  320. platform: Platform.PACIFICA,
  321. name: "Concurrent Pacifica",
  322. credentials: {
  323. type: "ed25519",
  324. privateKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
  325. }
  326. },
  327. {
  328. id: "concurrent-aster",
  329. platform: Platform.ASTER,
  330. name: "Concurrent Aster",
  331. credentials: {
  332. type: "eip191",
  333. privateKey: "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
  334. }
  335. },
  336. {
  337. id: "concurrent-binance",
  338. platform: Platform.BINANCE,
  339. name: "Concurrent Binance",
  340. credentials: {
  341. type: "hmac",
  342. apiKey: "concurrent-api-key",
  343. secretKey: "concurrent-secret-key"
  344. }
  345. }
  346. ]
  347. };
  348. await fs.writeFile(configPath, JSON.stringify(config));
  349. await credentialManager.loadConfig(configPath);
  350. const message = new TextEncoder().encode("Concurrent test message");
  351. // Act - create many concurrent signing requests
  352. const concurrentOperations = Array(20).fill(null).map((_, index) => {
  353. const accountId = index % 3 === 0 ? "concurrent-pacifica" :
  354. index % 3 === 1 ? "concurrent-aster" : "concurrent-binance";
  355. return measureTime(() => credentialManager.sign(accountId, message));
  356. });
  357. const startTime = Date.now();
  358. const results = await Promise.all(concurrentOperations);
  359. const totalDuration = Date.now() - startTime;
  360. // Assert - all should complete successfully and quickly
  361. results.forEach(({ result, duration }) => {
  362. expect(result.success).toBe(true);
  363. expect(duration).toBeLessThan(50); // Individual operation
  364. });
  365. // Total time should be reasonable (not just sum of individual times)
  366. expect(totalDuration).toBeLessThan(500); // 20 operations in <500ms
  367. });
  368. test('should maintain performance under sustained load', async () => {
  369. // Arrange
  370. const config: ConfigFile = {
  371. version: "1.0",
  372. accounts: [
  373. {
  374. id: "sustained-load-account",
  375. platform: Platform.PACIFICA,
  376. name: "Sustained Load Account",
  377. credentials: {
  378. type: "ed25519",
  379. privateKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
  380. }
  381. }
  382. ]
  383. };
  384. await fs.writeFile(configPath, JSON.stringify(config));
  385. await credentialManager.loadConfig(configPath);
  386. const message = new TextEncoder().encode("Sustained load test");
  387. const durations: number[] = [];
  388. // Act - sustained signing operations
  389. for (let i = 0; i < 100; i++) {
  390. const { result, duration } = await measureTime(() =>
  391. credentialManager.sign("sustained-load-account", message)
  392. );
  393. expect(result.success).toBe(true);
  394. durations.push(duration);
  395. // Small delay to simulate real usage
  396. await new Promise(resolve => setTimeout(resolve, 10));
  397. }
  398. // Assert - performance should not degrade significantly
  399. const averageDuration = durations.reduce((a, b) => a + b, 0) / durations.length;
  400. const maxDuration = Math.max(...durations);
  401. expect(averageDuration).toBeLessThan(50);
  402. expect(maxDuration).toBeLessThan(100); // Allow some variance but not too much
  403. });
  404. });
  405. describe('Memory Usage (<50MB)', () => {
  406. test('should maintain memory usage under 50MB with many accounts', async () => {
  407. // Arrange - large configuration
  408. const largeConfig: ConfigFile = {
  409. version: "1.0",
  410. accounts: Array(100).fill(null).map((_, index) => ({
  411. id: `memory-test-account-${index}`,
  412. platform: index % 3 === 0 ? Platform.PACIFICA :
  413. index % 3 === 1 ? Platform.ASTER : Platform.BINANCE,
  414. name: `Memory Test Account ${index}`,
  415. credentials: index % 3 === 0 ? {
  416. type: "ed25519",
  417. privateKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
  418. } : index % 3 === 1 ? {
  419. type: "eip191",
  420. privateKey: "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
  421. } : {
  422. type: "hmac",
  423. apiKey: `memory-api-key-${index}`,
  424. secretKey: `memory-secret-key-${index}`
  425. }
  426. }))
  427. };
  428. const initialMemory = measureMemory();
  429. // Act
  430. await fs.writeFile(configPath, JSON.stringify(largeConfig));
  431. await credentialManager.loadConfig(configPath);
  432. // Perform some operations to fully load everything
  433. for (let i = 0; i < 10; i++) {
  434. const accountId = `memory-test-account-${i * 10}`;
  435. const message = new TextEncoder().encode(`Memory test ${i}`);
  436. await credentialManager.sign(accountId, message);
  437. }
  438. const finalMemory = measureMemory();
  439. const memoryIncrease = finalMemory - initialMemory;
  440. // Assert - memory increase should be reasonable
  441. expect(memoryIncrease).toBeLessThan(50); // Core requirement
  442. });
  443. test('should not leak memory during repeated reloads', async () => {
  444. // Arrange
  445. const baseConfig: ConfigFile = {
  446. version: "1.0",
  447. accounts: Array(20).fill(null).map((_, index) => ({
  448. id: `leak-test-account-${index}`,
  449. platform: Platform.PACIFICA,
  450. name: `Leak Test Account ${index}`,
  451. credentials: {
  452. type: "ed25519",
  453. privateKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
  454. }
  455. }))
  456. };
  457. await fs.writeFile(configPath, JSON.stringify(baseConfig));
  458. const initialMemory = measureMemory();
  459. // Act - repeated reloads
  460. for (let i = 0; i < 10; i++) {
  461. const modifiedConfig = {
  462. ...baseConfig,
  463. accounts: baseConfig.accounts.map(account => ({
  464. ...account,
  465. name: `${account.name} - Reload ${i}`
  466. }))
  467. };
  468. await fs.writeFile(configPath, JSON.stringify(modifiedConfig));
  469. await credentialManager.loadConfig(configPath);
  470. // Perform some operations
  471. const message = new TextEncoder().encode(`Reload test ${i}`);
  472. await credentialManager.sign("leak-test-account-0", message);
  473. }
  474. const finalMemory = measureMemory();
  475. const memoryIncrease = finalMemory - initialMemory;
  476. // Assert - should not accumulate significant memory
  477. expect(memoryIncrease).toBeLessThan(10); // Should be minimal increase
  478. });
  479. });
  480. describe('Scalability (50+ Accounts)', () => {
  481. test('should support exactly 50 accounts efficiently', async () => {
  482. // Arrange - exactly 50 accounts
  483. const fiftyAccountConfig: ConfigFile = {
  484. version: "1.0",
  485. accounts: Array(50).fill(null).map((_, index) => ({
  486. id: `scalability-account-${index}`,
  487. platform: index % 3 === 0 ? Platform.PACIFICA :
  488. index % 3 === 1 ? Platform.ASTER : Platform.BINANCE,
  489. name: `Scalability Account ${index}`,
  490. credentials: index % 3 === 0 ? {
  491. type: "ed25519",
  492. privateKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
  493. } : index % 3 === 1 ? {
  494. type: "eip191",
  495. privateKey: "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
  496. } : {
  497. type: "hmac",
  498. apiKey: `scalability-api-key-${index}`,
  499. secretKey: `scalability-secret-key-${index}`
  500. }
  501. }))
  502. };
  503. // Act & Assert
  504. await fs.writeFile(configPath, JSON.stringify(fiftyAccountConfig));
  505. const { result, duration } = await measureTime(() =>
  506. credentialManager.loadConfig(configPath)
  507. );
  508. expect(result.success).toBe(true);
  509. expect(result.accounts).toHaveLength(50);
  510. expect(duration).toBeLessThan(100); // Should still meet reload requirement
  511. // Verify all accounts are accessible
  512. const allAccounts = credentialManager.listAccounts();
  513. expect(allAccounts).toHaveLength(50);
  514. // Test random access performance
  515. for (let i = 0; i < 10; i++) {
  516. const randomIndex = Math.floor(Math.random() * 50);
  517. const accountId = `scalability-account-${randomIndex}`;
  518. const account = credentialManager.getAccount(accountId);
  519. expect(account).not.toBeNull();
  520. }
  521. });
  522. test('should handle concurrent operations across all 50 accounts', async () => {
  523. // Arrange - 50 accounts
  524. const manyAccountsConfig: ConfigFile = {
  525. version: "1.0",
  526. accounts: Array(50).fill(null).map((_, index) => ({
  527. id: `concurrent-account-${index}`,
  528. platform: Platform.PACIFICA, // Use single platform for simplicity
  529. name: `Concurrent Account ${index}`,
  530. credentials: {
  531. type: "ed25519",
  532. privateKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
  533. }
  534. }))
  535. };
  536. await fs.writeFile(configPath, JSON.stringify(manyAccountsConfig));
  537. await credentialManager.loadConfig(configPath);
  538. // Act - concurrent operations across all accounts
  539. const message = new TextEncoder().encode("Concurrent scalability test");
  540. const concurrentOperations = Array(50).fill(null).map((_, index) =>
  541. measureTime(() => credentialManager.sign(`concurrent-account-${index}`, message))
  542. );
  543. const startTime = Date.now();
  544. const results = await Promise.all(concurrentOperations);
  545. const totalDuration = Date.now() - startTime;
  546. // Assert - all operations should succeed
  547. results.forEach(({ result, duration }, index) => {
  548. expect(result.success).toBe(true);
  549. expect(duration).toBeLessThan(50); // Individual operation requirement
  550. });
  551. // Total time should scale reasonably
  552. expect(totalDuration).toBeLessThan(1000); // 50 operations in <1 second
  553. });
  554. test('should maintain performance with over 50 accounts', async () => {
  555. // Arrange - 75 accounts (beyond minimum requirement)
  556. const manyAccountsConfig: ConfigFile = {
  557. version: "1.0",
  558. accounts: Array(75).fill(null).map((_, index) => ({
  559. id: `beyond-fifty-account-${index}`,
  560. platform: index % 3 === 0 ? Platform.PACIFICA :
  561. index % 3 === 1 ? Platform.ASTER : Platform.BINANCE,
  562. name: `Beyond Fifty Account ${index}`,
  563. credentials: index % 3 === 0 ? {
  564. type: "ed25519",
  565. privateKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
  566. } : index % 3 === 1 ? {
  567. type: "eip191",
  568. privateKey: "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
  569. } : {
  570. type: "hmac",
  571. apiKey: `beyond-api-key-${index}`,
  572. secretKey: `beyond-secret-key-${index}`
  573. }
  574. }))
  575. };
  576. // Act
  577. await fs.writeFile(configPath, JSON.stringify(manyAccountsConfig));
  578. const { result, duration } = await measureTime(() =>
  579. credentialManager.loadConfig(configPath)
  580. );
  581. // Assert - should handle gracefully even beyond minimum
  582. expect(result.success).toBe(true);
  583. expect(result.accounts).toHaveLength(75);
  584. // Performance may degrade slightly but should still be reasonable
  585. expect(duration).toBeLessThan(200); // Allow slightly more time for 75 accounts
  586. });
  587. });
  588. describe('High-Frequency Operations', () => {
  589. test('should handle high-frequency signing requests', async () => {
  590. // Arrange
  591. const config: ConfigFile = {
  592. version: "1.0",
  593. accounts: [
  594. {
  595. id: "high-freq-account",
  596. platform: Platform.PACIFICA,
  597. name: "High Frequency Account",
  598. credentials: {
  599. type: "ed25519",
  600. privateKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
  601. }
  602. }
  603. ]
  604. };
  605. await fs.writeFile(configPath, JSON.stringify(config));
  606. await credentialManager.loadConfig(configPath);
  607. // Act - rapid-fire signing requests
  608. const operations: Promise<SignResult>[] = [];
  609. const startTime = Date.now();
  610. for (let i = 0; i < 100; i++) {
  611. const message = new TextEncoder().encode(`High freq message ${i}`);
  612. operations.push(credentialManager.sign("high-freq-account", message));
  613. }
  614. const results = await Promise.all(operations);
  615. const totalDuration = Date.now() - startTime;
  616. // Assert
  617. results.forEach((result, index) => {
  618. expect(result.success).toBe(true);
  619. });
  620. // Should handle 100 operations efficiently
  621. const avgTimePerOperation = totalDuration / 100;
  622. expect(avgTimePerOperation).toBeLessThan(50);
  623. });
  624. test('should handle mixed operation types efficiently', async () => {
  625. // Arrange
  626. const config: ConfigFile = {
  627. version: "1.0",
  628. accounts: Array(10).fill(null).map((_, index) => ({
  629. id: `mixed-ops-account-${index}`,
  630. platform: Platform.PACIFICA,
  631. name: `Mixed Ops Account ${index}`,
  632. credentials: {
  633. type: "ed25519",
  634. privateKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
  635. }
  636. }))
  637. };
  638. await fs.writeFile(configPath, JSON.stringify(config));
  639. await credentialManager.loadConfig(configPath);
  640. // Act - mix of operations: sign, verify, getAccount, listAccounts
  641. const operations: Promise<any>[] = [];
  642. const message = new TextEncoder().encode("Mixed operations test");
  643. for (let i = 0; i < 50; i++) {
  644. const accountId = `mixed-ops-account-${i % 10}`;
  645. if (i % 4 === 0) {
  646. // Sign operation
  647. operations.push(credentialManager.sign(accountId, message));
  648. } else if (i % 4 === 1) {
  649. // Get account operation
  650. operations.push(Promise.resolve(credentialManager.getAccount(accountId)));
  651. } else if (i % 4 === 2) {
  652. // List accounts operation
  653. operations.push(Promise.resolve(credentialManager.listAccounts()));
  654. } else {
  655. // Verify operation (with dummy signature)
  656. operations.push(credentialManager.verify(accountId, message, "dummy-signature"));
  657. }
  658. }
  659. const startTime = Date.now();
  660. const results = await Promise.all(operations);
  661. const totalDuration = Date.now() - startTime;
  662. // Assert - should handle mixed operations efficiently
  663. expect(results).toHaveLength(50);
  664. expect(totalDuration).toBeLessThan(1000); // 50 mixed operations in <1 second
  665. });
  666. });
  667. });