Skip to content

Commit 072e4d9

Browse files
authored
fix(swift): retry strategy + cts test (#5654)
1 parent f095a01 commit 072e4d9

File tree

6 files changed

+134
-3
lines changed

6 files changed

+134
-3
lines changed

clients/algoliasearch-client-swift/Sources/Core/Networking/RetryStrategy/AlgoliaRetryStrategy.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ class AlgoliaRetryStrategy: RetryStrategy {
4242
}
4343

4444
var hostIterator = self.hosts
45-
.sorted { $0.lastUpdated.compare($1.lastUpdated) == .orderedAscending }
4645
.filter { $0.supports(callType) && $0.isUp }
4746
.makeIterator()
4847

scripts/cts/runCts.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,14 @@ import type { Language } from '../types.ts';
88
import { assertValidAccountCopyIndex } from './testServer/accountCopyIndex.ts';
99
import { printBenchmarkReport } from './testServer/benchmark.ts';
1010
import { assertChunkWrapperValid } from './testServer/chunkWrapper.ts';
11-
import { assertValidErrors } from './testServer/error.ts';
11+
import { assertNeverCalledServerWasNotCalled, assertValidErrors } from './testServer/error.ts';
1212
import { startTestServer } from './testServer/index.ts';
1313
import { assertPushMockValid } from './testServer/pushMock.ts';
1414
import { assertValidReplaceAllObjects } from './testServer/replaceAllObjects.ts';
1515
import { assertValidReplaceAllObjectsFailed } from './testServer/replaceAllObjectsFailed.ts';
1616
import { assertValidReplaceAllObjectsScopes } from './testServer/replaceAllObjectsScopes.ts';
1717
import { assertValidReplaceAllObjectsWithTransformation } from './testServer/replaceAllObjectsWithTransformation.ts';
18+
import { assertSuccessServerCalled } from './testServer/success.ts';
1819
import { assertValidTimeouts } from './testServer/timeout.ts';
1920
import { assertValidWaitForApiKey } from './testServer/waitFor.ts';
2021

@@ -158,6 +159,8 @@ export async function runCts(
158159

159160
assertValidErrors(languages.length);
160161
assertValidTimeouts(languages.length);
162+
assertNeverCalledServerWasNotCalled();
163+
assertSuccessServerCalled(languages.length);
161164
assertChunkWrapperValid(languages.length - skip('dart'));
162165
assertValidReplaceAllObjects(languages.length - skip('dart'));
163166
assertValidReplaceAllObjectsWithTransformation(

scripts/cts/testServer/error.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type express from 'express';
66
import { setupServer } from './index.ts';
77

88
const errorState: Record<string, { errorCount: number; maxError: number }> = {};
9+
const neverCalledState: Record<string, number> = {};
910

1011
export function assertValidErrors(expectedCount: number): void {
1112
// assert that the retry strategy uses the correct timings, by checking the time between each request, and how long each request took before being timed out
@@ -26,6 +27,15 @@ export function assertValidErrors(expectedCount: number): void {
2627
}
2728
}
2829

30+
export function assertNeverCalledServerWasNotCalled(): void {
31+
for (const [lang, callCount] of Object.entries(neverCalledState)) {
32+
expect(callCount).to.equal(
33+
0,
34+
`Never-called server was called ${callCount} times for ${lang}, but should never be called`,
35+
);
36+
}
37+
}
38+
2939
function addRoutes(app: express.Express): void {
3040
app.post('/1/test/error/:lang', (req, res) => {
3141
const lang = req.params.lang;
@@ -58,3 +68,24 @@ export function errorServerRetriedOnce(): Promise<Server> {
5868
export function errorServerRetriedTwice(): Promise<Server> {
5969
return setupServer('errorRetriedTwice', 6673, addRoutes);
6070
}
71+
72+
function addNeverCalledRoutes(app: express.Express): void {
73+
app.get('/1/test/calling/:lang', (req, res) => {
74+
const lang = req.params.lang;
75+
if (!neverCalledState[lang]) {
76+
neverCalledState[lang] = 0;
77+
}
78+
79+
neverCalledState[lang]++;
80+
81+
// This should never be reached if the retry strategy correctly reuses successful hosts
82+
res.status(500).json({
83+
message: 'This fallback server should never be called when the first host succeeds',
84+
callCount: neverCalledState[lang],
85+
});
86+
});
87+
}
88+
89+
export function neverCalledServer(): Promise<Server> {
90+
return setupServer('neverCalled', 6674, addNeverCalledRoutes);
91+
}

scripts/cts/testServer/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,14 @@ import { algoliaMockServer } from './algoliaMock.ts';
1212
import { apiKeyServer } from './apiKey.ts';
1313
import { benchmarkServer } from './benchmark.ts';
1414
import { chunkWrapperServer } from './chunkWrapper.ts';
15-
import { errorServer, errorServerRetriedOnce, errorServerRetriedTwice } from './error.ts';
15+
import { errorServer, errorServerRetriedOnce, errorServerRetriedTwice, neverCalledServer } from './error.ts';
1616
import { gzipServer } from './gzip.ts';
1717
import { pushMockServer, pushMockServerRetriedOnce } from './pushMock.ts';
1818
import { replaceAllObjectsServer } from './replaceAllObjects.ts';
1919
import { replaceAllObjectsServerFailed } from './replaceAllObjectsFailed.ts';
2020
import { replaceAllObjectsScopesServer } from './replaceAllObjectsScopes.ts';
2121
import { replaceAllObjectsWithTransformationServer } from './replaceAllObjectsWithTransformation.ts';
22+
import { successServer } from './success.ts';
2223
import { timeoutServer } from './timeout.ts';
2324
import { timeoutServerBis } from './timeoutBis.ts';
2425
import { waitForApiKeyServer } from './waitFor.ts';
@@ -31,6 +32,8 @@ export async function startTestServer(suites: Record<CTSType, boolean>): Promise
3132
errorServer(),
3233
errorServerRetriedOnce(),
3334
errorServerRetriedTwice(),
35+
neverCalledServer(),
36+
successServer(),
3437
gzipServer(),
3538
timeoutServerBis(),
3639
accountCopyIndexServer(),

scripts/cts/testServer/success.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import type { Server } from 'http';
2+
3+
import { expect } from 'chai';
4+
import type express from 'express';
5+
6+
import { setupServer } from './index.ts';
7+
8+
const successState: Record<string, number> = {};
9+
10+
export function assertSuccessServerCalled(expectedCount: number): void {
11+
// Verify that the success server was called the expected number of times per language
12+
if (Object.keys(successState).length !== expectedCount) {
13+
throw new Error(`Expected ${expectedCount} language(s) to test the success server`);
14+
}
15+
16+
for (const [lang, callCount] of Object.entries(successState)) {
17+
// python has sync and async tests, each making 2 requests
18+
if (lang === 'python') {
19+
expect(callCount).to.equal(
20+
4,
21+
`Success server was called ${callCount} times for ${lang}, expected 4 (2 sync + 2 async)`,
22+
);
23+
continue;
24+
}
25+
26+
// Each test makes 2 consecutive requests, both should hit this server
27+
expect(callCount).to.equal(2, `Success server was called ${callCount} times for ${lang}, expected 2`);
28+
}
29+
}
30+
31+
function addRoutes(app: express.Express): void {
32+
app.get('/1/test/calling/:lang', (req, res) => {
33+
const lang = req.params.lang;
34+
if (!successState[lang]) {
35+
successState[lang] = 0;
36+
}
37+
38+
successState[lang]++;
39+
40+
res.status(200).json({
41+
message: 'success server response',
42+
});
43+
});
44+
}
45+
46+
export function successServer(): Promise<Server> {
47+
return setupServer('success', 6675, addRoutes);
48+
}

tests/CTS/client/search/api.json

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,5 +336,52 @@
336336
}
337337
}
338338
]
339+
},
340+
{
341+
"testName": "does not retry on success",
342+
"autoCreateClient": false,
343+
"steps": [
344+
{
345+
"type": "createClient",
346+
"parameters": {
347+
"appId": "test-app-id",
348+
"apiKey": "test-api-key",
349+
"customHosts": [
350+
{
351+
"port": 6675
352+
},
353+
{
354+
"port": 6674
355+
}
356+
]
357+
}
358+
},
359+
{
360+
"type": "method",
361+
"method": "customGet",
362+
"parameters": {
363+
"path": "1/test/calling/${{language}}"
364+
},
365+
"expected": {
366+
"type": "response",
367+
"match": {
368+
"message": "success server response"
369+
}
370+
}
371+
},
372+
{
373+
"type": "method",
374+
"method": "customGet",
375+
"parameters": {
376+
"path": "1/test/calling/${{language}}"
377+
},
378+
"expected": {
379+
"type": "response",
380+
"match": {
381+
"message": "success server response"
382+
}
383+
}
384+
}
385+
]
339386
}
340387
]

0 commit comments

Comments
 (0)