Skip to content

Commit 6ed0320

Browse files
authored
Merge 007a85a into ce83a11
2 parents ce83a11 + 007a85a commit 6ed0320

File tree

28 files changed

+1711
-1072
lines changed

28 files changed

+1711
-1072
lines changed

clients/algoliasearch-client-php/composer.lock

Lines changed: 1014 additions & 589 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

generators/src/main/java/com/algolia/codegen/cts/manager/PythonCTSManager.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public void addTestsSupportingFiles(List<SupportingFile> supportingFiles) {
1818
supportingFiles.add(new SupportingFile("tests/__init__.mustache", "tests/output/python/tests/", "__init__.py"));
1919
supportingFiles.add(new SupportingFile("tests/__init__.mustache", "tests/output/python/tests/requests", "__init__.py"));
2020
supportingFiles.add(new SupportingFile("tests/__init__.mustache", "tests/output/python/tests/client", "__init__.py"));
21+
supportingFiles.add(new SupportingFile("tests/__init__.mustache", "tests/output/python/tests/e2e", "__init__.py"));
2122
}
2223

2324
@Override

generators/src/main/java/com/algolia/codegen/cts/tests/TestsRequest.java

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
public class TestsRequest extends TestsGenerator {
1616

1717
private final boolean withSnippets;
18+
private List<SupportingFile> supportingFiles;
1819

1920
public TestsRequest(String language, String client, boolean withSnippets) {
2021
super(language, client);
@@ -64,13 +65,40 @@ public void addSupportingFiles(List<SupportingFile> supportingFiles, String outp
6465
if (!available()) {
6566
return;
6667
}
68+
69+
this.supportingFiles = supportingFiles;
70+
6771
supportingFiles.add(
6872
new SupportingFile(
6973
"tests/requests/requests.mustache",
7074
"tests/output/" + language + "/" + outputFolder + "/requests",
7175
Helpers.createClientName(client, language) + extension
7276
)
7377
);
78+
if (new File("templates/" + language + "/tests/e2e/e2e.mustache").exists()) {
79+
supportingFiles.add(
80+
new SupportingFile(
81+
"tests/e2e/e2e.mustache",
82+
"tests/output/" + language + "/" + outputFolder + "/e2e",
83+
Helpers.createClientName(client, language) + extension
84+
)
85+
);
86+
}
87+
}
88+
89+
private String escapeBody(String body) {
90+
if (body == null) {
91+
return null;
92+
}
93+
94+
switch (language) {
95+
case "go": // jsonassert expect % to be formatted, we need to escape them
96+
return body.replace("%", "%%");
97+
case "dart": // Same thing but for $
98+
return body.replace("$", "\\$");
99+
default:
100+
return body;
101+
}
74102
}
75103

76104
@Override
@@ -82,6 +110,7 @@ public void run(Map<String, CodegenModel> models, Map<String, CodegenOperation>
82110
}
83111

84112
List<Object> blocks = new ArrayList<>();
113+
List<Object> blocksE2E = new ArrayList<>();
85114
ParametersWithDataType paramsType = new ParametersWithDataType(models, language, client);
86115

87116
bundle.put("e2eApiKey", client.equals("monitoring") ? "MONITORING_API_KEY" : "ALGOLIA_ADMIN_KEY");
@@ -138,15 +167,7 @@ public void run(Map<String, CodegenModel> models, Map<String, CodegenOperation>
138167
req.request.body = "{}";
139168
}
140169

141-
// For golang, jsonassert expect % to be formatted, we need to escape them
142-
if (language.equals("go") && req.request.body != null) {
143-
req.request.body = req.request.body.replace("%", "%%");
144-
}
145-
146-
// For dart, same thing but for $
147-
if (language.equals("dart") && req.request.body != null) {
148-
req.request.body = req.request.body.replace("$", "\\$");
149-
}
170+
req.request.body = escapeBody(req.request.body);
150171

151172
// In a case of a `GET` or `DELETE` request, we want to assert if the body
152173
// is correctly parsed (absent from the payload)
@@ -156,7 +177,7 @@ public void run(Map<String, CodegenModel> models, Map<String, CodegenOperation>
156177
}
157178

158179
if (req.response != null) {
159-
bundle.put("hasE2E", true);
180+
req.response.body = escapeBody(req.response.body);
160181
test.put("response", req.response);
161182
}
162183

@@ -219,7 +240,21 @@ public void run(Map<String, CodegenModel> models, Map<String, CodegenOperation>
219240
}
220241

221242
blocks.add(testObj);
243+
244+
// extract e2e
245+
List<Map<String, Object>> e2e = tests.stream().filter(t -> t.get("response") != null).toList();
246+
if (e2e.size() > 0) {
247+
Map<String, Object> e2eObj = new HashMap<>();
248+
e2eObj.put("tests", e2e);
249+
e2eObj.put("operationId", operationId);
250+
blocksE2E.add(e2eObj);
251+
}
222252
}
223253
bundle.put("blocksRequests", blocks);
254+
if (!blocksE2E.isEmpty()) {
255+
bundle.put("blocksE2E", blocksE2E);
256+
} else if (supportingFiles != null) {
257+
supportingFiles.removeIf(f -> f.getTemplateFile().equals("tests/e2e/e2e.mustache"));
258+
}
224259
}
225260
}

scripts/ci/githubActions/createMatrix.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,15 +69,15 @@ async function createClientMatrix(baseBranch: string): Promise<void> {
6969
const testsRootFolder = `tests/output/${language}`;
7070
const testsOutputBase = `${testsRootFolder}/${getTestOutputFolder(language)}`;
7171
// We delete tests to ensure the CI only run tests against what changed.
72-
const testsToDelete = `${testsOutputBase}/client ${testsOutputBase}/requests`;
72+
const testsToDelete = `${testsOutputBase}/client ${testsOutputBase}/requests ${testsOutputBase}/e2e`;
7373

7474
// We only store tests of clients that ran during this job, the rest stay as is
7575
let testsToStore = matrix[language].toRun
7676
.map((client) => {
7777
const clientName = createClientName(client, language);
7878
const extension = getTestExtension(language);
7979

80-
return `${testsOutputBase}/client/${clientName}${extension} ${testsOutputBase}/requests/${clientName}${extension}`;
80+
return `${testsOutputBase}/client/${clientName}${extension} ${testsOutputBase}/requests/${clientName}${extension} ${testsOutputBase}/e2e/${clientName}${extension}`;
8181
})
8282
.join(' ');
8383

scripts/cts/runCts.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,12 @@ async function runCtsOne(language: string): Promise<void> {
3636
break;
3737
case 'php':
3838
await runComposerInstall();
39-
await run(`php ./clients/algoliasearch-client-php/vendor/bin/phpunit ${cwd}`, {
40-
language,
41-
});
39+
await run(
40+
`php ./clients/algoliasearch-client-php/vendor/bin/phpunit --testdox --fail-on-warning ${cwd}`,
41+
{
42+
language,
43+
},
44+
);
4245
break;
4346
case 'python':
4447
await run('poetry lock --no-update && poetry install --sync && poetry run pytest -vv', {

scripts/husky/pre-commit.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export function getPatterns() {
2222
for (const [language, { tests }] of Object.entries(clientConfig)) {
2323
entries.unshift(`tests/output/${language}/${tests.outputFolder}/client/**`);
2424
entries.unshift(`tests/output/${language}/${tests.outputFolder}/requests/**`);
25+
entries.unshift(`tests/output/${language}/${tests.outputFolder}/e2e/**`);
2526
}
2627
return entries;
2728
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// {{generationBanner}}
2+
using Algolia.Search.Http;
3+
using Algolia.Search.Clients;
4+
using Algolia.Search.Models.{{clientPrefix}};
5+
using Algolia.Search.Serializer;
6+
using Algolia.Search.Tests.Utils;
7+
using Xunit;
8+
using System.Text.Json;
9+
using Quibble.Xunit;
10+
using dotenv.net;
11+
using Action = Algolia.Search.Models.Search.Action;
12+
13+
public class {{client}}RequestTestsE2E
14+
{
15+
private readonly {{client}} _client;
16+
17+
public {{client}}RequestTestsE2E()
18+
{
19+
DotEnv.Load(options: new DotEnvOptions(ignoreExceptions: true, probeForEnv: true, probeLevelsToSearch: 8, envFilePaths: new[] { ".env" }));
20+
21+
var appId = Environment.GetEnvironmentVariable("ALGOLIA_APPLICATION_ID");
22+
if (appId == null)
23+
{
24+
throw new Exception("please provide an `ALGOLIA_APPLICATION_ID` env var for e2e tests");
25+
}
26+
27+
var apiKey = Environment.GetEnvironmentVariable("{{e2eApiKey}}");
28+
if (apiKey == null)
29+
{
30+
throw new Exception("please provide an `{{e2eApiKey}}` env var for e2e tests");
31+
}
32+
33+
_client = new {{client}}(new {{clientPrefix}}Config(appId, apiKey{{#hasRegionalHost}},"{{defaultRegion}}"{{/hasRegionalHost}}));
34+
}
35+
36+
[Fact]
37+
public void Dispose()
38+
{
39+
40+
}
41+
42+
{{#blocksE2E}}
43+
{{#tests}}
44+
[Fact(DisplayName = "{{{testName}}}")]
45+
public async Task {{#lambda.pascalcase}}{{method}}Test{{testIndex}}{{/lambda.pascalcase}}()
46+
{
47+
try {
48+
var resp = await _client.{{#lambda.pascalcase}}{{method}}{{/lambda.pascalcase}}Async{{#isGeneric}}<Hit>{{/isGeneric}}({{#parametersWithDataType}}{{> tests/generateParams}}{{^-last}},{{/-last}}{{/parametersWithDataType}}{{#hasRequestOptions}}, new RequestOptions(){
49+
{{#requestOptions.queryParameters}}
50+
QueryParameters = new Dictionary<string, object>(){ {{#parametersWithDataType}} {"{{{key}}}", {{> tests/requests/requestOptionsParams}} } {{^-last}},{{/-last}}{{/parametersWithDataType}} },
51+
{{/requestOptions.queryParameters}}
52+
{{#requestOptions.headers}}
53+
Headers = new Dictionary<string, string>(){ {{#parametersWithDataType}} {"{{{key}}}", "{{{value}}}" } {{^-last}},{{/-last}}{{/parametersWithDataType}} },
54+
{{/requestOptions.headers}}
55+
}{{/hasRequestOptions}});
56+
{{#response}}
57+
{{#statusCode}}
58+
// Check status code {{statusCode}}
59+
Assert.NotNull(resp);
60+
{{/statusCode}}
61+
62+
{{#body}}
63+
JsonAssert.EqualOverrideDefault("{{#lambda.escapeQuotes}}{{{.}}}{{/lambda.escapeQuotes}}", JsonSerializer.Serialize(resp, JsonConfig.Options), new JsonDiffConfig(true));
64+
{{/body}}
65+
} catch (Exception e)
66+
{
67+
Assert.Fail("An exception was thrown: " + e.Message);
68+
}
69+
{{/response}}
70+
}
71+
{{/tests}}
72+
{{/blocksE2E}}
73+
}

templates/csharp/tests/requests/requests.mustache

Lines changed: 1 addition & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -12,31 +12,13 @@ using Action = Algolia.Search.Models.Search.Action;
1212

1313
public class {{client}}RequestTests
1414
{
15-
private readonly {{client}} _client{{#hasE2E}}, _e2eClient{{/hasE2E}};
15+
private readonly {{client}} _client;
1616
private readonly EchoHttpRequester _echo;
1717

1818
public {{client}}RequestTests()
1919
{
2020
_echo = new EchoHttpRequester();
2121
_client = new {{client}}(new {{clientPrefix}}Config("appId", "apiKey"{{#hasRegionalHost}},"{{defaultRegion}}"{{/hasRegionalHost}}), _echo);
22-
23-
{{#hasE2E}}
24-
DotEnv.Load(options: new DotEnvOptions(ignoreExceptions: true, probeForEnv: true, probeLevelsToSearch: 8, envFilePaths: new[] { ".env" }));
25-
26-
var e2EAppId = Environment.GetEnvironmentVariable("ALGOLIA_APPLICATION_ID");
27-
if (e2EAppId == null)
28-
{
29-
throw new Exception("please provide an `ALGOLIA_APPLICATION_ID` env var for e2e tests");
30-
}
31-
32-
var e2EApiKey = Environment.GetEnvironmentVariable("{{e2eApiKey}}");
33-
if (e2EApiKey == null)
34-
{
35-
throw new Exception("please provide an `{{e2eApiKey}}` env var for e2e tests");
36-
}
37-
38-
_e2eClient = new {{client}}(new {{clientPrefix}}Config(e2EAppId, e2EApiKey{{#hasRegionalHost}},"{{defaultRegion}}"{{/hasRegionalHost}}));
39-
{{/hasE2E}}
4022
}
4123

4224
[Fact]
@@ -99,32 +81,6 @@ private readonly {{client}} _client{{#hasE2E}}, _e2eClient{{/hasE2E}};
9981
}
10082
{{/headers}}
10183
{{/request}}
102-
103-
104-
{{#response}}
105-
// e2e
106-
try {
107-
var resp = await _e2eClient.{{#lambda.pascalcase}}{{method}}{{/lambda.pascalcase}}Async{{#isGeneric}}<Hit>{{/isGeneric}}({{#parametersWithDataType}}{{> tests/generateParams}}{{^-last}},{{/-last}}{{/parametersWithDataType}}{{#hasRequestOptions}}, new RequestOptions(){
108-
{{#requestOptions.queryParameters}}
109-
QueryParameters = new Dictionary<string, object>(){ {{#parametersWithDataType}} {"{{{key}}}", {{> tests/requests/requestOptionsParams}} } {{^-last}},{{/-last}}{{/parametersWithDataType}} },
110-
{{/requestOptions.queryParameters}}
111-
{{#requestOptions.headers}}
112-
Headers = new Dictionary<string, string>(){ {{#parametersWithDataType}} {"{{{key}}}", "{{{value}}}" } {{^-last}},{{/-last}}{{/parametersWithDataType}} },
113-
{{/requestOptions.headers}}
114-
}{{/hasRequestOptions}});
115-
{{#statusCode}}
116-
// Check status code {{statusCode}}
117-
Assert.NotNull(resp);
118-
{{/statusCode}}
119-
120-
{{#body}}
121-
JsonAssert.EqualOverrideDefault("{{#lambda.escapeQuotes}}{{{.}}}{{/lambda.escapeQuotes}}", JsonSerializer.Serialize(resp, JsonConfig.Options), new JsonDiffConfig(true));
122-
{{/body}}
123-
} catch (Exception e)
124-
{
125-
Assert.Fail("An exception was thrown: " + e.Message);
126-
}
127-
{{/response}}
12884
}
12985
{{/tests}}
13086
{{/blocksRequests}}

templates/go/tests/e2e/e2e.mustache

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// {{generationBanner}}
2+
package requestse2e
3+
4+
import (
5+
"os"
6+
"testing"
7+
8+
"github.com/stretchr/testify/require"
9+
10+
"github.com/joho/godotenv"
11+
12+
"github.com/algolia/algoliasearch-client-go/v4/algolia/{{clientImport}}"
13+
)
14+
15+
func createE2E{{#lambda.titlecase}}{{clientPrefix}}{{/lambda.titlecase}}Client(t *testing.T) *{{clientPrefix}}.APIClient {
16+
t.Helper()
17+
18+
appID := os.Getenv("ALGOLIA_APPLICATION_ID")
19+
if appID == "" && os.Getenv("CI") != "true" {
20+
err := godotenv.Load("../../../../.env")
21+
require.NoError(t, err)
22+
appID = os.Getenv("ALGOLIA_APPLICATION_ID")
23+
}
24+
apiKey := os.Getenv("{{e2eApiKey}}")
25+
client, err := {{clientPrefix}}.NewClient(appID, apiKey, {{#hasRegionalHost}}{{clientPrefix}}.{{#lambda.uppercase}}{{defaultRegion}}{{/lambda.uppercase}},{{/hasRegionalHost}})
26+
require.NoError(t, err)
27+
28+
return client
29+
}
30+
31+
{{#blocksE2E}}
32+
func Test{{#lambda.titlecase}}{{clientPrefix}}{{/lambda.titlecase}}E2E_{{#lambda.titlecase}}{{operationId}}{{/lambda.titlecase}}(t *testing.T) {
33+
{{#tests}}
34+
t.Run("{{{testName}}}", func(t *testing.T) {
35+
client := createE2E{{#lambda.titlecase}}{{clientPrefix}}{{/lambda.titlecase}}Client(t)
36+
res, err := client.{{#lambda.titlecase}}{{method}}{{/lambda.titlecase}}({{#hasOperationParams}}client.NewApi{{#lambda.titlecase}}{{method}}{{/lambda.titlecase}}Request(
37+
{{#parametersWithDataType}}{{#required}}{{> tests/generateParams}},{{/required}}{{/parametersWithDataType}}
38+
){{#parametersWithDataType}}{{^required}}.With{{#lambda.pascalcase}}{{{key}}}{{/lambda.pascalcase}}({{> tests/generateParams}}){{/required}}{{/parametersWithDataType}}{{/hasOperationParams}}{{#requestOptions}}{{#hasOperationParams}},{{/hasOperationParams}}
39+
{{#queryParameters.parametersWithDataType}}{{clientPrefix}}.QueryParamOption("{{{key}}}", {{> tests/generateInnerParams}}),{{/queryParameters.parametersWithDataType}}{{#headers.parametersWithDataType}}{{clientPrefix}}.HeaderParamOption("{{{key}}}", {{> tests/generateInnerParams}}),{{/headers.parametersWithDataType}}
40+
{{/requestOptions}})
41+
require.NoError(t, err)
42+
_ = res
43+
44+
{{#response}}
45+
{{#body}}
46+
rawBody, err := json.Marshal(res)
47+
require.NoError(t, err)
48+
49+
var rawBodyMap any
50+
err = json.Unmarshal(rawBody, &rawBodyMap)
51+
require.NoError(t, err)
52+
53+
expectedBodyRaw := `{{{.}}}`
54+
var expectedBody any
55+
err = json.Unmarshal([]byte(expectedBodyRaw), &expectedBody)
56+
require.NoError(t, err)
57+
58+
unionBody := tests.Union(expectedBody, rawBodyMap)
59+
unionBodyRaw, err := json.Marshal(unionBody)
60+
require.NoError(t, err)
61+
62+
jsonassert.New(t).Assertf(string(unionBodyRaw), expectedBodyRaw)
63+
{{/body}}
64+
{{/response}}
65+
})
66+
{{/tests}}
67+
}
68+
69+
{{/blocksE2E}}

0 commit comments

Comments
 (0)