account-registry.contract.test.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. /**
  2. * Contract Test for AccountRegistry Interface
  3. *
  4. * Tests the core AccountRegistry functionality following TDD principles.
  5. * These tests MUST fail initially and pass after implementation.
  6. */
  7. import { AccountRegistry } from '@/core/credential-manager/AccountRegistry'
  8. import { Platform, Account } from '@/types/credential'
  9. describe('AccountRegistry Contract Test', () => {
  10. let registry: AccountRegistry
  11. beforeEach(() => {
  12. registry = new AccountRegistry()
  13. })
  14. describe('Account Loading', () => {
  15. test('should load accounts from configuration', async () => {
  16. const config = {
  17. accounts: [
  18. {
  19. id: 'test-account-1',
  20. name: 'Test Account 1',
  21. platform: Platform.PACIFICA,
  22. enabled: true,
  23. credentials: {
  24. type: 'ed25519' as const,
  25. privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
  26. }
  27. }
  28. ]
  29. }
  30. await registry.loadAccounts(config)
  31. const accounts = registry.getAllAccounts()
  32. expect(accounts).toHaveLength(1)
  33. expect(accounts[0].id).toBe('test-account-1')
  34. expect(accounts[0].platform).toBe(Platform.PACIFICA)
  35. })
  36. test('should handle empty configuration gracefully', async () => {
  37. const config = { accounts: [] }
  38. await registry.loadAccounts(config)
  39. const accounts = registry.getAllAccounts()
  40. expect(accounts).toHaveLength(0)
  41. })
  42. test('should validate account configuration before loading', async () => {
  43. const invalidConfig = {
  44. accounts: [
  45. {
  46. id: 'invalid-account',
  47. // Missing required fields
  48. platform: Platform.PACIFICA
  49. }
  50. ]
  51. }
  52. await expect(registry.loadAccounts(invalidConfig as any))
  53. .rejects.toThrow('Invalid account configuration')
  54. })
  55. })
  56. describe('Account Retrieval', () => {
  57. beforeEach(async () => {
  58. const config = {
  59. accounts: [
  60. {
  61. id: 'pacifica-account',
  62. name: 'Pacifica Test',
  63. platform: Platform.PACIFICA,
  64. enabled: true,
  65. credentials: {
  66. type: 'ed25519' as const,
  67. privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
  68. }
  69. },
  70. {
  71. id: 'aster-account',
  72. name: 'Aster Test',
  73. platform: Platform.ASTER,
  74. enabled: true,
  75. credentials: {
  76. type: 'secp256k1' as const,
  77. privateKey: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'
  78. }
  79. }
  80. ]
  81. }
  82. await registry.loadAccounts(config)
  83. })
  84. test('should get account by ID', () => {
  85. const account = registry.getAccount('pacifica-account')
  86. expect(account).toBeDefined()
  87. expect(account?.id).toBe('pacifica-account')
  88. expect(account?.platform).toBe(Platform.PACIFICA)
  89. })
  90. test('should return undefined for non-existent account', () => {
  91. const account = registry.getAccount('non-existent')
  92. expect(account).toBeUndefined()
  93. })
  94. test('should get accounts by platform', () => {
  95. const pacificaAccounts = registry.getAccountsByPlatform(Platform.PACIFICA)
  96. const asterAccounts = registry.getAccountsByPlatform(Platform.ASTER)
  97. const binanceAccounts = registry.getAccountsByPlatform(Platform.BINANCE)
  98. expect(pacificaAccounts).toHaveLength(1)
  99. expect(asterAccounts).toHaveLength(1)
  100. expect(binanceAccounts).toHaveLength(0)
  101. expect(pacificaAccounts[0].id).toBe('pacifica-account')
  102. expect(asterAccounts[0].id).toBe('aster-account')
  103. })
  104. test('should get all accounts', () => {
  105. const allAccounts = registry.getAllAccounts()
  106. expect(allAccounts).toHaveLength(2)
  107. expect(allAccounts.map(a => a.id)).toContain('pacifica-account')
  108. expect(allAccounts.map(a => a.id)).toContain('aster-account')
  109. })
  110. test('should get enabled accounts only', () => {
  111. const enabledAccounts = registry.getEnabledAccounts()
  112. expect(enabledAccounts).toHaveLength(2)
  113. expect(enabledAccounts.every(a => a.enabled)).toBe(true)
  114. })
  115. })
  116. describe('Account Management', () => {
  117. test('should add new account', () => {
  118. const newAccount: Account = {
  119. id: 'new-account',
  120. name: 'New Account',
  121. platform: Platform.BINANCE,
  122. enabled: true,
  123. credentials: {
  124. type: 'hmac' as const,
  125. apiKey: 'test-api-key',
  126. secretKey: 'test-secret-key'
  127. }
  128. }
  129. registry.addAccount(newAccount)
  130. const retrieved = registry.getAccount('new-account')
  131. expect(retrieved).toBeDefined()
  132. expect(retrieved?.platform).toBe(Platform.BINANCE)
  133. })
  134. test('should prevent duplicate account IDs', () => {
  135. const account1: Account = {
  136. id: 'duplicate-id',
  137. name: 'Account 1',
  138. platform: Platform.PACIFICA,
  139. enabled: true,
  140. credentials: {
  141. type: 'ed25519' as const,
  142. privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
  143. }
  144. }
  145. const account2: Account = {
  146. id: 'duplicate-id',
  147. name: 'Account 2',
  148. platform: Platform.ASTER,
  149. enabled: true,
  150. credentials: {
  151. type: 'secp256k1' as const,
  152. privateKey: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'
  153. }
  154. }
  155. registry.addAccount(account1)
  156. expect(() => registry.addAccount(account2))
  157. .toThrow('Account with ID duplicate-id already exists')
  158. })
  159. test('should update existing account', () => {
  160. const originalAccount: Account = {
  161. id: 'update-test',
  162. name: 'Original Name',
  163. platform: Platform.PACIFICA,
  164. enabled: true,
  165. credentials: {
  166. type: 'ed25519' as const,
  167. privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
  168. }
  169. }
  170. registry.addAccount(originalAccount)
  171. const updatedAccount: Account = {
  172. ...originalAccount,
  173. name: 'Updated Name',
  174. enabled: false
  175. }
  176. registry.updateAccount('update-test', updatedAccount)
  177. const retrieved = registry.getAccount('update-test')
  178. expect(retrieved?.name).toBe('Updated Name')
  179. expect(retrieved?.enabled).toBe(false)
  180. })
  181. test('should remove account', () => {
  182. const account: Account = {
  183. id: 'remove-test',
  184. name: 'Remove Test',
  185. platform: Platform.PACIFICA,
  186. enabled: true,
  187. credentials: {
  188. type: 'ed25519' as const,
  189. privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
  190. }
  191. }
  192. registry.addAccount(account)
  193. expect(registry.getAccount('remove-test')).toBeDefined()
  194. const removed = registry.removeAccount('remove-test')
  195. expect(removed).toBe(true)
  196. expect(registry.getAccount('remove-test')).toBeUndefined()
  197. })
  198. test('should return false when removing non-existent account', () => {
  199. const removed = registry.removeAccount('non-existent')
  200. expect(removed).toBe(false)
  201. })
  202. })
  203. describe('Platform Support', () => {
  204. test('should return supported platforms', () => {
  205. const platforms = registry.getSupportedPlatforms()
  206. expect(platforms).toContain(Platform.PACIFICA)
  207. expect(platforms).toContain(Platform.ASTER)
  208. expect(platforms).toContain(Platform.BINANCE)
  209. })
  210. test('should check if platform is supported', () => {
  211. expect(registry.isPlatformSupported(Platform.PACIFICA)).toBe(true)
  212. expect(registry.isPlatformSupported(Platform.ASTER)).toBe(true)
  213. expect(registry.isPlatformSupported(Platform.BINANCE)).toBe(true)
  214. })
  215. })
  216. describe('Performance Requirements', () => {
  217. test('should load accounts within performance limits', async () => {
  218. const config = {
  219. accounts: Array.from({ length: 100 }, (_, i) => ({
  220. id: `account-${i}`,
  221. name: `Account ${i}`,
  222. platform: Platform.PACIFICA,
  223. enabled: true,
  224. credentials: {
  225. type: 'ed25519' as const,
  226. privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
  227. }
  228. }))
  229. }
  230. const startTime = Date.now()
  231. await registry.loadAccounts(config)
  232. const duration = Date.now() - startTime
  233. // Should load 100 accounts within 100ms
  234. expect(duration).toBeLessThan(100)
  235. expect(registry.getAllAccounts()).toHaveLength(100)
  236. })
  237. test('should retrieve accounts efficiently', async () => {
  238. const config = {
  239. accounts: Array.from({ length: 50 }, (_, i) => ({
  240. id: `account-${i}`,
  241. name: `Account ${i}`,
  242. platform: i % 2 === 0 ? Platform.PACIFICA : Platform.ASTER,
  243. enabled: true,
  244. credentials: {
  245. type: 'ed25519' as const,
  246. privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
  247. }
  248. }))
  249. }
  250. await registry.loadAccounts(config)
  251. const startTime = Date.now()
  252. for (let i = 0; i < 1000; i++) {
  253. registry.getAccount(`account-${i % 50}`)
  254. }
  255. const duration = Date.now() - startTime
  256. // Should handle 1000 lookups within 10ms
  257. expect(duration).toBeLessThan(10)
  258. })
  259. })
  260. describe('Error Handling', () => {
  261. test('should handle malformed configuration gracefully', async () => {
  262. const malformedConfig = {
  263. accounts: [
  264. {
  265. id: 'malformed',
  266. platform: 'invalid-platform',
  267. credentials: 'not-an-object'
  268. }
  269. ]
  270. }
  271. await expect(registry.loadAccounts(malformedConfig as any))
  272. .rejects.toThrow()
  273. })
  274. test('should provide meaningful error messages', async () => {
  275. const invalidConfig = {
  276. accounts: [
  277. {
  278. id: '', // Empty ID
  279. name: 'Test',
  280. platform: Platform.PACIFICA,
  281. enabled: true,
  282. credentials: {
  283. type: 'ed25519' as const,
  284. privateKey: 'invalid-key'
  285. }
  286. }
  287. ]
  288. }
  289. await expect(registry.loadAccounts(invalidConfig))
  290. .rejects.toThrow('Account ID cannot be empty')
  291. })
  292. })
  293. })