@audius/sdk 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (278) hide show
  1. package/.eslintrc +38 -0
  2. package/.prettierrc.js +1 -0
  3. package/.python-version +1 -0
  4. package/Dockerfile +15 -0
  5. package/README.md +3 -0
  6. package/babel.config.js +3 -0
  7. package/data-contracts/ABIs/AdminUpgradeabilityProxy.json +132 -0
  8. package/data-contracts/ABIs/BaseAdminUpgradeabilityProxy.json +113 -0
  9. package/data-contracts/ABIs/BaseUpgradeabilityProxy.json +22 -0
  10. package/data-contracts/ABIs/DiscoveryProviderFactory.json +189 -0
  11. package/data-contracts/ABIs/DiscoveryProviderFactoryInterface.json +61 -0
  12. package/data-contracts/ABIs/DiscoveryProviderStorage.json +205 -0
  13. package/data-contracts/ABIs/DiscoveryProviderStorageInterface.json +65 -0
  14. package/data-contracts/ABIs/ECDSA.json +4 -0
  15. package/data-contracts/ABIs/IPLDBlacklistFactory.json +168 -0
  16. package/data-contracts/ABIs/Initializable.json +4 -0
  17. package/data-contracts/ABIs/Migrations.json +67 -0
  18. package/data-contracts/ABIs/OpenZeppelinUpgradesAddress.json +4 -0
  19. package/data-contracts/ABIs/Ownable.json +79 -0
  20. package/data-contracts/ABIs/PlaylistFactory.json +669 -0
  21. package/data-contracts/ABIs/PlaylistFactoryInterface.json +42 -0
  22. package/data-contracts/ABIs/PlaylistStorage.json +250 -0
  23. package/data-contracts/ABIs/PlaylistStorageInterface.json +129 -0
  24. package/data-contracts/ABIs/Proxy.json +10 -0
  25. package/data-contracts/ABIs/Registry.json +240 -0
  26. package/data-contracts/ABIs/RegistryContract.json +102 -0
  27. package/data-contracts/ABIs/RegistryContractInterface.json +28 -0
  28. package/data-contracts/ABIs/RegistryInterface.json +66 -0
  29. package/data-contracts/ABIs/SigningLogic.json +43 -0
  30. package/data-contracts/ABIs/SigningLogicInitializable.json +46 -0
  31. package/data-contracts/ABIs/SocialFeatureFactory.json +460 -0
  32. package/data-contracts/ABIs/SocialFeatureStorage.json +225 -0
  33. package/data-contracts/ABIs/SocialFeatureStorageInterface.json +123 -0
  34. package/data-contracts/ABIs/TestContract.json +135 -0
  35. package/data-contracts/ABIs/TestContractInterface.json +19 -0
  36. package/data-contracts/ABIs/TestContractWithStorage.json +165 -0
  37. package/data-contracts/ABIs/TestContractWithStorageInterface.json +24 -0
  38. package/data-contracts/ABIs/TestStorage.json +144 -0
  39. package/data-contracts/ABIs/TestStorageInterface.json +42 -0
  40. package/data-contracts/ABIs/TestUserReplicaSetManager.json +432 -0
  41. package/data-contracts/ABIs/TrackFactory.json +391 -0
  42. package/data-contracts/ABIs/TrackFactoryInterface.json +73 -0
  43. package/data-contracts/ABIs/TrackStorage.json +223 -0
  44. package/data-contracts/ABIs/TrackStorageInterface.json +121 -0
  45. package/data-contracts/ABIs/UpgradeabilityProxy.json +37 -0
  46. package/data-contracts/ABIs/UserFactory.json +657 -0
  47. package/data-contracts/ABIs/UserFactoryInterface.json +65 -0
  48. package/data-contracts/ABIs/UserLibraryFactory.json +334 -0
  49. package/data-contracts/ABIs/UserReplicaSetManager.json +418 -0
  50. package/data-contracts/ABIs/UserStorage.json +233 -0
  51. package/data-contracts/ABIs/UserStorageInterface.json +93 -0
  52. package/data-contracts/signatureSchemas.ts +1236 -0
  53. package/dist/core.d.ts +446 -0
  54. package/dist/core.js +769 -0
  55. package/dist/core.js.map +1 -0
  56. package/dist/index.d.ts +689 -0
  57. package/dist/index.js +72850 -0
  58. package/dist/index.js.map +1 -0
  59. package/eth-contracts/ABIs/Address.json +4 -0
  60. package/eth-contracts/ABIs/AudiusAdminUpgradeabilityProxy.json +105 -0
  61. package/eth-contracts/ABIs/AudiusClaimDistributor.json +4968 -0
  62. package/eth-contracts/ABIs/AudiusToken.json +724 -0
  63. package/eth-contracts/ABIs/BaseUpgradeabilityProxy.json +23 -0
  64. package/eth-contracts/ABIs/Checkpointing.json +4 -0
  65. package/eth-contracts/ABIs/ClaimsManager.json +539 -0
  66. package/eth-contracts/ABIs/Context.json +11 -0
  67. package/eth-contracts/ABIs/DelegateManager.json +989 -0
  68. package/eth-contracts/ABIs/DelegateManagerV2.json +1049 -0
  69. package/eth-contracts/ABIs/DelegateManagerV2Bad.json +1049 -0
  70. package/eth-contracts/ABIs/ERC20.json +252 -0
  71. package/eth-contracts/ABIs/ERC20Burnable.json +287 -0
  72. package/eth-contracts/ABIs/ERC20Detailed.json +270 -0
  73. package/eth-contracts/ABIs/ERC20Mintable.json +364 -0
  74. package/eth-contracts/ABIs/ERC20Pausable.json +397 -0
  75. package/eth-contracts/ABIs/EthRewardsManager.json +174 -0
  76. package/eth-contracts/ABIs/Governance.json +938 -0
  77. package/eth-contracts/ABIs/GovernanceUpgraded.json +953 -0
  78. package/eth-contracts/ABIs/GovernanceV2.json +938 -0
  79. package/eth-contracts/ABIs/IERC20.json +200 -0
  80. package/eth-contracts/ABIs/Initializable.json +4 -0
  81. package/eth-contracts/ABIs/InitializableV2.json +14 -0
  82. package/eth-contracts/ABIs/Migrations.json +71 -0
  83. package/eth-contracts/ABIs/MinterRole.json +91 -0
  84. package/eth-contracts/ABIs/MockAccount.json +62 -0
  85. package/eth-contracts/ABIs/MockDelegateManager.json +55 -0
  86. package/eth-contracts/ABIs/MockStakingCaller.json +259 -0
  87. package/eth-contracts/ABIs/MockWormhole.json +106 -0
  88. package/eth-contracts/ABIs/OpenZeppelinUpgradesAddress.json +4 -0
  89. package/eth-contracts/ABIs/Ownable.json +93 -0
  90. package/eth-contracts/ABIs/Pausable.json +150 -0
  91. package/eth-contracts/ABIs/PauserRole.json +91 -0
  92. package/eth-contracts/ABIs/Proxy.json +10 -0
  93. package/eth-contracts/ABIs/Registry.json +288 -0
  94. package/eth-contracts/ABIs/Roles.json +4 -0
  95. package/eth-contracts/ABIs/SafeERC20.json +4 -0
  96. package/eth-contracts/ABIs/SafeMath.json +4 -0
  97. package/eth-contracts/ABIs/ServiceProviderFactory.json +1153 -0
  98. package/eth-contracts/ABIs/ServiceTypeManager.json +337 -0
  99. package/eth-contracts/ABIs/Staking.json +555 -0
  100. package/eth-contracts/ABIs/StakingUpgraded.json +570 -0
  101. package/eth-contracts/ABIs/TestContract.json +44 -0
  102. package/eth-contracts/ABIs/TrustedNotifierManager.json +265 -0
  103. package/eth-contracts/ABIs/Uint256Helpers.json +4 -0
  104. package/eth-contracts/ABIs/UpgradeabilityProxy.json +40 -0
  105. package/eth-contracts/ABIs/Wormhole.json +45 -0
  106. package/eth-contracts/ABIs/WormholeClient.json +155 -0
  107. package/examples/file.mp3 +0 -0
  108. package/examples/initAudiusLibs.js +86 -0
  109. package/examples/initializeVersions.js +95 -0
  110. package/examples/pic.jpg +0 -0
  111. package/initScripts/configureLocalDiscProv.js +167 -0
  112. package/initScripts/helpers/claim.js +43 -0
  113. package/initScripts/helpers/distributeTokens.js +24 -0
  114. package/initScripts/helpers/spRegistration.js +138 -0
  115. package/initScripts/helpers/utils.js +34 -0
  116. package/initScripts/helpers/version.js +93 -0
  117. package/initScripts/local.js +617 -0
  118. package/initScripts/mainnet.js +131 -0
  119. package/initScripts/manageProdRelayerWallets.js +191 -0
  120. package/package.json +125 -0
  121. package/rollup.config.js +164 -0
  122. package/scripts/AudiusClaimDistributor.json +4968 -0
  123. package/scripts/Wormhole.json +155 -0
  124. package/scripts/addCIDToIpldBlacklist.js +124 -0
  125. package/scripts/circleci-test.sh +53 -0
  126. package/scripts/communityRewards/transferCommunityRewardsToSolana.js +222 -0
  127. package/scripts/ipfs.sh +58 -0
  128. package/scripts/migrate_contracts.sh +25 -0
  129. package/scripts/reset.sh +65 -0
  130. package/scripts/test.sh +77 -0
  131. package/src/api/account.js +670 -0
  132. package/src/api/base.js +122 -0
  133. package/src/api/file.js +168 -0
  134. package/src/api/playlist.js +328 -0
  135. package/src/api/rewards.d.ts +4 -0
  136. package/src/api/rewards.js +682 -0
  137. package/src/api/serviceProvider.js +154 -0
  138. package/src/api/track.js +604 -0
  139. package/src/api/user.js +888 -0
  140. package/src/api/user.test.js +172 -0
  141. package/src/constants.ts +7 -0
  142. package/src/core.ts +3 -0
  143. package/src/index.js +6 -0
  144. package/src/libs.d.ts +3 -0
  145. package/src/libs.js +619 -0
  146. package/src/sanityChecks/addSecondaries.js +40 -0
  147. package/src/sanityChecks/assignReplicaSetIfNecessary.js +10 -0
  148. package/src/sanityChecks/index.d.ts +9 -0
  149. package/src/sanityChecks/index.js +31 -0
  150. package/src/sanityChecks/isCreator.js +73 -0
  151. package/src/sanityChecks/needsRecoveryEmail.js +20 -0
  152. package/src/sanityChecks/rolloverNodes.js +74 -0
  153. package/src/sanityChecks/sanitizeNodes.js +24 -0
  154. package/src/sanityChecks/syncNodes.js +28 -0
  155. package/src/sdk/constants.ts +10 -0
  156. package/src/sdk/index.ts +1 -0
  157. package/src/sdk/oauth/Oauth.ts +265 -0
  158. package/src/sdk/oauth/index.ts +1 -0
  159. package/src/sdk/sdk.ts +102 -0
  160. package/src/service-selection/ServiceSelection.test.ts +320 -0
  161. package/src/service-selection/ServiceSelection.ts +460 -0
  162. package/src/service-selection/constants.ts +14 -0
  163. package/src/service-selection/index.ts +1 -0
  164. package/src/services/ABIDecoder/AudiusABIDecoder.ts +71 -0
  165. package/src/services/ABIDecoder/index.ts +1 -0
  166. package/src/services/comstock/Comstock.ts +39 -0
  167. package/src/services/comstock/index.ts +1 -0
  168. package/src/services/contracts/ContractClient.ts +227 -0
  169. package/src/services/contracts/GovernedContractClient.ts +53 -0
  170. package/src/services/contracts/ProviderSelection.ts +42 -0
  171. package/src/services/creatorNode/CreatorNode.ts +1065 -0
  172. package/src/services/creatorNode/CreatorNodeSelection.test.ts +997 -0
  173. package/src/services/creatorNode/CreatorNodeSelection.ts +488 -0
  174. package/src/services/creatorNode/constants.ts +10 -0
  175. package/src/services/creatorNode/index.ts +2 -0
  176. package/src/services/dataContracts/AudiusContracts.ts +234 -0
  177. package/src/services/dataContracts/IPLDBlacklistFactoryClient.ts +73 -0
  178. package/src/services/dataContracts/PlaylistFactoryClient.ts +370 -0
  179. package/src/services/dataContracts/RegistryClient.ts +95 -0
  180. package/src/services/dataContracts/SocialFeatureFactoryClient.ts +196 -0
  181. package/src/services/dataContracts/TrackFactoryClient.ts +131 -0
  182. package/src/services/dataContracts/UserFactoryClient.ts +351 -0
  183. package/src/services/dataContracts/UserLibraryFactoryClient.ts +115 -0
  184. package/src/services/dataContracts/UserReplicaSetManagerClient.ts +206 -0
  185. package/src/services/dataContracts/index.ts +1 -0
  186. package/src/services/discoveryProvider/DiscoveryProvider.ts +1168 -0
  187. package/src/services/discoveryProvider/DiscoveryProviderSelection.test.ts +536 -0
  188. package/src/services/discoveryProvider/DiscoveryProviderSelection.ts +383 -0
  189. package/src/services/discoveryProvider/constants.ts +13 -0
  190. package/src/services/discoveryProvider/index.ts +1 -0
  191. package/src/services/discoveryProvider/requests.ts +629 -0
  192. package/src/services/ethContracts/AudiusTokenClient.ts +163 -0
  193. package/src/services/ethContracts/ClaimDistributionClient.ts +45 -0
  194. package/src/services/ethContracts/ClaimsManagerClient.ts +102 -0
  195. package/src/services/ethContracts/DelegateManagerClient.ts +480 -0
  196. package/src/services/ethContracts/EthContracts.ts +359 -0
  197. package/src/services/ethContracts/EthRewardsManagerClient.ts +33 -0
  198. package/src/services/ethContracts/GovernanceClient.ts +451 -0
  199. package/src/services/ethContracts/RegistryClient.ts +33 -0
  200. package/src/services/ethContracts/ServiceProviderFactoryClient.ts +691 -0
  201. package/src/services/ethContracts/ServiceTypeManagerClient.ts +112 -0
  202. package/src/services/ethContracts/StakingProxyClient.ts +97 -0
  203. package/src/services/ethContracts/TrustedNotifierManagerClient.ts +101 -0
  204. package/src/services/ethContracts/WormholeClient.ts +97 -0
  205. package/src/services/ethContracts/index.ts +1 -0
  206. package/src/services/ethWeb3Manager/EthWeb3Manager.ts +239 -0
  207. package/src/services/ethWeb3Manager/index.ts +1 -0
  208. package/src/services/hedgehog/Hedgehog.ts +96 -0
  209. package/src/services/hedgehog/index.ts +1 -0
  210. package/src/services/identity/IdentityService.ts +551 -0
  211. package/src/services/identity/index.ts +1 -0
  212. package/src/services/identity/requests.ts +65 -0
  213. package/src/services/schemaValidator/SchemaValidator.ts +105 -0
  214. package/src/services/schemaValidator/index.ts +1 -0
  215. package/src/services/schemaValidator/schemas/trackSchema.json +267 -0
  216. package/src/services/schemaValidator/schemas/userSchema.json +230 -0
  217. package/src/services/solanaAudiusData/errors.ts +20 -0
  218. package/src/services/solanaAudiusData/index.ts +1189 -0
  219. package/src/services/solanaWeb3Manager/errors.js +101 -0
  220. package/src/services/solanaWeb3Manager/index.d.ts +46 -0
  221. package/src/services/solanaWeb3Manager/index.js +655 -0
  222. package/src/services/solanaWeb3Manager/padBNToUint8Array.ts +7 -0
  223. package/src/services/solanaWeb3Manager/rewards.js +941 -0
  224. package/src/services/solanaWeb3Manager/rewardsAttester.ts +1093 -0
  225. package/src/services/solanaWeb3Manager/tokenAccount.js +149 -0
  226. package/src/services/solanaWeb3Manager/transactionHandler.js +345 -0
  227. package/src/services/solanaWeb3Manager/transfer.js +272 -0
  228. package/src/services/solanaWeb3Manager/userBank.js +160 -0
  229. package/src/services/solanaWeb3Manager/utils.d.ts +31 -0
  230. package/src/services/solanaWeb3Manager/utils.js +163 -0
  231. package/src/services/solanaWeb3Manager/wAudio.js +28 -0
  232. package/src/services/solanaWeb3Manager/wAudio.test.js +30 -0
  233. package/src/services/web3Manager/Web3Config.ts +14 -0
  234. package/src/services/web3Manager/Web3Manager.ts +360 -0
  235. package/src/services/web3Manager/XMLHttpRequest.ts +11 -0
  236. package/src/services/web3Manager/index.ts +2 -0
  237. package/src/services/wormhole/index.js +424 -0
  238. package/src/types.ts +8 -0
  239. package/src/userStateManager.ts +53 -0
  240. package/src/utils/apiSigning.ts +51 -0
  241. package/src/utils/captcha.ts +97 -0
  242. package/src/utils/estimateGas.ts +64 -0
  243. package/src/utils/fileHasher.ts +278 -0
  244. package/src/utils/importContractABI.d.ts +9 -0
  245. package/src/utils/importContractABI.js +19 -0
  246. package/src/utils/index.ts +11 -0
  247. package/src/utils/multiProvider.ts +72 -0
  248. package/src/utils/network.test.ts +127 -0
  249. package/src/utils/network.ts +308 -0
  250. package/src/utils/promiseFight.test.ts +87 -0
  251. package/src/utils/promiseFight.ts +36 -0
  252. package/src/utils/signatures.ts +139 -0
  253. package/src/utils/types.ts +34 -0
  254. package/src/utils/utils.test.ts +36 -0
  255. package/src/utils/utils.ts +235 -0
  256. package/src/utils/uuid.ts +14 -0
  257. package/src/web3.d.ts +9 -0
  258. package/src/web3.js +8 -0
  259. package/tests/assets/static_image.png +0 -0
  260. package/tests/assets/static_text.txt +1 -0
  261. package/tests/audiusTokenClientTest.js +37 -0
  262. package/tests/creatorNodeTest.js +19 -0
  263. package/tests/fileHasherTest.js +125 -0
  264. package/tests/governanceTest.js +382 -0
  265. package/tests/helpers.js +105 -0
  266. package/tests/index.js +14 -0
  267. package/tests/playlistClientTest.js +157 -0
  268. package/tests/providerSelectionTest.js +241 -0
  269. package/tests/registryClientTest.js +19 -0
  270. package/tests/rewardsAttesterTest.js +373 -0
  271. package/tests/serviceTypeManagerClientTest.js +33 -0
  272. package/tests/socialFeatureClientTest.js +79 -0
  273. package/tests/stakingTest.js +302 -0
  274. package/tests/trackClientTest.js +86 -0
  275. package/tests/userClientTest.js +121 -0
  276. package/tsconfig.json +10 -0
  277. package/types/@audius-hedgehog/index.d.ts +39 -0
  278. package/types/abi-decoder/index.d.ts +41 -0
@@ -0,0 +1,997 @@
1
+ import nock from 'nock'
2
+ import assert from 'assert'
3
+ import semver from 'semver'
4
+
5
+ import { CREATOR_NODE_SERVICE_NAME } from './constants'
6
+ import { CreatorNodeSelection } from './CreatorNodeSelection'
7
+ import type { EthContracts } from '../ethContracts'
8
+
9
+ const mockEthContracts = (
10
+ urls: string[],
11
+ currrentVersion: string,
12
+ previousVersions: string[] | null = null
13
+ ): EthContracts =>
14
+ ({
15
+ getCurrentVersion: async () => currrentVersion,
16
+ getNumberOfVersions: async () => 2,
17
+ getVersion: async (_: string, queryIndex: number) => {
18
+ if (previousVersions) {
19
+ return previousVersions[queryIndex] as string
20
+ }
21
+ return ['1.2.2', '1.2.3'][queryIndex] as string
22
+ },
23
+ getServiceProviderList: async () => urls.map((u) => ({ endpoint: u })),
24
+ hasSameMajorAndMinorVersion: (version1: string, version2: string) => {
25
+ return (
26
+ semver.major(version1) === semver.major(version2) &&
27
+ semver.minor(version1) === semver.minor(version2)
28
+ )
29
+ },
30
+ isInRegressedMode: () => {
31
+ return false
32
+ }
33
+ } as unknown as EthContracts)
34
+
35
+ const mockCreatorNode = {
36
+ getSyncStatus: async () => {
37
+ return {
38
+ isBehind: false,
39
+ isConfigured: true
40
+ }
41
+ },
42
+ monitoringCallbacks: {}
43
+ }
44
+
45
+ // Add fields as necessary
46
+ const defaultHealthCheckData = {
47
+ service: CREATOR_NODE_SERVICE_NAME,
48
+ version: '1.2.3',
49
+ healthy: true,
50
+ country: 'US',
51
+ latitude: '37.7749',
52
+ longitude: '-122.4194',
53
+ databaseConnections: 5,
54
+ totalMemory: 6237552640,
55
+ usedMemory: 6111436800,
56
+ storagePathSize: 62725623808,
57
+ storagePathUsed: 14723018752,
58
+ maxFileDescriptors: 524288,
59
+ allocatedFileDescriptors: 2912,
60
+ receivedBytesPerSec: 776.7638177541248,
61
+ transferredBytesPerSec: 39888.88888888889,
62
+ maxStorageUsedPercent: 95
63
+ }
64
+
65
+ type HealthCheckData = { [K in keyof typeof defaultHealthCheckData]?: unknown }
66
+
67
+ describe('test CreatorNodeSelection', () => {
68
+ it('selects the fastest healthy service as primary and rest as secondaries', async () => {
69
+ const healthy = 'https://healthy.audius.co'
70
+ nock(healthy)
71
+ .get('/health_check/verbose')
72
+ .reply(200, { data: defaultHealthCheckData })
73
+
74
+ const healthyButSlow = 'https://healthybutslow.audius.co'
75
+ nock(healthyButSlow)
76
+ .get('/health_check/verbose')
77
+ .delay(100)
78
+ .reply(200, { data: defaultHealthCheckData })
79
+
80
+ const healthyButSlowest = 'https://healthybutslowest.audius.co'
81
+ nock(healthyButSlowest)
82
+ .get('/health_check/verbose')
83
+ .delay(200)
84
+ .reply(200, { data: defaultHealthCheckData })
85
+
86
+ const cns = new CreatorNodeSelection({
87
+ creatorNode: mockCreatorNode,
88
+ numberOfNodes: 3,
89
+ ethContracts: mockEthContracts(
90
+ [healthy, healthyButSlow, healthyButSlowest],
91
+ '1.2.3'
92
+ ),
93
+ whitelist: null,
94
+ blacklist: null
95
+ })
96
+
97
+ const { primary, secondaries, services } = await cns.select(true, false)
98
+
99
+ assert(primary === healthy)
100
+ assert(secondaries.length === 2)
101
+ assert(secondaries.includes(healthyButSlow))
102
+ assert(secondaries.includes(healthyButSlowest))
103
+
104
+ const returnedHealthyServices = new Set(Object.keys(services))
105
+ assert(returnedHealthyServices.size === 3)
106
+ const healthyServices = [healthy, healthyButSlow, healthyButSlowest]
107
+ healthyServices.map((service) =>
108
+ assert(returnedHealthyServices.has(service))
109
+ )
110
+ })
111
+
112
+ it('Return nothing if whitelist is empty set', async function () {
113
+ const healthy = 'https://healthy.audius.co'
114
+ nock(healthy)
115
+ .get('/health_check/verbose')
116
+ .reply(200, { data: defaultHealthCheckData })
117
+
118
+ const healthyButSlow = 'https://healthybutslow.audius.co'
119
+ nock(healthyButSlow)
120
+ .get('/health_check/verbose')
121
+ .delay(100)
122
+ .reply(200, { data: defaultHealthCheckData })
123
+
124
+ const healthyButSlowest = 'https://healthybutslowest.audius.co'
125
+ nock(healthyButSlowest)
126
+ .get('/health_check/verbose')
127
+ .delay(200)
128
+ .reply(200, { data: defaultHealthCheckData })
129
+
130
+ const cns = new CreatorNodeSelection({
131
+ creatorNode: mockCreatorNode,
132
+ numberOfNodes: 3,
133
+ ethContracts: mockEthContracts(
134
+ [healthy, healthyButSlow, healthyButSlowest],
135
+ '1.2.3'
136
+ ),
137
+ whitelist: new Set([]),
138
+ blacklist: null
139
+ })
140
+
141
+ const { primary, secondaries, services } = await cns.select()
142
+ assert(primary === undefined)
143
+ assert(secondaries.length === 0)
144
+
145
+ const returnedHealthyServices = new Set(Object.keys(services))
146
+ assert(returnedHealthyServices.size === 0)
147
+ })
148
+
149
+ it('select healthy nodes as the primary and secondary, and do not select unhealthy nodes', async () => {
150
+ const upToDate = 'https://upToDate.audius.co'
151
+ nock(upToDate)
152
+ .get('/health_check/verbose')
153
+ .reply(200, { data: defaultHealthCheckData })
154
+
155
+ const behindMajor = 'https://behindMajor.audius.co'
156
+ nock(behindMajor)
157
+ .get('/health_check/verbose')
158
+ .reply(200, { data: { ...defaultHealthCheckData, version: '0.2.3' } })
159
+
160
+ const behindMinor = 'https://behindMinor.audius.co'
161
+ nock(behindMinor)
162
+ .get('/health_check/verbose')
163
+ .reply(200, { data: { ...defaultHealthCheckData, version: '1.0.3' } })
164
+
165
+ const behindPatch = 'https://behindPatch.audius.co'
166
+ nock(behindPatch)
167
+ .get('/health_check/verbose')
168
+ .reply(200, { data: { ...defaultHealthCheckData, version: '1.2.0' } })
169
+
170
+ const cns = new CreatorNodeSelection({
171
+ creatorNode: mockCreatorNode,
172
+ numberOfNodes: 3,
173
+ ethContracts: mockEthContracts(
174
+ [upToDate, behindMajor, behindMinor, behindPatch],
175
+ '1.2.3'
176
+ ),
177
+ whitelist: null,
178
+ blacklist: null
179
+ })
180
+
181
+ const { primary, secondaries, services } = await cns.select(true, false)
182
+
183
+ assert(primary === upToDate)
184
+ assert(secondaries.length === 1)
185
+ assert(secondaries.includes(behindPatch))
186
+
187
+ const returnedHealthyServices = new Set(Object.keys(services))
188
+ assert(returnedHealthyServices.size === 2)
189
+ const healthyServices = [upToDate, behindPatch]
190
+ healthyServices.map((service) =>
191
+ assert(returnedHealthyServices.has(service))
192
+ )
193
+ })
194
+
195
+ it('return nothing if no services are healthy', async () => {
196
+ const unhealthy1 = 'https://unhealthy1.audius.co'
197
+ nock(unhealthy1).get('/health_check/verbose').reply(500, {})
198
+
199
+ const unhealthy2 = 'https://unhealthy2.audius.co'
200
+ nock(unhealthy2).get('/health_check/verbose').delay(100).reply(500, {})
201
+
202
+ const unhealthy3 = 'https://unhealthy3.audius.co'
203
+ nock(unhealthy3).get('/health_check/verbose').delay(200).reply(500, {})
204
+
205
+ const unhealthy4 = 'https://unhealthy4.audius.co'
206
+ nock(unhealthy4).get('/health_check/verbose').delay(300).reply(500, {})
207
+
208
+ const unhealthy5 = 'https://unhealthy5.audius.co'
209
+ nock(unhealthy5).get('/health_check/verbose').delay(400).reply(500, {})
210
+
211
+ const cns = new CreatorNodeSelection({
212
+ creatorNode: mockCreatorNode,
213
+ numberOfNodes: 3,
214
+ ethContracts: mockEthContracts(
215
+ [unhealthy1, unhealthy2, unhealthy3, unhealthy4, unhealthy5],
216
+ '1.2.3'
217
+ ),
218
+ whitelist: null,
219
+ blacklist: null
220
+ })
221
+
222
+ const { primary, secondaries, services } = await cns.select(true, false)
223
+
224
+ // All unhealthy are bad candidates so don't select anything
225
+ assert(primary === undefined)
226
+ assert(secondaries.length === 0)
227
+
228
+ const returnedHealthyServices = new Set(Object.keys(services))
229
+ assert(returnedHealthyServices.size === 0)
230
+ })
231
+
232
+ it('selects the only healthy service among the services of different statuses', async () => {
233
+ // the cream of the crop -- up to date version, slow. you want this
234
+ const shouldBePrimary = 'https://shouldBePrimary.audius.co'
235
+ nock(shouldBePrimary)
236
+ .get('/health_check/verbose')
237
+ .delay(200)
238
+ .reply(200, { data: defaultHealthCheckData })
239
+
240
+ // cold, overnight pizza -- behind by minor version, fast. nope
241
+ const unhealthy2 = 'https://unhealthy2.audius.co'
242
+ nock(unhealthy2)
243
+ .get('/health_check/verbose')
244
+ .reply(200, { data: { ...defaultHealthCheckData, version: '1.0.3' } })
245
+
246
+ // stale chips from 2 weeks ago -- behind by major version, kinda slow. still nope
247
+ const unhealthy3 = 'https://unhealthy3.audius.co'
248
+ nock(unhealthy3)
249
+ .get('/health_check/verbose')
250
+ .delay(100)
251
+ .reply(200, { data: { ...defaultHealthCheckData, version: '0.2.3' } })
252
+
253
+ // moldy canned beans -- not available/up at all. for sure nope
254
+ const unhealthy1 = 'https://unhealthy1.audius.co'
255
+ nock(unhealthy1).get('/health_check/verbose').reply(500, {})
256
+
257
+ // your house mate's leftovers from her team outing -- behind by patch, kinda slow. solid
258
+ const shouldBeSecondary = 'https://secondary.audius.co'
259
+ nock(shouldBeSecondary)
260
+ .get('/health_check/verbose')
261
+ .delay(100)
262
+ .reply(200, { data: { ...defaultHealthCheckData, version: '1.2.0' } })
263
+
264
+ const cns = new CreatorNodeSelection({
265
+ creatorNode: mockCreatorNode,
266
+ numberOfNodes: 3,
267
+ ethContracts: mockEthContracts(
268
+ [
269
+ unhealthy1,
270
+ shouldBePrimary,
271
+ unhealthy2,
272
+ unhealthy3,
273
+ shouldBeSecondary
274
+ ],
275
+ '1.2.3'
276
+ ),
277
+ whitelist: null,
278
+ blacklist: null
279
+ })
280
+
281
+ const { primary, secondaries, services } = await cns.select(true, false)
282
+
283
+ assert(primary === shouldBePrimary)
284
+ assert(secondaries.length === 1)
285
+ assert(secondaries.includes(shouldBeSecondary))
286
+
287
+ const returnedHealthyServices = new Set(Object.keys(services))
288
+ assert(returnedHealthyServices.size === 2)
289
+ const healthyServices = [shouldBePrimary, shouldBeSecondary]
290
+ healthyServices.map((service) =>
291
+ assert(returnedHealthyServices.has(service))
292
+ )
293
+ })
294
+
295
+ /**
296
+ * This test is to ensure that the proper number of services is selected.
297
+ * If numNodes = n, then (assuming all nodes are healthy):
298
+ * - 1 primary is selected
299
+ * - n-1 secondaries are selected
300
+ * - n services are returned
301
+ */
302
+ it('selects numNodes - 1 number of secondaries (starting with numNodes=5->1)', async () => {
303
+ const contentNodes = []
304
+ const numNodes = 5
305
+ for (let i = 0; i < numNodes; i++) {
306
+ const healthyUrl = `https://healthy${i}.audius.co`
307
+ nock(healthyUrl)
308
+ .persist()
309
+ .get('/health_check/verbose')
310
+ .reply(200, { data: defaultHealthCheckData })
311
+ contentNodes.push(healthyUrl)
312
+ }
313
+
314
+ let cns
315
+ for (let i = 0; i < numNodes; i++) {
316
+ cns = new CreatorNodeSelection({
317
+ creatorNode: mockCreatorNode,
318
+ numberOfNodes: numNodes - i,
319
+ ethContracts: mockEthContracts(contentNodes, '1.2.3'),
320
+ whitelist: null,
321
+ blacklist: null
322
+ })
323
+
324
+ const { primary, secondaries, services } = await cns.select(true, false)
325
+ assert(primary)
326
+ assert(secondaries.length === numNodes - i - 1)
327
+ const returnedHealthyServices = Object.keys(services)
328
+ assert(returnedHealthyServices.length === numNodes)
329
+ }
330
+ })
331
+
332
+ it('selects 1 secondary if only 1 secondary is available', async () => {
333
+ const shouldBePrimary = 'https://shouldBePrimary.audius.co'
334
+ nock(shouldBePrimary)
335
+ .get('/health_check/verbose')
336
+ .delay(200)
337
+ .reply(200, { data: defaultHealthCheckData })
338
+
339
+ const shouldBeSecondary = 'https://secondary.audius.co'
340
+ nock(shouldBeSecondary)
341
+ .get('/health_check/verbose')
342
+ .delay(100)
343
+ .reply(200, { data: { ...defaultHealthCheckData, version: '1.2.0' } })
344
+
345
+ const unhealthy = 'https://unhealthy.audius.co'
346
+ nock(unhealthy).get('/health_check/verbose').reply(500, {})
347
+
348
+ const cns = new CreatorNodeSelection({
349
+ creatorNode: mockCreatorNode,
350
+ numberOfNodes: 3,
351
+ ethContracts: mockEthContracts(
352
+ [unhealthy, shouldBePrimary, shouldBeSecondary],
353
+ '1.2.3'
354
+ ),
355
+ whitelist: null,
356
+ blacklist: null
357
+ })
358
+
359
+ const { primary, secondaries, services } = await cns.select(true, false)
360
+
361
+ assert(primary === shouldBePrimary)
362
+ assert(secondaries.length === 1)
363
+ assert(secondaries.includes(shouldBeSecondary))
364
+
365
+ const returnedHealthyServices = new Set(Object.keys(services))
366
+ assert(returnedHealthyServices.size === 2)
367
+ const healthyServices = [shouldBePrimary, shouldBeSecondary]
368
+ healthyServices.map((service) =>
369
+ assert(returnedHealthyServices.has(service))
370
+ )
371
+ })
372
+
373
+ it('filters out nodes if over 95% of storage is used', async () => {
374
+ const shouldBePrimary = 'https://primary.audius.co'
375
+ nock(shouldBePrimary)
376
+ .get('/health_check/verbose')
377
+ .reply(200, {
378
+ data: {
379
+ ...defaultHealthCheckData,
380
+ storagePathUsed: 30,
381
+ storagePathSize: 100
382
+ }
383
+ })
384
+
385
+ const shouldBeSecondary1 = 'https://secondary1.audius.co'
386
+ nock(shouldBeSecondary1)
387
+ .get('/health_check/verbose')
388
+ .reply(200, {
389
+ data: {
390
+ ...defaultHealthCheckData,
391
+ version: '1.2.1',
392
+ storagePathUsed: 30,
393
+ storagePathSize: 100
394
+ }
395
+ })
396
+
397
+ const shouldBeSecondary2 = 'https://secondary2.audius.co'
398
+ nock(shouldBeSecondary2)
399
+ .get('/health_check/verbose')
400
+ .reply(200, {
401
+ data: {
402
+ ...defaultHealthCheckData,
403
+ version: '1.2.0',
404
+ storagePathUsed: 30,
405
+ storagePathSize: 100
406
+ }
407
+ })
408
+
409
+ const used95PercentStorage = 'https://used95PercentStorage.audius.co'
410
+ nock(used95PercentStorage)
411
+ .get('/health_check/verbose')
412
+ .reply(200, {
413
+ data: {
414
+ ...defaultHealthCheckData,
415
+ storagePathUsed: 95.354,
416
+ storagePathSize: 100
417
+ }
418
+ })
419
+
420
+ const used99PercentStorage = 'https://used99PercentStorage.audius.co'
421
+ nock(used99PercentStorage)
422
+ .get('/health_check/verbose')
423
+ .reply(200, {
424
+ data: {
425
+ ...defaultHealthCheckData,
426
+ storagePathUsed: 99,
427
+ storagePathSize: 100
428
+ }
429
+ })
430
+
431
+ const cns = new CreatorNodeSelection({
432
+ creatorNode: mockCreatorNode,
433
+ numberOfNodes: 3,
434
+ ethContracts: mockEthContracts(
435
+ [
436
+ shouldBePrimary,
437
+ shouldBeSecondary1,
438
+ shouldBeSecondary2,
439
+ used95PercentStorage,
440
+ used99PercentStorage
441
+ ],
442
+ '1.2.3'
443
+ ),
444
+ whitelist: null,
445
+ blacklist: null
446
+ })
447
+ assert(cns.maxStorageUsedPercent === 95)
448
+
449
+ const { primary, secondaries, services } = await cns.select(true, false)
450
+
451
+ assert(cns.maxStorageUsedPercent === 95)
452
+ assert(primary === shouldBePrimary)
453
+ assert(secondaries.length === 2)
454
+ assert(secondaries.includes(shouldBeSecondary1))
455
+ assert(secondaries.includes(shouldBeSecondary2))
456
+
457
+ const returnedHealthyServices = new Set(Object.keys(services))
458
+ assert(returnedHealthyServices.size === 3)
459
+ const healthyServices = [
460
+ shouldBePrimary,
461
+ shouldBeSecondary1,
462
+ shouldBeSecondary2
463
+ ]
464
+ healthyServices.map((service) =>
465
+ assert(returnedHealthyServices.has(service))
466
+ )
467
+ })
468
+
469
+ it('overrides with health check resp `maxStorageUsedPercent` even if it is passed into constructor', async () => {
470
+ const shouldBePrimary = 'https://primary.audius.co'
471
+ nock(shouldBePrimary)
472
+ .get('/health_check/verbose')
473
+ .reply(200, {
474
+ data: {
475
+ ...defaultHealthCheckData,
476
+ storagePathUsed: 30,
477
+ storagePathSize: 100
478
+ }
479
+ })
480
+
481
+ const shouldBeSecondary1 = 'https://secondary1.audius.co'
482
+ nock(shouldBeSecondary1)
483
+ .get('/health_check/verbose')
484
+ .reply(200, {
485
+ data: {
486
+ ...defaultHealthCheckData,
487
+ version: '1.2.2',
488
+ storagePathUsed: 30,
489
+ storagePathSize: 100
490
+ }
491
+ })
492
+
493
+ const shouldBeSecondary2 = 'https://secondary2.audius.co'
494
+ nock(shouldBeSecondary2)
495
+ .get('/health_check/verbose')
496
+ .reply(200, {
497
+ data: {
498
+ ...defaultHealthCheckData,
499
+ version: '1.2.1',
500
+ storagePathUsed: 30,
501
+ storagePathSize: 100
502
+ }
503
+ })
504
+
505
+ const used50PercentStorage = 'https://used95PercentStorage.audius.co'
506
+ nock(used50PercentStorage)
507
+ .get('/health_check/verbose')
508
+ .reply(200, {
509
+ data: {
510
+ ...defaultHealthCheckData,
511
+ version: '1.2.0',
512
+ storagePathUsed: 50,
513
+ storagePathSize: 100
514
+ }
515
+ })
516
+
517
+ const used70PercentStorage = 'https://used70PercentStorage.audius.co'
518
+ nock(used70PercentStorage)
519
+ .get('/health_check/verbose')
520
+ .reply(200, {
521
+ data: {
522
+ ...defaultHealthCheckData,
523
+ version: '1.2.0',
524
+ storagePathUsed: 70.546,
525
+ storagePathSize: 100
526
+ }
527
+ })
528
+
529
+ const cns = new CreatorNodeSelection({
530
+ creatorNode: mockCreatorNode,
531
+ numberOfNodes: 3,
532
+ ethContracts: mockEthContracts(
533
+ [
534
+ shouldBePrimary,
535
+ shouldBeSecondary1,
536
+ shouldBeSecondary2,
537
+ used50PercentStorage,
538
+ used70PercentStorage
539
+ ],
540
+ '1.2.3'
541
+ ),
542
+ whitelist: null,
543
+ blacklist: null,
544
+ maxStorageUsedPercent: 50
545
+ })
546
+
547
+ assert(cns.maxStorageUsedPercent === 50)
548
+
549
+ const { primary, secondaries, services } = await cns.select(true, false)
550
+
551
+ // @ts-expect-error maxStorageUsedPercent actually does change after another select call
552
+ assert(cns.maxStorageUsedPercent === 95)
553
+
554
+ assert(primary === shouldBePrimary)
555
+ assert(secondaries.length === 2)
556
+ assert(secondaries.includes(shouldBeSecondary1))
557
+ assert(secondaries.includes(shouldBeSecondary2))
558
+
559
+ const returnedHealthyServices = new Set(Object.keys(services))
560
+ assert(returnedHealthyServices.size === 5)
561
+ const healthyServices = [
562
+ shouldBePrimary,
563
+ shouldBeSecondary1,
564
+ shouldBeSecondary2
565
+ ]
566
+ healthyServices.map((service) =>
567
+ assert(returnedHealthyServices.has(service))
568
+ )
569
+ })
570
+
571
+ it('allows custom maxStorageUsedPercent as constructor param if `maxStorageUsedPercent` is not found in health check resp', async () => {
572
+ const healthCheckResponseWithNoMaxStorageUsedPercent: HealthCheckData = {
573
+ ...defaultHealthCheckData
574
+ }
575
+ delete healthCheckResponseWithNoMaxStorageUsedPercent.maxStorageUsedPercent
576
+
577
+ const shouldBePrimary = 'https://primary.audius.co'
578
+ nock(shouldBePrimary)
579
+ .get('/health_check/verbose')
580
+ .reply(200, {
581
+ data: {
582
+ ...healthCheckResponseWithNoMaxStorageUsedPercent,
583
+ storagePathUsed: 30,
584
+ storagePathSize: 100
585
+ }
586
+ })
587
+
588
+ const shouldBeSecondary1 = 'https://secondary1.audius.co'
589
+ nock(shouldBeSecondary1)
590
+ .get('/health_check/verbose')
591
+ .reply(200, {
592
+ data: {
593
+ ...healthCheckResponseWithNoMaxStorageUsedPercent,
594
+ version: '1.2.1',
595
+ storagePathUsed: 30,
596
+ storagePathSize: 100
597
+ }
598
+ })
599
+
600
+ const shouldBeSecondary2 = 'https://secondary2.audius.co'
601
+ nock(shouldBeSecondary2)
602
+ .get('/health_check/verbose')
603
+ .reply(200, {
604
+ data: {
605
+ ...healthCheckResponseWithNoMaxStorageUsedPercent,
606
+ version: '1.2.0',
607
+ storagePathUsed: 30,
608
+ storagePathSize: 100
609
+ }
610
+ })
611
+
612
+ const used50PercentStorage = 'https://used95PercentStorage.audius.co'
613
+ nock(used50PercentStorage)
614
+ .get('/health_check/verbose')
615
+ .reply(200, {
616
+ data: {
617
+ ...healthCheckResponseWithNoMaxStorageUsedPercent,
618
+ storagePathUsed: 50,
619
+ storagePathSize: 100
620
+ }
621
+ })
622
+
623
+ const used70PercentStorage = 'https://used70PercentStorage.audius.co'
624
+ nock(used70PercentStorage)
625
+ .get('/health_check/verbose')
626
+ .reply(200, {
627
+ data: {
628
+ ...healthCheckResponseWithNoMaxStorageUsedPercent,
629
+ storagePathUsed: 70.546,
630
+ storagePathSize: 100
631
+ }
632
+ })
633
+
634
+ const cns = new CreatorNodeSelection({
635
+ creatorNode: mockCreatorNode,
636
+ numberOfNodes: 3,
637
+ ethContracts: mockEthContracts(
638
+ [
639
+ shouldBePrimary,
640
+ shouldBeSecondary1,
641
+ shouldBeSecondary2,
642
+ used50PercentStorage,
643
+ used70PercentStorage
644
+ ],
645
+ '1.2.3'
646
+ ),
647
+ whitelist: null,
648
+ blacklist: null,
649
+ maxStorageUsedPercent: 50
650
+ })
651
+
652
+ assert(cns.maxStorageUsedPercent === 50)
653
+
654
+ const { primary, secondaries, services } = await cns.select(true, false)
655
+
656
+ assert(cns.maxStorageUsedPercent === 50)
657
+ assert(primary === shouldBePrimary)
658
+ assert(secondaries.length === 2)
659
+ assert(secondaries.includes(shouldBeSecondary1))
660
+ assert(secondaries.includes(shouldBeSecondary2))
661
+
662
+ const returnedHealthyServices = new Set(Object.keys(services))
663
+ assert(returnedHealthyServices.size === 3)
664
+ const healthyServices = [
665
+ shouldBePrimary,
666
+ shouldBeSecondary1,
667
+ shouldBeSecondary2
668
+ ]
669
+ healthyServices.map((service) =>
670
+ assert(returnedHealthyServices.has(service))
671
+ )
672
+ })
673
+
674
+ it('allows Content Node to be selected if storage information is unavailable (undefined or null)', async () => {
675
+ const healthCheckDataWithNoStorageInfo: HealthCheckData = {
676
+ ...defaultHealthCheckData
677
+ }
678
+ delete healthCheckDataWithNoStorageInfo.storagePathSize
679
+ healthCheckDataWithNoStorageInfo.storagePathUsed = null
680
+
681
+ const shouldBePrimary =
682
+ 'https://missingStoragePathUsedAndStoragePathSize.audius.co'
683
+ nock(shouldBePrimary)
684
+ .get('/health_check/verbose')
685
+ .reply(200, { data: healthCheckDataWithNoStorageInfo })
686
+
687
+ const shouldBeSecondary1 = 'https://missingStoragePathUsed.audius.co'
688
+ nock(shouldBeSecondary1)
689
+ .get('/health_check/verbose')
690
+ .reply(200, {
691
+ data: {
692
+ ...healthCheckDataWithNoStorageInfo,
693
+ version: '1.2.1',
694
+ storagePathSize: 100
695
+ }
696
+ })
697
+
698
+ const shouldBeSecondary2 = 'https://missingStoragePathSize.audius.co'
699
+ nock(shouldBeSecondary2)
700
+ .get('/health_check/verbose')
701
+ .reply(200, {
702
+ data: {
703
+ ...healthCheckDataWithNoStorageInfo,
704
+ version: '1.2.0',
705
+ storagePathUsed: 30
706
+ }
707
+ })
708
+
709
+ const cns = new CreatorNodeSelection({
710
+ creatorNode: mockCreatorNode,
711
+ numberOfNodes: 3,
712
+ ethContracts: mockEthContracts(
713
+ [shouldBePrimary, shouldBeSecondary1, shouldBeSecondary2],
714
+ '1.2.3'
715
+ ),
716
+ whitelist: null,
717
+ blacklist: null,
718
+ maxStorageUsedPercent: 50
719
+ })
720
+
721
+ const { primary, secondaries, services } = await cns.select(true, false)
722
+
723
+ assert(primary === shouldBePrimary)
724
+ assert(secondaries.length === 2)
725
+ assert(secondaries.includes(shouldBeSecondary1))
726
+ assert(secondaries.includes(shouldBeSecondary2))
727
+
728
+ const returnedHealthyServices = new Set(Object.keys(services))
729
+ assert(returnedHealthyServices.size === 3)
730
+ const healthyServices = [
731
+ shouldBePrimary,
732
+ shouldBeSecondary1,
733
+ shouldBeSecondary2
734
+ ]
735
+ healthyServices.map((service) =>
736
+ assert(returnedHealthyServices.has(service))
737
+ )
738
+ })
739
+
740
+ it('does not always pick the same one with equivalency delta', async () => {
741
+ // Run this test a few times and make sure we eventually get something
742
+ // different
743
+ const primaries: string[] = []
744
+ for (let i = 0; i < 20; ++i) {
745
+ const one = 'https://one.audius.co'
746
+ nock(one)
747
+ .get('/health_check/verbose')
748
+ .delay(100)
749
+ .reply(200, { data: defaultHealthCheckData })
750
+
751
+ const two = 'https://two.audius.co'
752
+ nock(two)
753
+ .get('/health_check/verbose')
754
+ .delay(200)
755
+ .reply(200, { data: defaultHealthCheckData })
756
+
757
+ const cns = new CreatorNodeSelection({
758
+ creatorNode: mockCreatorNode,
759
+ numberOfNodes: 2,
760
+ ethContracts: mockEthContracts([one, two], '1.2.3'),
761
+ whitelist: null,
762
+ blacklist: null,
763
+ // Even though one and two take 100ms and 200ms respectively,
764
+ // we consider 200ms equivalent, so they should be randomly picked
765
+ equivalencyDelta: 200
766
+ })
767
+
768
+ const { primary } = await cns.select(true, false)
769
+ primaries.push(primary)
770
+ }
771
+ // Make sure there is some variance
772
+ assert(!primaries.every((val) => val === primaries[0]))
773
+ }).timeout(10000)
774
+
775
+ describe('Test preferHigherPatchForPrimary and preferHigherPatchForSecondaries', () => {
776
+ it('Selects highest version nodes when preferHigherPatchForPrimary and preferHigherPatchForSecondaries are enabled', async () => {
777
+ const aheadPatch = 'https://aheadPatch.audius.co'
778
+ nock(aheadPatch)
779
+ .get('/health_check/verbose')
780
+ .reply(200, { data: { ...defaultHealthCheckData, version: '1.2.6' } })
781
+
782
+ const aheadPatchButSlow = 'https://aheadPatchButSlow.audius.co'
783
+ nock(aheadPatchButSlow)
784
+ .get('/health_check/verbose')
785
+ .delay(100)
786
+ .reply(200, { data: { ...defaultHealthCheckData, version: '1.2.6' } })
787
+
788
+ const healthy = 'https://healthy.audius.co'
789
+ nock(healthy)
790
+ .get('/health_check/verbose')
791
+ .reply(200, { data: defaultHealthCheckData })
792
+
793
+ const cns = new CreatorNodeSelection({
794
+ creatorNode: mockCreatorNode,
795
+ numberOfNodes: 3,
796
+ ethContracts: mockEthContracts(
797
+ [healthy, aheadPatchButSlow, aheadPatch],
798
+ '1.2.3'
799
+ ),
800
+ whitelist: null,
801
+ blacklist: null
802
+ // preferHigherPatchForPrimary and preferHigherPatchForSecondaries true by default
803
+ })
804
+
805
+ const { primary, secondaries, services } = await cns.select(true, false)
806
+
807
+ assert(primary === aheadPatch)
808
+ assert(secondaries.length === 2)
809
+ assert(secondaries[0] === aheadPatchButSlow)
810
+ assert(secondaries[1] === healthy)
811
+
812
+ const returnedHealthyServices = new Set(Object.keys(services))
813
+ assert(returnedHealthyServices.size === 3)
814
+ const healthyServices = [aheadPatch, aheadPatchButSlow, healthy]
815
+ healthyServices.map((service) =>
816
+ assert(returnedHealthyServices.has(service))
817
+ )
818
+ })
819
+
820
+ it('Selects highest version node for primary with preferHigherPatchForPrimary enabled and fastest node for secondaries with preferHigherPatchForSecondaries disabled', async () => {
821
+ const aheadPatch = 'https://aheadPatch.audius.co'
822
+ nock(aheadPatch)
823
+ .get('/health_check/verbose')
824
+ .reply(200, { data: { ...defaultHealthCheckData, version: '1.2.6' } })
825
+
826
+ const aheadPatchButSlow = 'https://aheadPatchButSlow.audius.co'
827
+ nock(aheadPatchButSlow)
828
+ .get('/health_check/verbose')
829
+ .delay(100)
830
+ .reply(200, { data: { ...defaultHealthCheckData, version: '1.2.6' } })
831
+
832
+ const healthyButBehindPatch = 'https://healthyButBehindPatch.audius.co'
833
+ nock(healthyButBehindPatch)
834
+ .get('/health_check/verbose')
835
+ .reply(200, { data: { ...defaultHealthCheckData, version: '1.2.0' } })
836
+
837
+ const healthy = 'https://healthy.audius.co'
838
+ nock(healthy)
839
+ .get('/health_check/verbose')
840
+ .reply(200, { data: { ...defaultHealthCheckData, version: '1.2.3' } })
841
+
842
+ const cns = new CreatorNodeSelection({
843
+ creatorNode: mockCreatorNode,
844
+ numberOfNodes: 3,
845
+ ethContracts: mockEthContracts(
846
+ [healthy, healthyButBehindPatch, aheadPatchButSlow, aheadPatch],
847
+ '1.2.3'
848
+ ),
849
+ whitelist: null,
850
+ blacklist: null,
851
+ // preferHigherPatchForPrimary defaults to true
852
+ preferHigherPatchForSecondaries: false
853
+ })
854
+
855
+ const { primary, secondaries, services } = await cns.select(true, false)
856
+
857
+ assert(primary === aheadPatch)
858
+ assert(secondaries.length === 2)
859
+ assert(secondaries[0] === healthy)
860
+ assert(secondaries[1] === aheadPatchButSlow)
861
+
862
+ const returnedHealthyServices = new Set(Object.keys(services))
863
+ assert(returnedHealthyServices.size === 4)
864
+ const healthyServices = [
865
+ aheadPatch,
866
+ aheadPatchButSlow,
867
+ healthy,
868
+ healthyButBehindPatch
869
+ ]
870
+ healthyServices.map((service) =>
871
+ assert(returnedHealthyServices.has(service))
872
+ )
873
+ })
874
+
875
+ it('Selects fastest node for primary with preferHigherPatchForPrimary disabled and highest version nodes for secondaries with preferHigherPatchForSecondaries enabled', async () => {
876
+ const higherPatchAndSlow = 'https://higherPatchAndSlow.audius.co'
877
+ nock(higherPatchAndSlow)
878
+ .get('/health_check/verbose')
879
+ .delay(50)
880
+ .reply(200, { data: { ...defaultHealthCheckData, version: '1.2.4' } })
881
+
882
+ const highestPatchAndSlowest = 'https://highestPatchAndSlowest.audius.co'
883
+ nock(highestPatchAndSlowest)
884
+ .get('/health_check/verbose')
885
+ .delay(100)
886
+ .reply(200, { data: { ...defaultHealthCheckData, version: '1.2.5' } })
887
+
888
+ const healthyAndBehindPatch = 'https://healthyAndBehindPatch.audius.co'
889
+ nock(healthyAndBehindPatch)
890
+ .get('/health_check/verbose')
891
+ .reply(200, { data: { ...defaultHealthCheckData, version: '1.2.2' } })
892
+
893
+ const healthy = 'https://healthy.audius.co'
894
+ nock(healthy)
895
+ .get('/health_check/verbose')
896
+ .reply(200, { data: { ...defaultHealthCheckData, version: '1.2.3' } })
897
+
898
+ const cns = new CreatorNodeSelection({
899
+ creatorNode: mockCreatorNode,
900
+ numberOfNodes: 3,
901
+ ethContracts: mockEthContracts(
902
+ [
903
+ healthy,
904
+ healthyAndBehindPatch,
905
+ higherPatchAndSlow,
906
+ highestPatchAndSlowest
907
+ ],
908
+ '1.2.3'
909
+ ),
910
+ whitelist: null,
911
+ blacklist: null,
912
+ preferHigherPatchForPrimary: false
913
+ // preferHigherPatchForSecondaries defaults to true
914
+ })
915
+
916
+ const { primary, secondaries, services } = await cns.select(true, false)
917
+
918
+ assert(primary === healthy)
919
+ assert(secondaries.length === 2)
920
+ assert(secondaries[0] === highestPatchAndSlowest)
921
+ assert(secondaries[1] === higherPatchAndSlow)
922
+
923
+ const returnedHealthyServices = new Set(Object.keys(services))
924
+ assert(returnedHealthyServices.size === 4)
925
+ const healthyServices = [
926
+ highestPatchAndSlowest,
927
+ higherPatchAndSlow,
928
+ healthy,
929
+ healthyAndBehindPatch
930
+ ]
931
+ healthyServices.map((service) =>
932
+ assert(returnedHealthyServices.has(service))
933
+ )
934
+ })
935
+
936
+ it('Selects fastest nodes when preferHigherPatchForPrimary and preferHigherPatchForSecondaries are disabled', async () => {
937
+ const higherPatchAndSlow = 'https://higherPatchAndSlow.audius.co'
938
+ nock(higherPatchAndSlow)
939
+ .get('/health_check/verbose')
940
+ .delay(50)
941
+ .reply(200, { data: { ...defaultHealthCheckData, version: '1.2.4' } })
942
+
943
+ const highestPatchAndSlowest = 'https://highestPatchAndSlowest.audius.co'
944
+ nock(highestPatchAndSlowest)
945
+ .get('/health_check/verbose')
946
+ .delay(100)
947
+ .reply(200, { data: { ...defaultHealthCheckData, version: '1.2.5' } })
948
+
949
+ const healthyAndBehindPatch = 'https://healthyAndBehindPatch.audius.co'
950
+ nock(healthyAndBehindPatch)
951
+ .get('/health_check/verbose')
952
+ .reply(200, { data: { ...defaultHealthCheckData, version: '1.2.2' } })
953
+
954
+ const healthy = 'https://healthy.audius.co'
955
+ nock(healthy)
956
+ .get('/health_check/verbose')
957
+ .reply(200, { data: { ...defaultHealthCheckData, version: '1.2.3' } })
958
+
959
+ const cns = new CreatorNodeSelection({
960
+ creatorNode: mockCreatorNode,
961
+ numberOfNodes: 3,
962
+ ethContracts: mockEthContracts(
963
+ [
964
+ healthy,
965
+ healthyAndBehindPatch,
966
+ higherPatchAndSlow,
967
+ highestPatchAndSlowest
968
+ ],
969
+ '1.2.3'
970
+ ),
971
+ whitelist: null,
972
+ blacklist: null,
973
+ preferHigherPatchForPrimary: false,
974
+ preferHigherPatchForSecondaries: false
975
+ })
976
+
977
+ const { primary, secondaries, services } = await cns.select(true, false)
978
+
979
+ assert(primary === healthy)
980
+ assert(secondaries.length === 2)
981
+ assert(secondaries[0] === higherPatchAndSlow)
982
+ assert(secondaries[1] === highestPatchAndSlowest)
983
+
984
+ const returnedHealthyServices = new Set(Object.keys(services))
985
+ assert(returnedHealthyServices.size === 4)
986
+ const healthyServices = [
987
+ highestPatchAndSlowest,
988
+ higherPatchAndSlow,
989
+ healthy,
990
+ healthyAndBehindPatch
991
+ ]
992
+ healthyServices.map((service) =>
993
+ assert(returnedHealthyServices.has(service))
994
+ )
995
+ })
996
+ })
997
+ })