@rabbitio/ui-kit 1.0.0-beta.41 → 1.0.0-beta.43
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.
- package/.gitlab-ci.yml +29 -0
- package/.husky/commit-msg +8 -0
- package/.husky/pre-push +1 -0
- package/README.md +13 -4
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/clover.xml +6516 -0
- package/coverage/coverage-final.json +43 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +416 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/rabbit-ui-kit/index.html +116 -0
- package/coverage/rabbit-ui-kit/index.js.html +88 -0
- package/coverage/rabbit-ui-kit/src/common/adapters/axiosAdapter.js.html +190 -0
- package/coverage/rabbit-ui-kit/src/common/adapters/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/common/amountUtils.js.html +1393 -0
- package/coverage/rabbit-ui-kit/src/common/errorUtils.js.html +211 -0
- package/coverage/rabbit-ui-kit/src/common/external-apis/apiGroups.js.html +250 -0
- package/coverage/rabbit-ui-kit/src/common/external-apis/index.html +131 -0
- package/coverage/rabbit-ui-kit/src/common/external-apis/ipAddressProviders.js.html +499 -0
- package/coverage/rabbit-ui-kit/src/common/fiatCurrenciesService.js.html +568 -0
- package/coverage/rabbit-ui-kit/src/common/index.html +146 -0
- package/coverage/rabbit-ui-kit/src/common/models/blockchain.js.html +115 -0
- package/coverage/rabbit-ui-kit/src/common/models/coin.js.html +556 -0
- package/coverage/rabbit-ui-kit/src/common/models/index.html +146 -0
- package/coverage/rabbit-ui-kit/src/common/models/protocol.js.html +100 -0
- package/coverage/rabbit-ui-kit/src/common/utils/cache.js.html +889 -0
- package/coverage/rabbit-ui-kit/src/common/utils/emailAPI.js.html +139 -0
- package/coverage/rabbit-ui-kit/src/common/utils/index.html +161 -0
- package/coverage/rabbit-ui-kit/src/common/utils/logging/index.html +131 -0
- package/coverage/rabbit-ui-kit/src/common/utils/logging/logger.js.html +229 -0
- package/coverage/rabbit-ui-kit/src/common/utils/logging/logsStorage.js.html +268 -0
- package/coverage/rabbit-ui-kit/src/common/utils/postponeExecution.js.html +118 -0
- package/coverage/rabbit-ui-kit/src/common/utils/safeStringify.js.html +235 -0
- package/coverage/rabbit-ui-kit/src/components/atoms/AssetIcon/AssetIcon.jsx.html +250 -0
- package/coverage/rabbit-ui-kit/src/components/atoms/AssetIcon/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/components/atoms/LoadingDots/LoadingDots.jsx.html +256 -0
- package/coverage/rabbit-ui-kit/src/components/atoms/LoadingDots/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/components/atoms/SupportChat/SupportChat.jsx.html +229 -0
- package/coverage/rabbit-ui-kit/src/components/atoms/SupportChat/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/components/atoms/buttons/Button/Button.jsx.html +802 -0
- package/coverage/rabbit-ui-kit/src/components/atoms/buttons/Button/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/components/hooks/index.html +131 -0
- package/coverage/rabbit-ui-kit/src/components/hooks/useCallHandlingErrors.js.html +163 -0
- package/coverage/rabbit-ui-kit/src/components/hooks/useReferredState.js.html +157 -0
- package/coverage/rabbit-ui-kit/src/components/utils/index.html +146 -0
- package/coverage/rabbit-ui-kit/src/components/utils/inputValueProviders.js.html +259 -0
- package/coverage/rabbit-ui-kit/src/components/utils/uiUtils.js.html +127 -0
- package/coverage/rabbit-ui-kit/src/components/utils/urlQueryUtils.js.html +346 -0
- package/coverage/rabbit-ui-kit/src/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/index.js.html +250 -0
- package/coverage/rabbit-ui-kit/src/robustExteranlApiCallerService/cacheAndConcurrentRequestsResolver.js.html +1762 -0
- package/coverage/rabbit-ui-kit/src/robustExteranlApiCallerService/cachedRobustExternalApiCallerService.js.html +649 -0
- package/coverage/rabbit-ui-kit/src/robustExteranlApiCallerService/cancelProcessing.js.html +172 -0
- package/coverage/rabbit-ui-kit/src/robustExteranlApiCallerService/concurrentCalculationsMetadataHolder.js.html +394 -0
- package/coverage/rabbit-ui-kit/src/robustExteranlApiCallerService/externalApiProvider.js.html +553 -0
- package/coverage/rabbit-ui-kit/src/robustExteranlApiCallerService/externalServicesStatsCollector.js.html +331 -0
- package/coverage/rabbit-ui-kit/src/robustExteranlApiCallerService/index.html +206 -0
- package/coverage/rabbit-ui-kit/src/robustExteranlApiCallerService/robustExternalAPICallerService.js.html +1249 -0
- package/coverage/rabbit-ui-kit/src/swaps-lib/external-apis/index.html +131 -0
- package/coverage/rabbit-ui-kit/src/swaps-lib/external-apis/swapProvider.js.html +727 -0
- package/coverage/rabbit-ui-kit/src/swaps-lib/external-apis/swapspaceSwapProvider.js.html +2899 -0
- package/coverage/rabbit-ui-kit/src/swaps-lib/models/baseSwapCreationInfo.js.html +214 -0
- package/coverage/rabbit-ui-kit/src/swaps-lib/models/existingSwap.js.html +304 -0
- package/coverage/rabbit-ui-kit/src/swaps-lib/models/existingSwapWithFiatData.js.html +487 -0
- package/coverage/rabbit-ui-kit/src/swaps-lib/models/index.html +146 -0
- package/coverage/rabbit-ui-kit/src/swaps-lib/services/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/swaps-lib/services/publicSwapService.js.html +2191 -0
- package/coverage/rabbit-ui-kit/src/swaps-lib/utils/index.html +116 -0
- package/coverage/rabbit-ui-kit/src/swaps-lib/utils/swapUtils.js.html +742 -0
- package/coverage/rabbit-ui-kit/stories/atoms/LoadingDots.stories.jsx.html +226 -0
- package/coverage/rabbit-ui-kit/stories/atoms/buttons/Button.stories.jsx.html +946 -0
- package/coverage/rabbit-ui-kit/stories/atoms/buttons/index.html +116 -0
- package/coverage/rabbit-ui-kit/stories/atoms/index.html +116 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +196 -0
- package/dist/index.cjs +1706 -1498
- package/dist/index.cjs.map +1 -1
- package/dist/index.modern.js +817 -666
- package/dist/index.modern.js.map +1 -1
- package/dist/index.module.js +1704 -1498
- package/dist/index.module.js.map +1 -1
- package/dist/index.umd.js +1664 -1456
- package/dist/index.umd.js.map +1 -1
- package/package.json +11 -3
- package/src/common/amountUtils.js +4 -2
- package/src/common/external-apis/ipAddressProviders.js +138 -0
- package/src/common/tests/integration/external-apis/ipAddressProviders/getClientIpAddress.test.js +14 -0
- package/src/common/utils/cache.js +4 -4
- package/src/components/tests/utils/inputValueProviders/provideFormatOfFloatValueByInputString.test.js +139 -0
- package/src/components/tests/utils/urlQueryUtils/getQueryParameterValues.test.js +71 -0
- package/src/components/tests/utils/urlQueryUtils/saveQueryParameterAndValues.test.js +144 -0
- package/src/components/utils/inputValueProviders.js +58 -0
- package/src/index.js +2 -0
- package/src/robustExteranlApiCallerService/robustExternalAPICallerService.js +4 -2
- package/src/robustExteranlApiCallerService/tests/robustExternalAPICallerService/robustExternalAPICallerService/callExternalAPI/_performCallAttempt.test.js +787 -0
- package/src/robustExteranlApiCallerService/tests/robustExternalAPICallerService/robustExternalAPICallerService/callExternalAPI/callExternalAPI.test.js +745 -0
- package/src/robustExteranlApiCallerService/tests/robustExternalAPICallerService/robustExternalAPICallerService/constructor.test.js +31 -0
- package/src/swaps-lib/external-apis/swapProvider.js +17 -4
- package/src/swaps-lib/external-apis/swapspaceSwapProvider.js +91 -30
- package/src/swaps-lib/models/baseSwapCreationInfo.js +4 -1
- package/src/swaps-lib/models/existingSwap.js +3 -0
- package/src/swaps-lib/models/existingSwapWithFiatData.js +4 -0
- package/src/swaps-lib/services/publicSwapService.js +32 -4
- package/src/swaps-lib/test/external-apis/swapspaceSwapProvider/_fetchSupportedCurrenciesIfNeeded.test.js +506 -0
- package/src/swaps-lib/test/external-apis/swapspaceSwapProvider/createSwap.test.js +1311 -0
- package/src/swaps-lib/test/external-apis/swapspaceSwapProvider/getAllSupportedCurrencies.test.js +76 -0
- package/src/swaps-lib/test/external-apis/swapspaceSwapProvider/getDepositCurrencies.test.js +82 -0
- package/src/swaps-lib/test/external-apis/swapspaceSwapProvider/getSwapInfo.test.js +1892 -0
- package/src/swaps-lib/test/external-apis/swapspaceSwapProvider/getWithdrawalCurrencies.test.js +111 -0
- package/src/swaps-lib/test/utils/swapUtils/safeHandleRequestsLimitExceeding.test.js +88 -0
|
@@ -0,0 +1,787 @@
|
|
|
1
|
+
import sinon from "sinon";
|
|
2
|
+
import should from "should";
|
|
3
|
+
|
|
4
|
+
import { beforeEach, afterEach, describe, it } from "vitest";
|
|
5
|
+
import { v4 } from "uuid";
|
|
6
|
+
import { ExternalApiProvider } from "../../../../externalApiProvider.js";
|
|
7
|
+
import { ApiGroup } from "../../../../../common/external-apis/apiGroups.js";
|
|
8
|
+
import { AxiosAdapter } from "../../../../../common/adapters/axiosAdapter.js";
|
|
9
|
+
import { RobustExternalAPICallerService } from "../../../../robustExternalAPICallerService.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* _performCallAttempt what aspects to test:
|
|
13
|
+
* + 1. Should reorder providers according to nice factor
|
|
14
|
+
* + 2. Should not touch the instance's providers list during the reordering
|
|
15
|
+
* + 3. Should call only first provider if it returns data
|
|
16
|
+
* + 4. Should call several providers until the data retrieved
|
|
17
|
+
* + 5. Should return null data if all providers returns null
|
|
18
|
+
* + 6.1 Should return shouldBeForceRetried=true if all providers declined by RPS (isRPSExceeded returns true)
|
|
19
|
+
* + 6.2 Should return shouldBeForceRetried=true if some providers fail and the rest ones declined by RPS (isRPSExceeded returns true) and count of requests declined by RPS is more than ceil of half of providers count
|
|
20
|
+
* + 7.1. Should return rpsFactor multiplied by rpsMultiplier if all providers declined by RPS (isRPSExceeded returns true)
|
|
21
|
+
* + 7.2. Should return rpsFactor multiplied by rpsMultiplier if some providers fail and the rest ones declined by RPS (isRPSExceeded returns true) and count of requests declined by RPS is more than ceil of half of providers count
|
|
22
|
+
* + 8. Should return the errors list caught when calling the providers and one of them fails
|
|
23
|
+
* + 9. Should return the errors list caught when calling the providers and all them fail
|
|
24
|
+
* + 9.1 Should return null data when all the providers fail
|
|
25
|
+
* + 10. Should not call the provider if it has RPS and rpsEnsurer says that this domain's RPS is exceeded
|
|
26
|
+
* + 11. Should correctly count the number of requests declined by RPS exceeding
|
|
27
|
+
* + 12. Should add cancelToken to axios params if it is not null
|
|
28
|
+
* + 13. Should add timeout to axios params if a provider has it
|
|
29
|
+
* + 14. Should do all sub-request if all of them except one don't fail
|
|
30
|
+
* + 15. Should call proper sub-request's query composer if there are composers for each sub-request
|
|
31
|
+
* + 16. Should pass correct endpoint with query returned by query composer as the first axios param
|
|
32
|
+
* 17. Should call composeBody if the http method is post/put/patch
|
|
33
|
+
* 18. Should pass body as the second axios parameter if httpMethod is post/put/patch and it is present
|
|
34
|
+
* 18. Should pass null as the second axios parameter if httpMethod is post/put/patch and the provider has no bodyComposer
|
|
35
|
+
* 19. Should make requests for each page if requests for previous pages succeed
|
|
36
|
+
* 20. Should make requests for all pages till not failing
|
|
37
|
+
* 21. Should actualize last called timestamp by calling rpsEnsuring method
|
|
38
|
+
* 22. Should pass axios config as the third parameter if http method is post/put/patch
|
|
39
|
+
* 23. Should pass axios config as the second parameter if the http method is not post/put/patch
|
|
40
|
+
* 24. Should notify stats collector that the service is called without an error
|
|
41
|
+
* 25. Should call query parameters changer for second and more pages
|
|
42
|
+
* 26. Should call dedicated query parameters changer for second and more pages if the request has sub-requests
|
|
43
|
+
* 27. Should pass proper params to query parameters changer for second and more page number
|
|
44
|
+
* 28. Should delay the request for second and more page number for the time corresponding to the RPS and rpsFactor if the provider has RPS
|
|
45
|
+
* 29. Should use corrected query string for second and more page as a first parameter for axios call
|
|
46
|
+
* 30. Should call axios with the http method of current provider
|
|
47
|
+
* 31. Should call axios with the http method of current provider's current sub-request
|
|
48
|
+
* 32. Should actualize the last called timestamp in rpsEnsurer when postponing the request for second and more page number
|
|
49
|
+
* 33. Should call checkWhetherResponseIsForLastPage if provider has this function
|
|
50
|
+
* 34. Should call checkWhetherResponseIsForLastPage with the correct params
|
|
51
|
+
* 35. Should call checkWhetherResponseIsForLastPage corresponding to sub-request if provider has sub-requests
|
|
52
|
+
* 36. Should return the paginated data as a single array if the request per page returns an array
|
|
53
|
+
* 37. Should return the joined sub-requests data as a single array if the request per sub-request returns an array
|
|
54
|
+
* 38. Should return the joined paginated sub-requests data as a single array if the request per sub-request returns an array and some sub-requests return an array per page
|
|
55
|
+
* 39. Should return the result of the request if the request has no sub-requests
|
|
56
|
+
* 40. Should punish the provider failed to retrieve data
|
|
57
|
+
* 41. Should punish the provider returned no data
|
|
58
|
+
* 42. Should call stats collector when the provider fails to retrieve data
|
|
59
|
+
* 43. Should call stats collector when the provider returns no data
|
|
60
|
+
* + 44. Should bind each query composer to provider
|
|
61
|
+
*/
|
|
62
|
+
describe("RobustExternalAPICallerService", function () {
|
|
63
|
+
describe("#callExternalAPI>_performCallAttempt", function () {
|
|
64
|
+
const anyAxiosResponse = (method) => `${method}-result`;
|
|
65
|
+
|
|
66
|
+
const generateProviders = (
|
|
67
|
+
count,
|
|
68
|
+
endpoints = "any",
|
|
69
|
+
httpMethods = "any",
|
|
70
|
+
getDataByResponseFunctions = "any",
|
|
71
|
+
RPSes = null,
|
|
72
|
+
timeouts = null
|
|
73
|
+
) => {
|
|
74
|
+
const providers = [];
|
|
75
|
+
const composeQueryStringStubs = [];
|
|
76
|
+
for (let i = 0; i < count; ++i) {
|
|
77
|
+
class TempProvider extends ExternalApiProvider {
|
|
78
|
+
constructor() {
|
|
79
|
+
super(
|
|
80
|
+
endpoints === "any" ? "any" : endpoints[i],
|
|
81
|
+
httpMethods === "any" ? "get" : httpMethods[i],
|
|
82
|
+
timeouts ? timeouts[i] : null,
|
|
83
|
+
RPSes === null
|
|
84
|
+
? new ApiGroup(v4(), 10)
|
|
85
|
+
: new ApiGroup(v4(), RPSes[i])
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
getDataByResponse(
|
|
89
|
+
response,
|
|
90
|
+
params = [],
|
|
91
|
+
subRequestIndex = 0,
|
|
92
|
+
iterationsData = []
|
|
93
|
+
) {
|
|
94
|
+
const func =
|
|
95
|
+
getDataByResponseFunctions === "any"
|
|
96
|
+
? "any"
|
|
97
|
+
: Array.isArray(getDataByResponseFunctions[i])
|
|
98
|
+
? getDataByResponseFunctions[i][
|
|
99
|
+
subRequestIndex
|
|
100
|
+
]
|
|
101
|
+
: getDataByResponseFunctions[i];
|
|
102
|
+
return func();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const provider = new TempProvider();
|
|
106
|
+
providers.push(provider);
|
|
107
|
+
const composeQueryStringStub = sinon.stub(
|
|
108
|
+
provider,
|
|
109
|
+
"composeQueryString"
|
|
110
|
+
);
|
|
111
|
+
composeQueryStringStubs.push(composeQueryStringStub);
|
|
112
|
+
}
|
|
113
|
+
const isRpsExceededStubs = [];
|
|
114
|
+
providers.forEach((provider) => {
|
|
115
|
+
if (
|
|
116
|
+
provider.httpMethod === "post" ||
|
|
117
|
+
provider.httpMethod === "put" ||
|
|
118
|
+
provider.httpMethod === "patch"
|
|
119
|
+
) {
|
|
120
|
+
provider.composeBody = () => {};
|
|
121
|
+
sinon.stub(provider, "composeBody");
|
|
122
|
+
}
|
|
123
|
+
const isRPSExceededStub = sinon.stub(provider, "isRpsExceeded");
|
|
124
|
+
isRPSExceededStub.returns(false);
|
|
125
|
+
isRpsExceededStubs.push(isRPSExceededStub);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
return { providers, isRpsExceededStubs, composeQueryStringStubs };
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const anyRPSes = (count) => new Array(count).fill(10);
|
|
132
|
+
|
|
133
|
+
let anyParameters = [];
|
|
134
|
+
let anyTimeout = 10000;
|
|
135
|
+
let anyCancelToken = "null";
|
|
136
|
+
let anyRPSFactor = 1;
|
|
137
|
+
let callStub;
|
|
138
|
+
beforeEach(function () {
|
|
139
|
+
callStub = sinon.stub(AxiosAdapter, "call");
|
|
140
|
+
callStub
|
|
141
|
+
.withArgs(
|
|
142
|
+
"get",
|
|
143
|
+
sinon.match.any,
|
|
144
|
+
sinon.match.any,
|
|
145
|
+
sinon.match.any
|
|
146
|
+
)
|
|
147
|
+
.resolves(anyAxiosResponse("get"));
|
|
148
|
+
callStub
|
|
149
|
+
.withArgs(
|
|
150
|
+
"post",
|
|
151
|
+
sinon.match.any,
|
|
152
|
+
sinon.match.any,
|
|
153
|
+
sinon.match.any
|
|
154
|
+
)
|
|
155
|
+
.resolves(anyAxiosResponse("post"));
|
|
156
|
+
callStub
|
|
157
|
+
.withArgs(
|
|
158
|
+
"patch",
|
|
159
|
+
sinon.match.any,
|
|
160
|
+
sinon.match.any,
|
|
161
|
+
sinon.match.any
|
|
162
|
+
)
|
|
163
|
+
.resolves(anyAxiosResponse("patch"));
|
|
164
|
+
callStub
|
|
165
|
+
.withArgs(
|
|
166
|
+
"delete",
|
|
167
|
+
sinon.match.any,
|
|
168
|
+
sinon.match.any,
|
|
169
|
+
sinon.match.any
|
|
170
|
+
)
|
|
171
|
+
.resolves(anyAxiosResponse("delete"));
|
|
172
|
+
callStub
|
|
173
|
+
.withArgs(
|
|
174
|
+
"put",
|
|
175
|
+
sinon.match.any,
|
|
176
|
+
sinon.match.any,
|
|
177
|
+
sinon.match.any
|
|
178
|
+
)
|
|
179
|
+
.resolves(anyAxiosResponse("put"));
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
afterEach(function () {
|
|
183
|
+
callStub.restore();
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it("Should reorder providers according to nice factor", async function () {
|
|
187
|
+
const { providers } = generateProviders(
|
|
188
|
+
3,
|
|
189
|
+
["###1", "###2", "###3"],
|
|
190
|
+
["get", "get", "get"],
|
|
191
|
+
[() => "any", () => null, () => null]
|
|
192
|
+
);
|
|
193
|
+
const retriever = new RobustExternalAPICallerService(
|
|
194
|
+
"any",
|
|
195
|
+
providers
|
|
196
|
+
);
|
|
197
|
+
[1, 5, 3].forEach(
|
|
198
|
+
(niceFactor, i) =>
|
|
199
|
+
(retriever.providers[i].niceFactor = niceFactor)
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
await retriever._performCallAttempt(
|
|
203
|
+
anyParameters,
|
|
204
|
+
anyTimeout,
|
|
205
|
+
anyCancelToken,
|
|
206
|
+
anyRPSFactor
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
callStub.args[0][0].should.be.equal("get");
|
|
210
|
+
callStub.args[0][1].should.startWith(
|
|
211
|
+
retriever.providers[1].endpoint
|
|
212
|
+
);
|
|
213
|
+
callStub.args[1][1].should.startWith(
|
|
214
|
+
retriever.providers[2].endpoint
|
|
215
|
+
);
|
|
216
|
+
callStub.args[2][1].should.startWith(
|
|
217
|
+
retriever.providers[0].endpoint
|
|
218
|
+
);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it("Should not touch the instance's providers list during the reordering", async function () {
|
|
222
|
+
const { providers } = generateProviders(
|
|
223
|
+
3,
|
|
224
|
+
["###1", "###2", "###3"],
|
|
225
|
+
["get", "get", "get"],
|
|
226
|
+
[() => "any", () => null, () => null]
|
|
227
|
+
);
|
|
228
|
+
const retriever = new RobustExternalAPICallerService(
|
|
229
|
+
"any",
|
|
230
|
+
providers
|
|
231
|
+
);
|
|
232
|
+
[1, 5, 3].forEach(
|
|
233
|
+
(niceFactor, i) =>
|
|
234
|
+
(retriever.providers[i].niceFactor = niceFactor)
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
await retriever._performCallAttempt(
|
|
238
|
+
anyParameters,
|
|
239
|
+
anyTimeout,
|
|
240
|
+
anyCancelToken,
|
|
241
|
+
anyRPSFactor
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
retriever.providers[0].endpoint.should.be.equal(
|
|
245
|
+
providers[0].endpoint
|
|
246
|
+
);
|
|
247
|
+
retriever.providers[1].endpoint.should.be.equal(
|
|
248
|
+
providers[1].endpoint
|
|
249
|
+
);
|
|
250
|
+
retriever.providers[2].endpoint.should.be.equal(
|
|
251
|
+
providers[2].endpoint
|
|
252
|
+
);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it("Should call only first provider if it returns data", async function () {
|
|
256
|
+
const { providers } = generateProviders(
|
|
257
|
+
3,
|
|
258
|
+
["###1", "###2", "###3"],
|
|
259
|
+
["get", "get", "get"],
|
|
260
|
+
[() => "any", () => "any", () => "any"]
|
|
261
|
+
);
|
|
262
|
+
const retriever = new RobustExternalAPICallerService(
|
|
263
|
+
"any",
|
|
264
|
+
providers
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
await retriever._performCallAttempt(
|
|
268
|
+
anyParameters,
|
|
269
|
+
anyTimeout,
|
|
270
|
+
anyCancelToken,
|
|
271
|
+
anyRPSFactor
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
callStub.callCount.should.be.equal(1);
|
|
275
|
+
callStub.args[0][0].should.be.equal("get");
|
|
276
|
+
callStub.args[0][1].should.startWith(
|
|
277
|
+
retriever.providers[0].endpoint
|
|
278
|
+
);
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it("Should call several providers until the data retrieved", async function () {
|
|
282
|
+
const { providers } = generateProviders(
|
|
283
|
+
3,
|
|
284
|
+
["###1", "###2", "###3"],
|
|
285
|
+
["get", "get", "get"],
|
|
286
|
+
[() => null, () => null, () => "any"]
|
|
287
|
+
);
|
|
288
|
+
const retriever = new RobustExternalAPICallerService(
|
|
289
|
+
"any",
|
|
290
|
+
providers
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
await retriever._performCallAttempt(
|
|
294
|
+
anyParameters,
|
|
295
|
+
anyTimeout,
|
|
296
|
+
anyCancelToken,
|
|
297
|
+
anyRPSFactor
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
callStub.callCount.should.be.equal(3);
|
|
301
|
+
callStub.args[0][0].should.be.equal("get");
|
|
302
|
+
callStub.args[1][0].should.be.equal("get");
|
|
303
|
+
callStub.args[2][0].should.be.equal("get");
|
|
304
|
+
callStub.args[0][1].should.startWith(
|
|
305
|
+
retriever.providers[0].endpoint
|
|
306
|
+
);
|
|
307
|
+
callStub.args[1][1].should.startWith(
|
|
308
|
+
retriever.providers[1].endpoint
|
|
309
|
+
);
|
|
310
|
+
callStub.args[2][1].should.startWith(
|
|
311
|
+
retriever.providers[2].endpoint
|
|
312
|
+
);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
it("Should return null data if all providers returns null", async function () {
|
|
316
|
+
const { providers } = generateProviders(
|
|
317
|
+
3,
|
|
318
|
+
["###1", "###2", "###3"],
|
|
319
|
+
["get", "get", "get"],
|
|
320
|
+
[() => null, () => null, () => null]
|
|
321
|
+
);
|
|
322
|
+
const retriever = new RobustExternalAPICallerService(
|
|
323
|
+
"any",
|
|
324
|
+
providers
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
const result = await retriever._performCallAttempt(
|
|
328
|
+
anyParameters,
|
|
329
|
+
anyTimeout,
|
|
330
|
+
anyCancelToken,
|
|
331
|
+
anyRPSFactor
|
|
332
|
+
);
|
|
333
|
+
|
|
334
|
+
(result.data === null).should.be.true();
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
it("Should return shouldBeForceRetried=true if providers have RPS and all providers declined by RPS (isRPSExceeded returns true)", async function () {
|
|
338
|
+
const { providers, isRpsExceededStubs } = generateProviders(
|
|
339
|
+
3,
|
|
340
|
+
["1", "2", "3"],
|
|
341
|
+
"any",
|
|
342
|
+
"any",
|
|
343
|
+
anyRPSes(3)
|
|
344
|
+
);
|
|
345
|
+
const retriever = new RobustExternalAPICallerService(
|
|
346
|
+
"any",
|
|
347
|
+
providers
|
|
348
|
+
);
|
|
349
|
+
|
|
350
|
+
isRpsExceededStubs.forEach((stub) => {
|
|
351
|
+
stub.resetBehavior();
|
|
352
|
+
stub.returns(true);
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
const result = await retriever._performCallAttempt(
|
|
356
|
+
anyParameters,
|
|
357
|
+
anyTimeout,
|
|
358
|
+
anyCancelToken,
|
|
359
|
+
anyRPSFactor
|
|
360
|
+
);
|
|
361
|
+
|
|
362
|
+
result.shouldBeForceRetried.should.be.true();
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
it("Should return shouldBeForceRetried=true if some providers fail and the rest ones declined by RPS (isRPSExceeded returns true) and count of requests declined by RPS is more than ceil of half of providers count", async function () {
|
|
366
|
+
const { providers, isRpsExceededStubs } = generateProviders(
|
|
367
|
+
5,
|
|
368
|
+
["1", "2", "3", "4", "5"],
|
|
369
|
+
"any",
|
|
370
|
+
[() => null, () => "any", () => null, () => "any", () => "any"],
|
|
371
|
+
anyRPSes(5)
|
|
372
|
+
);
|
|
373
|
+
const retriever = new RobustExternalAPICallerService(
|
|
374
|
+
"any",
|
|
375
|
+
providers
|
|
376
|
+
);
|
|
377
|
+
|
|
378
|
+
isRpsExceededStubs.forEach((stub, index) => {
|
|
379
|
+
if (index === 1 || index === 3 || index === 4) {
|
|
380
|
+
stub.resetBehavior();
|
|
381
|
+
stub.returns(true);
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
const result = await retriever._performCallAttempt(
|
|
386
|
+
anyParameters,
|
|
387
|
+
anyTimeout,
|
|
388
|
+
anyCancelToken,
|
|
389
|
+
anyRPSFactor
|
|
390
|
+
);
|
|
391
|
+
|
|
392
|
+
result.shouldBeForceRetried.should.be.true();
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
it("Should return rpsFactor multiplied by rpsMultiplier if providers have RPS and all providers declined by RPS (isRPSExceeded returns true)", async function () {
|
|
396
|
+
const { providers, isRpsExceededStubs } = generateProviders(
|
|
397
|
+
3,
|
|
398
|
+
["1", "2", "3"],
|
|
399
|
+
"any",
|
|
400
|
+
"any",
|
|
401
|
+
anyRPSes(3)
|
|
402
|
+
);
|
|
403
|
+
const retriever = new RobustExternalAPICallerService(
|
|
404
|
+
"any",
|
|
405
|
+
providers
|
|
406
|
+
);
|
|
407
|
+
|
|
408
|
+
isRpsExceededStubs.forEach((stub) => {
|
|
409
|
+
stub.resetBehavior();
|
|
410
|
+
stub.returns(true);
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
const rpsFactor = 1;
|
|
414
|
+
|
|
415
|
+
const result = await retriever._performCallAttempt(
|
|
416
|
+
anyParameters,
|
|
417
|
+
anyTimeout,
|
|
418
|
+
anyCancelToken,
|
|
419
|
+
anyRPSFactor
|
|
420
|
+
);
|
|
421
|
+
|
|
422
|
+
result.rpsFactor.should.be.equal(
|
|
423
|
+
rpsFactor * RobustExternalAPICallerService.rpsMultiplier
|
|
424
|
+
);
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
it("Should return rpsFactor multiplied by rpsMultiplier if some providers fail and the rest ones declined by RPS (isRPSExceeded returns true) and count of requests declined by RPS is more than ceil of half of providers count", async function () {
|
|
428
|
+
const { providers, isRpsExceededStubs } = generateProviders(
|
|
429
|
+
5,
|
|
430
|
+
["1", "2", "3", "4", "5"],
|
|
431
|
+
"any",
|
|
432
|
+
["any", () => null, "any", () => null, () => null],
|
|
433
|
+
anyRPSes(5)
|
|
434
|
+
);
|
|
435
|
+
const retriever = new RobustExternalAPICallerService(
|
|
436
|
+
"any",
|
|
437
|
+
providers
|
|
438
|
+
);
|
|
439
|
+
|
|
440
|
+
isRpsExceededStubs.forEach((stub, index) => {
|
|
441
|
+
if (index === 1 || index === 3 || index === 4) {
|
|
442
|
+
stub.resetBehavior();
|
|
443
|
+
stub.returns(true);
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
const rpsFactor = 1;
|
|
448
|
+
|
|
449
|
+
const result = await retriever._performCallAttempt(
|
|
450
|
+
[],
|
|
451
|
+
anyTimeout,
|
|
452
|
+
null,
|
|
453
|
+
rpsFactor
|
|
454
|
+
);
|
|
455
|
+
|
|
456
|
+
result.rpsFactor.should.be.equal(
|
|
457
|
+
rpsFactor * RobustExternalAPICallerService.rpsMultiplier
|
|
458
|
+
);
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
it("Should return the errors list caught when calling the providers and one of them fails", async function () {
|
|
462
|
+
const { providers } = generateProviders(
|
|
463
|
+
3,
|
|
464
|
+
["1", "2", "3"],
|
|
465
|
+
["get", "get", "get"],
|
|
466
|
+
[() => null, () => "any", () => "any"]
|
|
467
|
+
);
|
|
468
|
+
const retriever = new RobustExternalAPICallerService(
|
|
469
|
+
"any",
|
|
470
|
+
providers
|
|
471
|
+
);
|
|
472
|
+
|
|
473
|
+
const someError = new Error("E1");
|
|
474
|
+
callStub.resetBehavior();
|
|
475
|
+
callStub.onCall(0).resolves("any");
|
|
476
|
+
callStub.onCall(1).rejects(someError);
|
|
477
|
+
callStub.onCall(2).resolves("any");
|
|
478
|
+
|
|
479
|
+
const result = await retriever._performCallAttempt(
|
|
480
|
+
anyParameters,
|
|
481
|
+
anyTimeout,
|
|
482
|
+
anyCancelToken,
|
|
483
|
+
anyRPSFactor
|
|
484
|
+
);
|
|
485
|
+
|
|
486
|
+
result.errors.should.be.deepEqual([someError]);
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
it("Should return the errors list caught when calling the providers and all of them fail", async function () {
|
|
490
|
+
const { providers } = generateProviders(
|
|
491
|
+
3,
|
|
492
|
+
["1", "2", "3"],
|
|
493
|
+
["get", "get", "get"],
|
|
494
|
+
[() => null, () => null, () => null]
|
|
495
|
+
);
|
|
496
|
+
const retriever = new RobustExternalAPICallerService(
|
|
497
|
+
"any",
|
|
498
|
+
providers
|
|
499
|
+
);
|
|
500
|
+
|
|
501
|
+
const someError1 = new Error("E1");
|
|
502
|
+
const someError2 = new Error("E2");
|
|
503
|
+
const someError3 = new Error("E3");
|
|
504
|
+
callStub.resetBehavior();
|
|
505
|
+
callStub.onCall(0).rejects(someError1);
|
|
506
|
+
callStub.onCall(1).rejects(someError2);
|
|
507
|
+
callStub.onCall(2).rejects(someError3);
|
|
508
|
+
|
|
509
|
+
const result = await retriever._performCallAttempt(
|
|
510
|
+
anyParameters,
|
|
511
|
+
anyTimeout,
|
|
512
|
+
anyCancelToken,
|
|
513
|
+
anyRPSFactor
|
|
514
|
+
);
|
|
515
|
+
|
|
516
|
+
result.errors.should.be.deepEqual([
|
|
517
|
+
someError1,
|
|
518
|
+
someError2,
|
|
519
|
+
someError3,
|
|
520
|
+
]);
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
it("Should return null data when all the providers fail", async function () {
|
|
524
|
+
const { providers } = generateProviders(
|
|
525
|
+
3,
|
|
526
|
+
["1", "2", "3"],
|
|
527
|
+
["get", "get", "get"],
|
|
528
|
+
[() => "any", () => "any", () => "any"]
|
|
529
|
+
);
|
|
530
|
+
const retriever = new RobustExternalAPICallerService(
|
|
531
|
+
"any",
|
|
532
|
+
providers
|
|
533
|
+
);
|
|
534
|
+
|
|
535
|
+
const someError = new Error("E1");
|
|
536
|
+
callStub.resetBehavior();
|
|
537
|
+
callStub.rejects(someError);
|
|
538
|
+
|
|
539
|
+
const result = await retriever._performCallAttempt(
|
|
540
|
+
anyParameters,
|
|
541
|
+
anyTimeout,
|
|
542
|
+
anyCancelToken,
|
|
543
|
+
anyRPSFactor
|
|
544
|
+
);
|
|
545
|
+
|
|
546
|
+
(result.data === null).should.be.true();
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
it("Should not call the provider if it has RPS and rpsEnsurer says that this domain's RPS is exceeded", async function () {
|
|
550
|
+
const { providers, isRpsExceededStubs } = generateProviders(
|
|
551
|
+
3,
|
|
552
|
+
["1", "2", "3"],
|
|
553
|
+
["get", "get", "get"],
|
|
554
|
+
[() => null, () => null, () => null],
|
|
555
|
+
anyRPSes(3)
|
|
556
|
+
);
|
|
557
|
+
const retriever = new RobustExternalAPICallerService(
|
|
558
|
+
"any",
|
|
559
|
+
providers
|
|
560
|
+
);
|
|
561
|
+
|
|
562
|
+
isRpsExceededStubs.forEach((stub, index) => {
|
|
563
|
+
if (index === 0 || index === 2) {
|
|
564
|
+
stub.resetBehavior();
|
|
565
|
+
stub.returns(false);
|
|
566
|
+
} else if (index === 1) {
|
|
567
|
+
stub.resetBehavior();
|
|
568
|
+
stub.returns(true);
|
|
569
|
+
}
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
await retriever._performCallAttempt(
|
|
573
|
+
anyParameters,
|
|
574
|
+
anyTimeout,
|
|
575
|
+
anyCancelToken,
|
|
576
|
+
anyRPSFactor
|
|
577
|
+
);
|
|
578
|
+
|
|
579
|
+
(
|
|
580
|
+
callStub.args.find((params) =>
|
|
581
|
+
params[1].startsWith(providers[1].endpoint)
|
|
582
|
+
) == null
|
|
583
|
+
).should.be.true();
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
it("Should correctly count the number of requests declined by RPS exceeding", async function () {
|
|
587
|
+
const { providers, isRpsExceededStubs } = generateProviders(
|
|
588
|
+
5,
|
|
589
|
+
["1", "2", "3", "4", "5"],
|
|
590
|
+
["get", "get", "get", "get", "get"],
|
|
591
|
+
[() => null, () => "any", () => "any", () => null, () => null],
|
|
592
|
+
anyRPSes(5)
|
|
593
|
+
);
|
|
594
|
+
const retriever = new RobustExternalAPICallerService(
|
|
595
|
+
"any",
|
|
596
|
+
providers
|
|
597
|
+
);
|
|
598
|
+
|
|
599
|
+
isRpsExceededStubs.forEach((stub, index) => {
|
|
600
|
+
if (index === 0 || index === 3 || index === 4) {
|
|
601
|
+
stub.resetBehavior();
|
|
602
|
+
stub.returns(false);
|
|
603
|
+
} else if (index === 1 || index === 2) {
|
|
604
|
+
stub.resetBehavior();
|
|
605
|
+
stub.returns(true);
|
|
606
|
+
}
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
const result = await retriever._performCallAttempt(
|
|
610
|
+
anyParameters,
|
|
611
|
+
anyTimeout,
|
|
612
|
+
anyCancelToken,
|
|
613
|
+
anyRPSFactor
|
|
614
|
+
);
|
|
615
|
+
|
|
616
|
+
result.shouldBeForceRetried.should.be.false();
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
it("Should add cancelToken to axios params if it is not null", async function () {
|
|
620
|
+
const { providers } = generateProviders(
|
|
621
|
+
2,
|
|
622
|
+
["1", "2"],
|
|
623
|
+
["get", "get"],
|
|
624
|
+
[() => null, () => "any"]
|
|
625
|
+
);
|
|
626
|
+
const retriever = new RobustExternalAPICallerService(
|
|
627
|
+
"any",
|
|
628
|
+
providers
|
|
629
|
+
);
|
|
630
|
+
|
|
631
|
+
const someCancelToken = "someCC";
|
|
632
|
+
await retriever._performCallAttempt(
|
|
633
|
+
anyParameters,
|
|
634
|
+
anyTimeout,
|
|
635
|
+
someCancelToken,
|
|
636
|
+
anyRPSFactor
|
|
637
|
+
);
|
|
638
|
+
|
|
639
|
+
for (let i = 0; i < callStub.args.length; ++i) {
|
|
640
|
+
callStub.args[i][2]?.cancelToken.should.be.equal(
|
|
641
|
+
someCancelToken
|
|
642
|
+
);
|
|
643
|
+
}
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
it("Should add timeout to axios params if a provider has it", async function () {
|
|
647
|
+
const { providers } = generateProviders(
|
|
648
|
+
2,
|
|
649
|
+
["1", "2"],
|
|
650
|
+
["get", "get"],
|
|
651
|
+
[() => null, () => "any"],
|
|
652
|
+
null,
|
|
653
|
+
[1001, 1002]
|
|
654
|
+
);
|
|
655
|
+
|
|
656
|
+
const retriever = new RobustExternalAPICallerService(
|
|
657
|
+
"any",
|
|
658
|
+
providers
|
|
659
|
+
);
|
|
660
|
+
|
|
661
|
+
await retriever._performCallAttempt(
|
|
662
|
+
anyParameters,
|
|
663
|
+
anyTimeout,
|
|
664
|
+
anyCancelToken,
|
|
665
|
+
anyRPSFactor
|
|
666
|
+
);
|
|
667
|
+
|
|
668
|
+
for (let i = 0; i < callStub.args.length; ++i) {
|
|
669
|
+
callStub.args[i][2]?.timeout.should.be.equal(
|
|
670
|
+
providers[i].timeout
|
|
671
|
+
);
|
|
672
|
+
}
|
|
673
|
+
});
|
|
674
|
+
|
|
675
|
+
it("Should do all sub-request if all of them except the last one don't throw error", async function () {
|
|
676
|
+
const { providers } = generateProviders(
|
|
677
|
+
2,
|
|
678
|
+
["1", "2"],
|
|
679
|
+
[["get", "get", "get"], "get"],
|
|
680
|
+
[() => "any", () => "any"]
|
|
681
|
+
);
|
|
682
|
+
|
|
683
|
+
const retriever = new RobustExternalAPICallerService(
|
|
684
|
+
"any",
|
|
685
|
+
providers
|
|
686
|
+
);
|
|
687
|
+
|
|
688
|
+
const someError = new Error("E1");
|
|
689
|
+
callStub.resetBehavior();
|
|
690
|
+
callStub.onFirstCall().resolves("any");
|
|
691
|
+
callStub.onSecondCall().resolves("any");
|
|
692
|
+
callStub.onThirdCall().rejects(someError);
|
|
693
|
+
|
|
694
|
+
await retriever._performCallAttempt(
|
|
695
|
+
anyParameters,
|
|
696
|
+
anyTimeout,
|
|
697
|
+
anyCancelToken,
|
|
698
|
+
anyRPSFactor
|
|
699
|
+
);
|
|
700
|
+
|
|
701
|
+
for (let i = 0; i < 3; ++i) {
|
|
702
|
+
callStub.args[i][1].should.startWith(providers[0].endpoint);
|
|
703
|
+
}
|
|
704
|
+
});
|
|
705
|
+
|
|
706
|
+
it("Should call query composer proper number of times if there are sub-request", async function () {
|
|
707
|
+
// const queryComposers = [[sinon.spy(), sinon.spy()], sinon.spy()];
|
|
708
|
+
const { providers, composeQueryStringStubs } = generateProviders(
|
|
709
|
+
2,
|
|
710
|
+
["1", "2"],
|
|
711
|
+
[["get", "get"], "get"],
|
|
712
|
+
[
|
|
713
|
+
[
|
|
714
|
+
() => "any",
|
|
715
|
+
function () {
|
|
716
|
+
throw new Error("any");
|
|
717
|
+
},
|
|
718
|
+
],
|
|
719
|
+
() => "any",
|
|
720
|
+
]
|
|
721
|
+
);
|
|
722
|
+
|
|
723
|
+
const retriever = new RobustExternalAPICallerService(
|
|
724
|
+
"any",
|
|
725
|
+
providers
|
|
726
|
+
);
|
|
727
|
+
|
|
728
|
+
await retriever._performCallAttempt(
|
|
729
|
+
anyParameters,
|
|
730
|
+
anyTimeout,
|
|
731
|
+
anyCancelToken,
|
|
732
|
+
anyRPSFactor
|
|
733
|
+
);
|
|
734
|
+
|
|
735
|
+
composeQueryStringStubs[0].callCount.should.be.equal(2);
|
|
736
|
+
composeQueryStringStubs[1].callCount.should.be.equal(1);
|
|
737
|
+
});
|
|
738
|
+
|
|
739
|
+
it("Should pass correct endpoint with query returned by query composer as the first axios param", async function () {
|
|
740
|
+
const { providers, composeQueryStringStubs } = generateProviders(
|
|
741
|
+
2,
|
|
742
|
+
["1", "2"],
|
|
743
|
+
[["get", "get"], "get"],
|
|
744
|
+
[
|
|
745
|
+
[
|
|
746
|
+
() => "any",
|
|
747
|
+
function () {
|
|
748
|
+
throw new Error("any");
|
|
749
|
+
},
|
|
750
|
+
],
|
|
751
|
+
() => "any",
|
|
752
|
+
]
|
|
753
|
+
);
|
|
754
|
+
|
|
755
|
+
const query1 = "?a=4&fgrh=4l4ao4";
|
|
756
|
+
const query2 = "a/s?j3jk=lkk0-3&h=oool230";
|
|
757
|
+
const query3 = "?jijiji2332=09o3i4r3&p1=-121120&hfwowfif=wqoi23";
|
|
758
|
+
composeQueryStringStubs[0].resetBehavior();
|
|
759
|
+
composeQueryStringStubs[0]
|
|
760
|
+
.withArgs(anyParameters, 0)
|
|
761
|
+
.returns(query1);
|
|
762
|
+
composeQueryStringStubs[0]
|
|
763
|
+
.withArgs(anyParameters, 1)
|
|
764
|
+
.returns(query2);
|
|
765
|
+
composeQueryStringStubs[1].resetBehavior();
|
|
766
|
+
composeQueryStringStubs[1]
|
|
767
|
+
.withArgs(anyParameters, 0)
|
|
768
|
+
.returns(query3);
|
|
769
|
+
|
|
770
|
+
const retriever = new RobustExternalAPICallerService(
|
|
771
|
+
"any",
|
|
772
|
+
providers
|
|
773
|
+
);
|
|
774
|
+
|
|
775
|
+
await retriever._performCallAttempt(
|
|
776
|
+
anyParameters,
|
|
777
|
+
anyTimeout,
|
|
778
|
+
anyCancelToken,
|
|
779
|
+
anyRPSFactor
|
|
780
|
+
);
|
|
781
|
+
|
|
782
|
+
callStub.args[0][1].should.be.equal(providers[0].endpoint + query1);
|
|
783
|
+
callStub.args[1][1].should.be.equal(providers[0].endpoint + query2);
|
|
784
|
+
callStub.args[2][1].should.be.equal(providers[1].endpoint + query3);
|
|
785
|
+
});
|
|
786
|
+
});
|
|
787
|
+
});
|