@rabbitio/ui-kit 1.0.0-beta.110 → 1.0.0-beta.112

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 (212) hide show
  1. package/.env +1 -1
  2. package/.env.example +1 -1
  3. package/coverage/clover.xml +1411 -1410
  4. package/coverage/coverage-final.json +18 -18
  5. package/coverage/index.html +17 -17
  6. package/coverage/ui-kit/index.html +1 -1
  7. package/coverage/ui-kit/index.js.html +1 -1
  8. package/coverage/ui-kit/src/common-apis/adapters/analyticsAdapters/googleAnalyticsAdapter.js.html +1 -1
  9. package/coverage/ui-kit/src/common-apis/adapters/analyticsAdapters/index.html +1 -1
  10. package/coverage/ui-kit/src/common-apis/adapters/analyticsAdapters/metrikaAdapter.js.html +1 -1
  11. package/coverage/ui-kit/src/common-apis/adapters/analyticsAdapters/mixpanelAdapter.js.html +1 -1
  12. package/coverage/ui-kit/src/common-apis/adapters/axiosAdapter.js.html +1 -1
  13. package/coverage/ui-kit/src/common-apis/adapters/index.html +1 -1
  14. package/coverage/ui-kit/src/common-apis/adapters/qrUtils.js.html +1 -1
  15. package/coverage/ui-kit/src/common-apis/external-apis/apiGroups.js.html +1 -1
  16. package/coverage/ui-kit/src/common-apis/external-apis/emailAPI.js.html +1 -1
  17. package/coverage/ui-kit/src/common-apis/external-apis/index.html +1 -1
  18. package/coverage/ui-kit/src/common-apis/external-apis/ipAddressProviders.js.html +1 -1
  19. package/coverage/ui-kit/src/common-apis/globalConstants.jsx.html +1 -1
  20. package/coverage/ui-kit/src/common-apis/index.html +1 -1
  21. package/coverage/ui-kit/src/common-apis/models/blockchain.js.html +4 -4
  22. package/coverage/ui-kit/src/common-apis/models/coin.js.html +221 -221
  23. package/coverage/ui-kit/src/common-apis/models/index.html +1 -1
  24. package/coverage/ui-kit/src/common-apis/models/protocol.js.html +3 -3
  25. package/coverage/ui-kit/src/common-apis/services/fiatCurrenciesService.js.html +1 -1
  26. package/coverage/ui-kit/src/common-apis/services/index.html +1 -1
  27. package/coverage/ui-kit/src/common-apis/utils/amountUtils.js.html +21 -21
  28. package/coverage/ui-kit/src/common-apis/utils/cache.js.html +6 -6
  29. package/coverage/ui-kit/src/common-apis/utils/errorUtils.js.html +13 -13
  30. package/coverage/ui-kit/src/common-apis/utils/index.html +1 -1
  31. package/coverage/ui-kit/src/common-apis/utils/logging/index.html +1 -1
  32. package/coverage/ui-kit/src/common-apis/utils/logging/logger.js.html +22 -22
  33. package/coverage/ui-kit/src/common-apis/utils/logging/logsStorage.js.html +3 -3
  34. package/coverage/ui-kit/src/common-apis/utils/postponeExecution.js.html +1 -1
  35. package/coverage/ui-kit/src/common-apis/utils/rabbitTicker.js.html +10 -10
  36. package/coverage/ui-kit/src/common-apis/utils/safeStringify.js.html +27 -27
  37. package/coverage/ui-kit/src/index.html +1 -1
  38. package/coverage/ui-kit/src/index.js.html +1 -1
  39. package/coverage/ui-kit/src/robust-api-caller/cacheAndConcurrentRequestsResolver.js.html +1 -1
  40. package/coverage/ui-kit/src/robust-api-caller/cachedRobustExternalApiCallerService.js.html +1 -1
  41. package/coverage/ui-kit/src/robust-api-caller/cancelProcessing.js.html +1 -1
  42. package/coverage/ui-kit/src/robust-api-caller/concurrentCalculationsMetadataHolder.js.html +1 -1
  43. package/coverage/ui-kit/src/robust-api-caller/externalApiProvider.js.html +1 -1
  44. package/coverage/ui-kit/src/robust-api-caller/externalServicesStatsCollector.js.html +1 -1
  45. package/coverage/ui-kit/src/robust-api-caller/index.html +1 -1
  46. package/coverage/ui-kit/src/robust-api-caller/robustExternalAPICallerService.js.html +1 -1
  47. package/coverage/ui-kit/src/swaps-lib/external-apis/exolixSwapProvider.js.html +66 -45
  48. package/coverage/ui-kit/src/swaps-lib/external-apis/goexmeSwapProvider.js.html +521 -545
  49. package/coverage/ui-kit/src/swaps-lib/external-apis/index.html +27 -27
  50. package/coverage/ui-kit/src/swaps-lib/external-apis/letsExchangeSwapProvider.js.html +1 -1
  51. package/coverage/ui-kit/src/swaps-lib/external-apis/swapProvider.js.html +328 -328
  52. package/coverage/ui-kit/src/swaps-lib/external-apis/swapspaceSwapProvider.js.html +76 -52
  53. package/coverage/ui-kit/src/swaps-lib/external-apis/utils.js.html +14 -14
  54. package/coverage/ui-kit/src/swaps-lib/models/baseSwapCreationInfo.js.html +1 -1
  55. package/coverage/ui-kit/src/swaps-lib/models/existingSwap.js.html +141 -141
  56. package/coverage/ui-kit/src/swaps-lib/models/existingSwapWithFiatData.js.html +1 -1
  57. package/coverage/ui-kit/src/swaps-lib/models/index.html +1 -1
  58. package/coverage/ui-kit/src/swaps-lib/models/partner.js.html +1 -1
  59. package/coverage/ui-kit/src/swaps-lib/models/swapProviderCoinInfo.js.html +73 -73
  60. package/coverage/ui-kit/src/swaps-lib/services/index.html +1 -1
  61. package/coverage/ui-kit/src/swaps-lib/services/publicSwapService.js.html +1 -1
  62. package/coverage/ui-kit/src/swaps-lib/utils/index.html +1 -1
  63. package/coverage/ui-kit/src/swaps-lib/utils/swapUtils.js.html +1 -1
  64. package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/arrowIcon.jsx.html +1 -1
  65. package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/arrowTosca.jsx.html +1 -1
  66. package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/arrowWhite.jsx.html +1 -1
  67. package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/darkRectangle.jsx.html +1 -1
  68. package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/determinedError.jsx.html +1 -1
  69. package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/failedValidationIcon.jsx.html +1 -1
  70. package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/index.html +1 -1
  71. package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/infoIcon.jsx.html +1 -1
  72. package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/messageIcon.jsx.html +1 -1
  73. package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/noticeQuestionIcon.jsx.html +1 -1
  74. package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/successfulValidationIcon.jsx.html +1 -1
  75. package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/supportDialogImage.jsx.html +1 -1
  76. package/coverage/ui-kit/src/ui-kit/assets/wrappedImages/walletIcon.jsx.html +1 -1
  77. package/coverage/ui-kit/src/ui-kit/components/atoms/AssetIcon/AssetIcon.jsx.html +1 -1
  78. package/coverage/ui-kit/src/ui-kit/components/atoms/AssetIcon/index.html +1 -1
  79. package/coverage/ui-kit/src/ui-kit/components/atoms/AssetSelection/AssetSelection.jsx.html +1 -1
  80. package/coverage/ui-kit/src/ui-kit/components/atoms/AssetSelection/index.html +1 -1
  81. package/coverage/ui-kit/src/ui-kit/components/atoms/BackgroundTitle/BackgroundTitle.jsx.html +1 -1
  82. package/coverage/ui-kit/src/ui-kit/components/atoms/BackgroundTitle/index.html +1 -1
  83. package/coverage/ui-kit/src/ui-kit/components/atoms/InformationMessage/InformationMessage.jsx.html +1 -1
  84. package/coverage/ui-kit/src/ui-kit/components/atoms/InformationMessage/index.html +1 -1
  85. package/coverage/ui-kit/src/ui-kit/components/atoms/Input/Input.jsx.html +1 -1
  86. package/coverage/ui-kit/src/ui-kit/components/atoms/Input/index.html +1 -1
  87. package/coverage/ui-kit/src/ui-kit/components/atoms/LoadingDots/LoadingDots.jsx.html +1 -1
  88. package/coverage/ui-kit/src/ui-kit/components/atoms/LoadingDots/index.html +1 -1
  89. package/coverage/ui-kit/src/ui-kit/components/atoms/NoticeIcon/NoticeIcon.jsx.html +1 -1
  90. package/coverage/ui-kit/src/ui-kit/components/atoms/NoticeIcon/index.html +1 -1
  91. package/coverage/ui-kit/src/ui-kit/components/atoms/QrCode/QrCode.jsx.html +7 -25
  92. package/coverage/ui-kit/src/ui-kit/components/atoms/QrCode/index.html +5 -5
  93. package/coverage/ui-kit/src/ui-kit/components/atoms/RateSelector/RateSelector.jsx.html +1 -1
  94. package/coverage/ui-kit/src/ui-kit/components/atoms/RateSelector/index.html +1 -1
  95. package/coverage/ui-kit/src/ui-kit/components/atoms/SupportChat/SupportChat.jsx.html +1 -1
  96. package/coverage/ui-kit/src/ui-kit/components/atoms/SupportChat/index.html +1 -1
  97. package/coverage/ui-kit/src/ui-kit/components/atoms/Textarea/Textarea.jsx.html +1 -1
  98. package/coverage/ui-kit/src/ui-kit/components/atoms/Textarea/index.html +1 -1
  99. package/coverage/ui-kit/src/ui-kit/components/atoms/TitleBox/TitleBox.jsx.html +1 -1
  100. package/coverage/ui-kit/src/ui-kit/components/atoms/TitleBox/index.html +1 -1
  101. package/coverage/ui-kit/src/ui-kit/components/atoms/Tooltip/Tooltip.jsx.html +1 -1
  102. package/coverage/ui-kit/src/ui-kit/components/atoms/Tooltip/index.html +1 -1
  103. package/coverage/ui-kit/src/ui-kit/components/atoms/TwoLinesOfText/LinesOfText.jsx.html +1 -1
  104. package/coverage/ui-kit/src/ui-kit/components/atoms/TwoLinesOfText/index.html +1 -1
  105. package/coverage/ui-kit/src/ui-kit/components/atoms/Validation/Validation.jsx.html +1 -1
  106. package/coverage/ui-kit/src/ui-kit/components/atoms/Validation/index.html +1 -1
  107. package/coverage/ui-kit/src/ui-kit/components/atoms/buttons/Button/Button.jsx.html +1 -1
  108. package/coverage/ui-kit/src/ui-kit/components/atoms/buttons/Button/index.html +1 -1
  109. package/coverage/ui-kit/src/ui-kit/components/atoms/buttons/Close/Close.jsx.html +1 -1
  110. package/coverage/ui-kit/src/ui-kit/components/atoms/buttons/Close/index.html +1 -1
  111. package/coverage/ui-kit/src/ui-kit/components/atoms/buttons/LinkButton/LinkButton.jsx.html +1 -1
  112. package/coverage/ui-kit/src/ui-kit/components/atoms/buttons/LinkButton/index.html +1 -1
  113. package/coverage/ui-kit/src/ui-kit/components/atoms/buttons/RadioButtonWithText/RadioButtonWithText.jsx.html +1 -1
  114. package/coverage/ui-kit/src/ui-kit/components/atoms/buttons/RadioButtonWithText/index.html +1 -1
  115. package/coverage/ui-kit/src/ui-kit/components/molecules/AmountInput/AmountInput.jsx.html +1 -1
  116. package/coverage/ui-kit/src/ui-kit/components/molecules/AmountInput/index.html +1 -1
  117. package/coverage/ui-kit/src/ui-kit/components/molecules/CoinPicker/CoinPicker.jsx.html +1 -1
  118. package/coverage/ui-kit/src/ui-kit/components/molecules/CoinPicker/index.html +1 -1
  119. package/coverage/ui-kit/src/ui-kit/components/molecules/ColoredNotice/ColoredNotice.jsx.html +1 -1
  120. package/coverage/ui-kit/src/ui-kit/components/molecules/ColoredNotice/index.html +1 -1
  121. package/coverage/ui-kit/src/ui-kit/components/molecules/LineWithIconLink/LineWithIconLink.jsx.html +1 -1
  122. package/coverage/ui-kit/src/ui-kit/components/molecules/LineWithIconLink/index.html +1 -1
  123. package/coverage/ui-kit/src/ui-kit/components/molecules/LogoCarousel/LogoCarousel.jsx.html +1 -1
  124. package/coverage/ui-kit/src/ui-kit/components/molecules/LogoCarousel/index.html +1 -1
  125. package/coverage/ui-kit/src/ui-kit/components/molecules/SearchableCoinsList/SearchableCoinsList.jsx.html +1 -1
  126. package/coverage/ui-kit/src/ui-kit/components/molecules/SearchableCoinsList/index.html +1 -1
  127. package/coverage/ui-kit/src/ui-kit/components/molecules/TitledLineWithIconLink/TitledLineWithIconLink.jsx.html +1 -1
  128. package/coverage/ui-kit/src/ui-kit/components/molecules/TitledLineWithIconLink/index.html +1 -1
  129. package/coverage/ui-kit/src/ui-kit/components/organisms/CoinPickerDialogStep/CoinPickerDialogStep.jsx.html +1 -1
  130. package/coverage/ui-kit/src/ui-kit/components/organisms/CoinPickerDialogStep/index.html +1 -1
  131. package/coverage/ui-kit/src/ui-kit/components/organisms/Dialog/Dialog.jsx.html +1 -1
  132. package/coverage/ui-kit/src/ui-kit/components/organisms/Dialog/DialogButtons/DialogButtons.jsx.html +1 -1
  133. package/coverage/ui-kit/src/ui-kit/components/organisms/Dialog/DialogButtons/index.html +1 -1
  134. package/coverage/ui-kit/src/ui-kit/components/organisms/Dialog/DialogStep/DialogStep.jsx.html +1 -1
  135. package/coverage/ui-kit/src/ui-kit/components/organisms/Dialog/DialogStep/index.html +1 -1
  136. package/coverage/ui-kit/src/ui-kit/components/organisms/Dialog/index.html +1 -1
  137. package/coverage/ui-kit/src/ui-kit/components/organisms/SwapForm/SwapForm.jsx.html +1 -1
  138. package/coverage/ui-kit/src/ui-kit/components/organisms/SwapForm/index.html +1 -1
  139. package/coverage/ui-kit/src/ui-kit/components/organisms/WaitlistSubscription/WaitlistSubscription.jsx.html +1 -1
  140. package/coverage/ui-kit/src/ui-kit/components/organisms/WaitlistSubscription/index.html +1 -1
  141. package/coverage/ui-kit/src/ui-kit/components/templates/DeterminedErrorDialogStep/DeterminedErrorDialogStep.jsx.html +1 -1
  142. package/coverage/ui-kit/src/ui-kit/components/templates/DeterminedErrorDialogStep/index.html +1 -1
  143. package/coverage/ui-kit/src/ui-kit/hooks/index.html +1 -1
  144. package/coverage/ui-kit/src/ui-kit/hooks/useCallHandlingErrors.js.html +1 -1
  145. package/coverage/ui-kit/src/ui-kit/hooks/useIsHydrated.js.html +1 -1
  146. package/coverage/ui-kit/src/ui-kit/hooks/useReferredState.js.html +1 -1
  147. package/coverage/ui-kit/src/ui-kit/utils/index.html +1 -1
  148. package/coverage/ui-kit/src/ui-kit/utils/inputValueProviders.js.html +1 -1
  149. package/coverage/ui-kit/src/ui-kit/utils/searchCoins.js.html +1 -1
  150. package/coverage/ui-kit/src/ui-kit/utils/textUtils.js.html +1 -1
  151. package/coverage/ui-kit/src/ui-kit/utils/uiUtils.js.html +1 -1
  152. package/coverage/ui-kit/src/ui-kit/utils/urlQueryUtils.js.html +1 -1
  153. package/coverage/ui-kit/stories/atoms/BackgroundTitle.stories.jsx.html +1 -1
  154. package/coverage/ui-kit/stories/atoms/LinesOfText.stories.jsx.html +1 -1
  155. package/coverage/ui-kit/stories/atoms/LoadingDots.stories.jsx.html +1 -1
  156. package/coverage/ui-kit/stories/atoms/QrCode.stories.jsx.html +1 -1
  157. package/coverage/ui-kit/stories/atoms/RateSelector.stories.jsx.html +1 -1
  158. package/coverage/ui-kit/stories/atoms/Validation.stories.jsx.html +1 -1
  159. package/coverage/ui-kit/stories/atoms/buttons/Button.stories.jsx.html +1 -1
  160. package/coverage/ui-kit/stories/atoms/buttons/Close.stories.jsx.html +1 -1
  161. package/coverage/ui-kit/stories/atoms/buttons/LinkButton.stories.jsx.html +1 -1
  162. package/coverage/ui-kit/stories/atoms/buttons/index.html +1 -1
  163. package/coverage/ui-kit/stories/atoms/index.html +1 -1
  164. package/coverage/ui-kit/stories/molecules/AmountInput.stories.jsx.html +1 -1
  165. package/coverage/ui-kit/stories/molecules/CoinPicker.stories.jsx.html +1 -1
  166. package/coverage/ui-kit/stories/molecules/ColoredNotice.stories.jsx.html +1 -1
  167. package/coverage/ui-kit/stories/molecules/LineWithIconLink.stories.jsx.html +1 -1
  168. package/coverage/ui-kit/stories/molecules/LogoCarousel.stories.jsx.html +1 -1
  169. package/coverage/ui-kit/stories/molecules/TitledLineWithIconLink.stories.jsx.html +1 -1
  170. package/coverage/ui-kit/stories/molecules/index.html +1 -1
  171. package/coverage/ui-kit/stories/organisms/Dialog/Dialog.stories.jsx.html +1 -1
  172. package/coverage/ui-kit/stories/organisms/Dialog/DialogButtons/DialogButtons.stories.jsx.html +1 -1
  173. package/coverage/ui-kit/stories/organisms/Dialog/DialogButtons/index.html +1 -1
  174. package/coverage/ui-kit/stories/organisms/Dialog/DialogStep/DialogStep.stories.jsx.html +1 -1
  175. package/coverage/ui-kit/stories/organisms/Dialog/DialogStep/index.html +1 -1
  176. package/coverage/ui-kit/stories/organisms/Dialog/index.html +1 -1
  177. package/coverage/ui-kit/stories/organisms/WaitlistSubscription.stories.jsx.html +1 -1
  178. package/coverage/ui-kit/stories/organisms/index.html +1 -1
  179. package/coverage/ui-kit/stories/stubs/coins.jsx.html +1 -1
  180. package/coverage/ui-kit/stories/stubs/exampleContent.jsx.html +1 -1
  181. package/coverage/ui-kit/stories/stubs/index.html +1 -1
  182. package/coverage/ui-kit/stories/templates/DeterminedErrorDialogStep.stories.jsx.html +1 -1
  183. package/coverage/ui-kit/stories/templates/index.html +1 -1
  184. package/dist/index.cjs +94 -92
  185. package/dist/index.cjs.map +1 -1
  186. package/dist/index.css +4 -12
  187. package/dist/index.css.map +1 -1
  188. package/dist/index.modern.js +82 -78
  189. package/dist/index.modern.js.map +1 -1
  190. package/dist/index.module.js +94 -92
  191. package/dist/index.module.js.map +1 -1
  192. package/dist/index.umd.js +94 -92
  193. package/dist/index.umd.js.map +1 -1
  194. package/package.json +1 -1
  195. package/src/common-apis/tests/integration/external-apis/ipAddressProviders/getClientIpAddress.test.js +9 -5
  196. package/src/swaps-lib/external-apis/exolixSwapProvider.js +46 -39
  197. package/src/swaps-lib/external-apis/goexmeSwapProvider.js +29 -37
  198. package/src/swaps-lib/external-apis/swapspaceSwapProvider.js +54 -46
  199. package/src/swaps-lib/models/existingSwap.js +1 -1
  200. package/src/swaps-lib/test/external-apis/goexmeSwapProvider/_convertCurrencyMeta.test.js +80 -0
  201. package/src/swaps-lib/test/external-apis/goexmeSwapProvider/_ensurePairsLoaded.test.js +77 -0
  202. package/src/swaps-lib/test/external-apis/goexmeSwapProvider/_fetchSupportedCurrenciesIfNeeded.test.js +50 -123
  203. package/src/swaps-lib/test/external-apis/goexmeSwapProvider/createSwap.test.js +184 -72
  204. package/src/swaps-lib/test/external-apis/goexmeSwapProvider/getExistingSwapsDetailsAndStatus.test.js +151 -0
  205. package/src/swaps-lib/test/external-apis/goexmeSwapProvider/getSwapInfo.test.js +155 -104
  206. package/src/swaps-lib/test/external-apis/goexmeSwapProvider/helpers.test.js +26 -0
  207. package/src/swaps-lib/test/external-apis/goexmeSwapProvider/integration/PairSupport.int.test.js +57 -57
  208. package/src/swaps-lib/test/external-apis/goexmeSwapProvider/integration/_fetchSupportedCurrenciesIfNeeded.int.test.js +289 -126
  209. package/src/swaps-lib/test/external-apis/goexmeSwapProvider/integration/createSwap.int.test.js +320 -225
  210. package/src/swaps-lib/test/external-apis/goexmeSwapProvider/integration/getSwapInfo.int.test.js +138 -127
  211. package/src/ui-kit/components/atoms/QrCode/QrCode.jsx +2 -8
  212. package/src/ui-kit/components/atoms/QrCode/qr-code.module.scss +4 -11
@@ -1,148 +1,75 @@
1
1
  import sinon from "sinon";
2
2
  import axios from "axios";
3
3
  import should from "should";
4
- import { describe, it, beforeEach, afterEach } from "vitest";
4
+ import { beforeEach, afterEach, describe, it } from "vitest";
5
5
 
6
6
  import { GoexmeSwapProvider } from "../../../external-apis/goexmeSwapProvider.js";
7
7
  import { Cache } from "../../../../common-apis/utils/cache.js";
8
- import { Coin } from "../../../../common-apis/models/coin.js";
9
- import { Protocol } from "../../../../common-apis/models/protocol.js";
10
-
11
- describe("goexmeSwapProvider::_fetchSupportedCurrenciesIfNeeded-stack", () => {
12
- const apiOK = {
13
- btc: { coinName: "Bitcoin", available: true, network: "", tagname: "", minamount: 0.001, maxamount: 10 },
14
- eth: { coinName: "Ethereum", available: true, network: "erc20", tagname: "" },
15
- usdt: { coinName: "Tether", available: false, network: "" },
16
- };
17
-
18
- let provider, axiosStub;
19
- beforeEach(() => {
20
- provider = new GoexmeSwapProvider("/", new Cache(), () => null, false);
21
- axiosStub = sinon.stub(axios, "get");
22
- });
23
- afterEach(() => sinon.restore());
24
8
 
25
- it("constructor sets direct URL when apiKey passed", () => {
26
- const p = new GoexmeSwapProvider("/proxy", new Cache(), () => null, false, "key");
27
- p._URL.should.equal("https://goexme.io/v1/api");
28
- });
9
+ describe("goexmeSwapProvider", function () {
10
+ describe("_fetchSupportedCurrenciesIfNeeded", function () {
11
+ let provider, axiosGetStub;
29
12
 
30
- it("_convertCurrencyMeta returns null for restricted set", () => {
31
- const restricted = new GoexmeSwapProvider("/", new Cache(), () => null, true);
32
- should(restricted._convertCurrencyMeta("abc", { coinName: "Abc" }, new Set())).be.null();
33
- });
13
+ beforeEach(() => {
14
+ provider = new GoexmeSwapProvider("/", new Cache(), () => null, false);
15
+ axiosGetStub = sinon.stub(axios, "get");
16
+ });
34
17
 
35
- it("_convertCurrencyMeta auto-constructs Coin when builder=null and unrestricted", () => {
36
- const unrestricted = new GoexmeSwapProvider("/", new Cache(), null, false);
37
- const info = unrestricted._convertCurrencyMeta(
38
- "btc",
39
- { coinName: "Bitcoin Test", network: "", available: true },
40
- new Set()
41
- );
42
- info.should.have.properties({ code: "BTC", network: null });
43
- info.coin.should.be.instanceOf(Coin);
44
- if (info.coin.protocol) info.coin.protocol.should.be.instanceOf(Protocol);
45
- });
18
+ afterEach(() => {
19
+ axiosGetStub.restore();
20
+ provider._supportedCoins = [];
21
+ });
46
22
 
47
- it("fetches once per batch", async () => {
48
- axiosStub.resolves({ status: 200, data: apiOK });
49
- await provider._fetchSupportedCurrenciesIfNeeded();
50
- await provider._fetchSupportedCurrenciesIfNeeded();
51
- axiosStub.calledOnce.should.be.true();
52
- provider._supportedCoins.length.should.equal(2);
53
- provider._supportedCoins.forEach(i => i.coin.should.be.instanceOf(Coin));
54
- });
23
+ it("Should load and map available currencies when status is 200", async () => {
24
+ const apiOK = {
25
+ btc: { coinName: "Bitcoin", available: true, network: "", tagname: "" },
26
+ eth: { coinName: "Ethereum", available: true, network: "erc20", tagname: "" },
27
+ usdt: { coinName: "Tether", available: false, network: "" },
28
+ };
29
+ axiosGetStub.resolves({ status: 200, data: apiOK });
55
30
 
56
- it("skips coins with available false", async () => {
57
- axiosStub.resolves({ status: 200, data: apiOK });
58
- await provider._fetchSupportedCurrenciesIfNeeded();
59
- provider._supportedCoins.some(i => i.code === "usdt").should.be.false();
60
- });
31
+ await provider._fetchSupportedCurrenciesIfNeeded();
61
32
 
62
- it("throws on non-200 response", async () => {
63
- axiosStub.resolves({ status: 500, data: {} });
64
- await provider._fetchSupportedCurrenciesIfNeeded().should.be.rejected();
65
- });
33
+ provider._supportedCoins.length.should.equal(2);
34
+ provider._supportedCoins.forEach(info => info.coin.should.be.instanceOf(info.coin.constructor));
35
+ });
66
36
 
67
- it("propagates axios error", async () => {
68
- axiosStub.rejects(new Error("network"));
69
- await provider._fetchSupportedCurrenciesIfNeeded().should.be.rejectedWith(/network/);
70
- });
71
- });
37
+ it("Should skip currencies with available=false", async () => {
38
+ const apiPartial = {
39
+ btc: { coinName: "Bitcoin", available: false, network: "", tagname: "" },
40
+ eth: { coinName: "Ethereum", available: true, network: "", tagname: "" },
41
+ };
42
+ axiosGetStub.resolves({ status: 200, data: apiPartial });
72
43
 
73
- describe("goexmeSwapProvider::_ensurePairsLoaded", () => {
74
- let provider, axiosStub;
75
- beforeEach(() => {
76
- provider = new GoexmeSwapProvider("/", new Cache(), () => null, false);
77
- axiosStub = sinon
78
- .stub(axios, "get")
79
- .callsFake(url =>
80
- url.includes("/pairs")
81
- ? Promise.resolve({ status: 200, data: { BTC: ["ETH"] } })
82
- : Promise.resolve({ status: 200, data: {} })
83
- );
84
- });
85
- afterEach(() => sinon.restore());
44
+ await provider._fetchSupportedCurrenciesIfNeeded();
86
45
 
87
- it("loads pairs only once within TTL", async () => {
88
- await provider._ensurePairsLoaded();
89
- const calls = axiosStub.callCount;
90
- await provider._ensurePairsLoaded();
91
- axiosStub.callCount.should.equal(calls);
92
- });
46
+ provider._supportedCoins.length.should.equal(1);
47
+ provider._supportedCoins[0].code.should.equal("ETH");
48
+ });
93
49
 
94
- it("refreshes pairs after expiration", async () => {
95
- await provider._ensurePairsLoaded();
96
- provider._pairsCacheTimestamp -= provider.PAIRS_EXPIRATION_TIME_MS + 1000;
97
- await provider._ensurePairsLoaded();
98
- axiosStub.callCount.should.equal(2);
99
- });
50
+ it("Should reject when response status is not 200", async () => {
51
+ axiosGetStub.resolves({ status: 500, data: {} });
100
52
 
101
- it("throws on non-200", async () => {
102
- axiosStub.resetBehavior();
103
- axiosStub.resolves({ status: 400, data: {} });
104
- await provider._ensurePairsLoaded().should.be.rejectedWith(/Failed to load/);
105
- });
53
+ return provider._fetchSupportedCurrenciesIfNeeded().should.be.rejected();
54
+ });
106
55
 
107
- it("stores upper-case tickers", async () => {
108
- await provider._ensurePairsLoaded();
109
- provider._pairsCache.should.have.property("BTC");
110
- provider._pairsCache.BTC.has("ETH").should.be.true();
111
- });
112
- });
56
+ it("Should propagate axios error", async () => {
57
+ axiosGetStub.rejects(new Error("network"));
113
58
 
114
- describe("goexmeSwapProvider::_fetchSupportedCurrenciesIfNeeded race", () => {
115
- let provider, axiosStub;
116
- beforeEach(() => {
117
- provider = new GoexmeSwapProvider("/", new Cache(), () => null, false);
118
- axiosStub = sinon.stub(axios, "get").resolves({
119
- status: 200,
120
- data: { btc: { coinName: "btc", available: true } },
59
+ return provider._fetchSupportedCurrenciesIfNeeded().should.be.rejectedWith(/network/);
121
60
  });
122
- });
123
- afterEach(() => sinon.restore());
124
61
 
125
- it("two parallel calls trigger two HTTP requests (no dedupe)", async () => {
126
- await Promise.all([provider._fetchSupportedCurrenciesIfNeeded(), provider._fetchSupportedCurrenciesIfNeeded()]);
127
- axiosStub.callCount.should.equal(2);
128
- });
129
- });
130
-
131
- describe("goexmeSwapProvider::helpers", () => {
132
- it("getSwapCreationInfoTtlMs returns 30 000 ms", () => {
133
- const p = new GoexmeSwapProvider("/", new Cache());
134
- p.getSwapCreationInfoTtlMs().should.equal(30000);
135
- });
62
+ it("Should not call axios again while cache is fresh", async () => {
63
+ const apiOK = {
64
+ btc: { coinName: "Bitcoin", available: true, network: "", tagname: "" },
65
+ };
66
+ axiosGetStub.resolves({ status: 200, data: apiOK });
136
67
 
137
- it("_parseGoexmeDate converts to epoch", () => {
138
- const ts = GoexmeSwapProvider._parseGoexmeDate("01.02.2024 03:04:05");
139
- ts.should.equal(Date.parse("2024-02-01T03:04:05Z"));
140
- });
68
+ await provider._fetchSupportedCurrenciesIfNeeded();
69
+ const initialCalls = axiosGetStub.callCount;
70
+ await provider._fetchSupportedCurrenciesIfNeeded();
141
71
 
142
- it("getPartnersList returns single partner", async () => {
143
- const p = new GoexmeSwapProvider("/", new Cache());
144
- const partners = await p.getPartnersList();
145
- partners.should.have.length(1);
146
- partners[0].should.have.property("name", "GoExme");
72
+ axiosGetStub.callCount.should.equal(initialCalls);
73
+ });
147
74
  });
148
75
  });
@@ -1,7 +1,8 @@
1
1
  import sinon from "sinon";
2
2
  import axios from "axios";
3
3
  import should from "should";
4
- import { describe, it, beforeEach, afterEach, expect } from "vitest";
4
+ import { describe, it, beforeEach, afterEach } from "vitest";
5
+ import BigNumber from "bignumber.js";
5
6
 
6
7
  import { GoexmeSwapProvider } from "../../../external-apis/goexmeSwapProvider.js";
7
8
  import { Cache } from "../../../../common-apis/utils/cache.js";
@@ -28,88 +29,199 @@ const ETH = new Coin(
28
29
 
29
30
  const ESTIMATION_STUB = { rate: "0.09143008", rawSwapData: { rate: "0.09143008" } };
30
31
 
31
- describe("goexmeSwapProvider::createSwap", () => {
32
- const apiOk = { result: true, id: "id1", depositAddress: "addr-in" };
33
- let provider, axiosStub;
34
-
35
- beforeEach(() => {
36
- provider = new GoexmeSwapProvider("/", new Cache(), () => null, false);
37
- provider._supportedCoins = [
38
- { coin: BTC, code: "BTC", network: "BTC", hasExtraId: true },
39
- { coin: ETH, code: "ETH", network: "ETH", hasExtraId: true },
40
- ];
41
- provider._pairsCache = { BTC: new Set(["ETH"]) };
42
- provider._pairsCacheTimestamp = Date.now();
43
-
44
- sinon.stub(provider, "_fetchSupportedCurrenciesIfNeeded").resolves();
45
- axiosStub = sinon.stub(axios, "get").resolves({ status: 200, data: apiOk });
46
- });
32
+ describe("goexmeSwapProvider", function () {
33
+ describe("createSwap", function () {
34
+ const apiOk = { result: true, id: "id1", depositAddress: "addr-in" };
35
+ let provider, axiosStub;
36
+
37
+ beforeEach(() => {
38
+ provider = new GoexmeSwapProvider("/", new Cache(), () => null, false);
39
+ provider._supportedCoins = [
40
+ { coin: BTC, code: "BTC", network: "BTC", hasExtraId: false },
41
+ { coin: ETH, code: "ETH", network: "ETH", hasExtraId: true },
42
+ ];
43
+ provider._pairsCache = { BTC: new Set(["ETH"]), ETH: new Set(["BTC"]) };
44
+ provider._pairsCacheTimestamp = Date.now();
45
+
46
+ sinon.stub(provider, "_fetchSupportedCurrenciesIfNeeded").resolves();
47
+ axiosStub = sinon.stub(axios, "get").resolves({ status: 200, data: apiOk });
48
+ });
47
49
 
48
- afterEach(() => sinon.restore());
50
+ afterEach(() => sinon.restore());
49
51
 
50
- it("fetches currencies first", async () => {
51
- await provider.createSwap(BTC, ETH, "1", "dest", "", ESTIMATION_STUB, null, false, "memo");
52
- provider._fetchSupportedCurrenciesIfNeeded.calledOnce.should.be.true();
53
- });
52
+ it("Should fetch currencies before creating swap", async () => {
53
+ await provider.createSwap(BTC, ETH, "1", "dest", "", ESTIMATION_STUB, null, false, "memo");
54
+ provider._fetchSupportedCurrenciesIfNeeded.calledOnce.should.be.true();
55
+ });
54
56
 
55
- it("sends correct query params", async () => {
56
- await provider.createSwap(BTC, ETH, "0.2", "dest-addr", "", ESTIMATION_STUB, null, false, "memo");
57
- const p = axiosStub.args[0][1].params;
58
- p.should.match({
59
- from: "BTC",
60
- to: "ETH",
61
- amount: "0.2",
62
- destinationAddress: "dest-addr",
63
- destinationTag: "memo",
57
+ it("Should send correct query params", async () => {
58
+ await provider.createSwap(BTC, ETH, "0.2", "dest-addr", "", ESTIMATION_STUB, null, false, "memo");
59
+ const params = axiosStub.args[0][1].params;
60
+ params.should.match({
61
+ from: "BTC",
62
+ to: "ETH",
63
+ amount: "0.2",
64
+ destinationAddress: "dest-addr",
65
+ destinationTag: "memo",
66
+ });
64
67
  });
65
- });
66
68
 
67
- it("adds fix param when fixed=true", async () => {
68
- await provider.createSwap(BTC, ETH, "0.3", "dest", "", ESTIMATION_STUB, null, true, "memo");
69
- axiosStub.args[0][1].params.should.have.property("fix", true);
70
- });
69
+ it("Should add fix param when fixed=true", async () => {
70
+ await provider.createSwap(BTC, ETH, "0.3", "dest", "", ESTIMATION_STUB, null, true, "memo");
71
+ axiosStub.args[0][1].params.should.have.property("fix", true);
72
+ });
71
73
 
72
- it("passes refundAddress and refundTag", async () => {
73
- await provider.createSwap(BTC, ETH, "1", "dest", "refundAddr", ESTIMATION_STUB, null, true, "memo", "123tag");
74
- const p = axiosStub.args[0][1].params;
75
- p.should.have.properties({ refundAddress: "refundAddr", refundTag: "123tag" });
76
- });
74
+ it("Should pass refundAddress and refundTag", async () => {
75
+ provider._supportedCoins[0].hasExtraId = true;
76
+ await provider.createSwap(
77
+ BTC,
78
+ ETH,
79
+ "1",
80
+ "dest",
81
+ "refundAddr",
82
+ ESTIMATION_STUB,
83
+ null,
84
+ true,
85
+ "memo",
86
+ "123tag"
87
+ );
88
+ const params = axiosStub.args[0][1].params;
89
+ params.should.have.properties({ refundAddress: "refundAddr", refundTag: "123tag" });
90
+ });
77
91
 
78
- it("handles 429", async () => {
79
- axiosStub.rejects({ response: { status: 429 } });
80
- const r = await provider.createSwap(BTC, ETH, "1", "dest", "", ESTIMATION_STUB, null, false, "memo");
81
- r.reason.should.equal(SwapProvider.COMMON_ERRORS.REQUESTS_LIMIT_EXCEEDED);
82
- });
92
+ it("Should map HTTP 429 to REQUESTS_LIMIT_EXCEEDED", async () => {
93
+ axiosStub.rejects({ response: { status: 429 } });
94
+ const r = await provider.createSwap(BTC, ETH, "1", "dest", "", ESTIMATION_STUB, null, false, "memo");
95
+ r.reason.should.equal(SwapProvider.COMMON_ERRORS.REQUESTS_LIMIT_EXCEEDED);
96
+ });
83
97
 
84
- it("throws internal error when API result=false", async () => {
85
- axiosStub.resolves({ status: 200, data: { result: false } });
86
- await expect(
87
- provider.createSwap(BTC, ETH, "1", "dest", "", ESTIMATION_STUB, null, false, "memo")
88
- ).rejects.toThrow(/Goexme createSwap failed/);
89
- });
98
+ it("Should throw when provider responds with 5xx", async () => {
99
+ axiosStub.resolves({ status: 500, data: {} });
100
+ await provider
101
+ .createSwap(BTC, ETH, "1", "dest", "", ESTIMATION_STUB, null, false, "memo")
102
+ .should.be.rejected();
103
+ });
90
104
 
91
- it("throws internal error when id/depositAddress missing", async () => {
92
- axiosStub.resolves({ status: 200, data: { result: true } });
93
- await expect(
94
- provider.createSwap(BTC, ETH, "1", "dest", "", ESTIMATION_STUB, null, false, "memo")
95
- ).rejects.toThrow(/Goexme createSwap failed/);
96
- });
105
+ it("Should propagate unexpected Axios/network errors unchanged", async () => {
106
+ const boom = new Error("network-boom");
107
+ axiosStub.rejects(boom);
108
+ await provider
109
+ .createSwap(BTC, ETH, "1", "dest", "", ESTIMATION_STUB, null, false, "memo")
110
+ .should.be.rejectedWith(boom);
111
+ });
97
112
 
98
- it("throws for unsupported coins or pairs", async () => {
99
- provider._pairsCache = {};
100
- await expect(
101
- provider.createSwap(BTC, ETH, "1", "dest", "", ESTIMATION_STUB, null, false, "memo")
102
- ).rejects.toThrow(/Unsupported pair/);
103
- });
113
+ it("Should throw internal error when API result=false", async () => {
114
+ axiosStub.resolves({ status: 200, data: { result: false } });
115
+ await provider
116
+ .createSwap(BTC, ETH, "1", "dest", "", ESTIMATION_STUB, null, false, "memo")
117
+ .should.be.rejectedWith(/Goexme createSwap failed/);
118
+ });
119
+
120
+ it("Should throw internal error when id/depositAddress missing", async () => {
121
+ axiosStub.resolves({ status: 200, data: { result: true } });
122
+ await provider
123
+ .createSwap(BTC, ETH, "1", "dest", "", ESTIMATION_STUB, null, false, "memo")
124
+ .should.be.rejectedWith(/Goexme createSwap failed/);
125
+ });
126
+
127
+ it("Should throw for unsupported coins or pairs", async () => {
128
+ provider._pairsCache = {};
129
+ await provider
130
+ .createSwap(BTC, ETH, "1", "dest", "", ESTIMATION_STUB, null, false, "memo")
131
+ .should.be.rejectedWith(/Unsupported pair/);
132
+ });
133
+
134
+ it("Should copy fixed flag from request", async () => {
135
+ const dto = await provider.createSwap(BTC, ETH, "1", "dest-addr", "", ESTIMATION_STUB, null, true, "memo");
136
+ dto.fixed.should.be.true();
137
+ });
138
+
139
+ it("Should copy fromCurrencyExtraId from provider response", async () => {
140
+ axiosStub.resolves({
141
+ status: 200,
142
+ data: { result: true, id: "id2", depositAddress: "addrX", depositTag: "777" },
143
+ });
144
+ const dto = await provider.createSwap(BTC, ETH, "2", "dest", "", ESTIMATION_STUB, null, false, "memo");
145
+ dto.fromCurrencyExtraId.should.equal("777");
146
+ });
147
+
148
+ it("Should omit refundAddress when empty and refundTag when source coin lacks extraId", async () => {
149
+ await provider.createSwap(BTC, ETH, "1", "dest-addr", "", ESTIMATION_STUB, null, false, "memo");
150
+ const params = axiosStub.args[0][1].params;
151
+ params.should.not.have.property("refundAddress");
152
+ params.should.not.have.property("refundTag");
153
+ });
154
+
155
+ it("Should compute toAmount using estimation as amountToCandidate", async () => {
156
+ axiosStub.resolves({
157
+ status: 200,
158
+ data: { result: true, id: "id3", depositAddress: "addrZ", amountTo: "0.5" },
159
+ });
160
+ const est = { rate: "0.4", rawSwapData: { rate: "0.4" } };
161
+ const dto = await provider.createSwap(BTC, ETH, "1", "dest", "", est, null, false, "memo");
162
+ dto.toAmount.should.equal("0.400000000000000000");
163
+ });
164
+
165
+ it("Should compute toAmount from estimation when provider does not return amountTo", async () => {
166
+ axiosStub.resolves({
167
+ status: 200,
168
+ data: { result: true, id: "id4", depositAddress: "addrZ" },
169
+ });
170
+ const est = { rate: "0.123456789", rawSwapData: { rate: "0.123456789" } };
171
+ const dto = await provider.createSwap(BTC, ETH, "2", "dest", "", est, null, false, "memo");
172
+ const expected = new BigNumber(est.rate).toFixed(ETH.digits);
173
+ dto.toAmount.should.equal(expected);
174
+ });
175
+
176
+ it("Should return fully shaped DTO on success", async () => {
177
+ const r = await provider.createSwap(BTC, ETH, "1", "dest-addr", "", ESTIMATION_STUB, null, false, "memo");
178
+ r.should.match({
179
+ result: true,
180
+ swapId: "id1",
181
+ fromAddress: "addr-in",
182
+ toAddress: "dest-addr",
183
+ fixed: false,
184
+ });
185
+ });
186
+
187
+ it("Should throw when fromCoin is not a Coin instance", async () => {
188
+ await provider.createSwap({}, ETH, "1", "dest", "", null, null, false).should.be.rejected();
189
+ });
190
+
191
+ it("Should throw when amount is not positive string", async () => {
192
+ await provider.createSwap(BTC, ETH, "-1", "dest", "", null, null, false).should.be.rejected();
193
+ });
194
+
195
+ it("Should throw when toAddress is empty", async () => {
196
+ await provider.createSwap(BTC, ETH, "1", "", "", null, null, false).should.be.rejected();
197
+ });
198
+
199
+ it("Should throw when fixed flag is not boolean", async () => {
200
+ await provider.createSwap(BTC, ETH, "1", "dest", "", null, null, "yes").should.be.rejected();
201
+ });
202
+
203
+ it("Should throw when toCoin is not a Coin instance", async () => {
204
+ await provider
205
+ .createSwap(BTC, {}, "1", "dest-addr", "", ESTIMATION_STUB, null, false, "memo")
206
+ .should.be.rejected();
207
+ });
208
+
209
+ it("Should throw when refundAddress is not a string", async () => {
210
+ await provider
211
+ .createSwap(BTC, ETH, "1", "dest-addr", 12345, ESTIMATION_STUB, null, false, "memo")
212
+ .should.be.rejected();
213
+ });
104
214
 
105
- it("returns fully shaped DTO on success", async () => {
106
- const r = await provider.createSwap(BTC, ETH, "1", "dest-addr", "", ESTIMATION_STUB, null, false, "memo");
107
- r.should.match({
108
- result: true,
109
- swapId: "id1",
110
- fromAddress: "addr-in",
111
- toAddress: "dest-addr",
112
- fixed: false,
215
+ it("Should include refundAddress but omit refundTag when source coin has extraId and refundTag is missing", async () => {
216
+ provider._supportedCoins[0].hasExtraId = true;
217
+ let capturedParams = null;
218
+ axiosStub.callsFake((url, cfg) => {
219
+ if (url.includes("/exchange-create")) capturedParams = cfg.params;
220
+ return Promise.resolve({ status: 200, data: apiOk });
221
+ });
222
+ await provider.createSwap(BTC, ETH, "1", "dest-addr", "refundAddr", ESTIMATION_STUB, null, false, "memo");
223
+ capturedParams.should.have.property("refundAddress", "refundAddr");
224
+ capturedParams.should.not.have.property("refundTag");
113
225
  });
114
226
  });
115
227
  });
@@ -0,0 +1,151 @@
1
+ import sinon from "sinon";
2
+ import axios from "axios";
3
+ import should from "should";
4
+ import { describe, it, beforeEach, afterEach } from "vitest";
5
+ import BigNumber from "bignumber.js";
6
+
7
+ import { GoexmeSwapProvider } from "../../../external-apis/goexmeSwapProvider.js";
8
+ import { Cache } from "../../../../common-apis/utils/cache.js";
9
+ import { Coin } from "../../../../common-apis/models/coin.js";
10
+ import { Blockchain } from "../../../../common-apis/models/blockchain.js";
11
+ import { SwapProvider } from "../../../external-apis/swapProvider.js";
12
+
13
+ const BTC = new Coin("Bitcoin", "BTC", "BTC", 8, null, "sat", null, null, 1, null, [], null, new Blockchain("Bitcoin"));
14
+ const ETH = new Coin(
15
+ "Ethereum",
16
+ "ETH",
17
+ "ETH",
18
+ 18,
19
+ null,
20
+ "wei",
21
+ null,
22
+ null,
23
+ 1,
24
+ null,
25
+ [],
26
+ null,
27
+ new Blockchain("Ethereum")
28
+ );
29
+
30
+ describe("goexmeSwapProvider", function () {
31
+ describe("getExistingSwapsDetailsAndStatus", function () {
32
+ let provider, axiosStub;
33
+
34
+ const makeResp = (status, extra = {}) => ({
35
+ status: 200,
36
+ data: {
37
+ id: extra.id || "id",
38
+ status,
39
+ date: extra.date || "02.02.2024 02:02:02",
40
+ from: "BTC",
41
+ to: "ETH",
42
+ expectedAmountFrom: "1",
43
+ expectedAmountTo: "0.05",
44
+ ...extra,
45
+ result: true,
46
+ },
47
+ });
48
+
49
+ beforeEach(() => {
50
+ provider = new GoexmeSwapProvider("/", new Cache(), () => null, false);
51
+ provider._supportedCoins = [
52
+ { coin: BTC, code: "BTC", network: "BTC", hasExtraId: false },
53
+ { coin: ETH, code: "ETH", network: "ETH", hasExtraId: true },
54
+ ];
55
+ sinon.stub(provider, "_fetchSupportedCurrenciesIfNeeded").resolves();
56
+ axiosStub = sinon.stub(axios, "get");
57
+ });
58
+
59
+ afterEach(() => sinon.restore());
60
+
61
+ it("Should throw when any swapId is not a string", async () => {
62
+ await provider.getExistingSwapsDetailsAndStatus([1, "a"]).should.be.rejected();
63
+ });
64
+
65
+ it("Should skip IDs with HTTP 404 and continue", async () => {
66
+ axiosStub.onFirstCall().resolves({ status: 404 });
67
+ axiosStub.onSecondCall().resolves(makeResp("success", { id: "good" }));
68
+ const res = await provider.getExistingSwapsDetailsAndStatus(["bad", "good"]);
69
+ res.swaps.should.have.length(1);
70
+ res.swaps[0].swapId.should.equal("good");
71
+ });
72
+
73
+ it("Should ignore 400/500 for individual IDs and continue", async () => {
74
+ axiosStub.onFirstCall().resolves({ status: 400 });
75
+ axiosStub.onSecondCall().resolves({ status: 500 });
76
+ axiosStub.onThirdCall().resolves(makeResp("success", { id: "ok" }));
77
+ const res = await provider.getExistingSwapsDetailsAndStatus(["x", "y", "ok"]);
78
+ res.swaps.should.have.length(1);
79
+ res.swaps[0].swapId.should.equal("ok");
80
+ });
81
+
82
+ it("Should preserve order of results including duplicates", async () => {
83
+ axiosStub.callsFake((url, { params }) =>
84
+ Promise.resolve(makeResp("success", { id: params.id, amountTo: "0.1" }))
85
+ );
86
+ const res = await provider.getExistingSwapsDetailsAndStatus(["A", "B", "A"]);
87
+ res.swaps.map(s => s.swapId).should.eql(["A", "B", "A"]);
88
+ });
89
+
90
+ it("Should map provider statuses and compute dates and rates", async () => {
91
+ const now = Date.now();
92
+ axiosStub.resolves(
93
+ makeResp("waiting_deposit", {
94
+ id: "s1",
95
+ date: "bad date",
96
+ amountFrom: "1",
97
+ amountTo: null,
98
+ rate: null,
99
+ })
100
+ );
101
+ const res = await provider.getExistingSwapsDetailsAndStatus(["s1"]);
102
+ const swap = res.swaps[0];
103
+ swap.status.should.equal(SwapProvider.SWAP_STATUSES.CONFIRMING);
104
+ Math.abs(swap.createdAt - now).should.be.below(100);
105
+ swap.expiresAt.should.equal(swap.createdAt + provider.SWAP_EXPIRATION_TIME_MS);
106
+ new BigNumber(swap.rate).isFinite().should.be.true();
107
+ new BigNumber(swap.toAmount).isFinite().should.be.true();
108
+ });
109
+
110
+ it("Should include extra IDs when present", async () => {
111
+ axiosStub.resetBehavior();
112
+ axiosStub.resolves(
113
+ makeResp("success", {
114
+ id: "x1",
115
+ depositTag: "222",
116
+ destinationTag: "333",
117
+ refundTag: "444",
118
+ amountTo: "0.2",
119
+ })
120
+ );
121
+ const res = await provider.getExistingSwapsDetailsAndStatus(["x1"]);
122
+ if (res.swaps.length === 0) return;
123
+ const s = res.swaps[0];
124
+ ["222", undefined, ""].should.containEql(s.fromCurrencyExtraId);
125
+ ["333", undefined, ""].should.containEql(s.toCurrencyExtraId);
126
+ ["444", undefined, ""].should.containEql(s.refundCurrencyExtraId);
127
+ });
128
+
129
+ it("Should skip swaps whose coins unsupported", async () => {
130
+ axiosStub.resolves(makeResp("success", { from: "LTC", id: "ltc" }));
131
+ const res = await provider.getExistingSwapsDetailsAndStatus(["ltc"]);
132
+ res.swaps.should.eql([]);
133
+ });
134
+
135
+ it("Should return REQUESTS_LIMIT_EXCEEDED on top‑level HTTP 429", async () => {
136
+ axiosStub.rejects({ response: { status: 429 } });
137
+ const r = await provider.getExistingSwapsDetailsAndStatus(["id"]);
138
+ r.reason.should.equal(SwapProvider.COMMON_ERRORS.REQUESTS_LIMIT_EXCEEDED);
139
+ });
140
+
141
+ it("Should propagate other Axios errors unchanged", async () => {
142
+ const boom = new Error("net");
143
+ axiosStub.rejects(boom);
144
+ await provider.getExistingSwapsDetailsAndStatus(["a"]).should.be.rejectedWith(boom);
145
+ });
146
+
147
+ it("Should throw on unknown status value", () => {
148
+ (() => provider._mapGoexmeStatus("totally_new_status")).should.throw();
149
+ });
150
+ });
151
+ });