diff --git a/modules/openapi-generator/src/main/resources/python-blueplanet/app/{{packageName}}/test/controller_test.mustache b/modules/openapi-generator/src/main/resources/python-blueplanet/app/{{packageName}}/test/controller_test.mustache index ae6636192d50..899bdcdb5aa7 100644 --- a/modules/openapi-generator/src/main/resources/python-blueplanet/app/{{packageName}}/test/controller_test.mustache +++ b/modules/openapi-generator/src/main/resources/python-blueplanet/app/{{packageName}}/test/controller_test.mustache @@ -28,7 +28,8 @@ class {{#operations}}Test{{classname}}(BaseTestCase): {{#headerParams}} {{#-first}}headers = [{{/-first}}{{^-first}} {{/-first}}('{{baseName}}', {{#vendorExtensions.x-python-connexion-test-header-value}}{{{vendorExtensions.x-python-connexion-test-header-value}}}{{/vendorExtensions.x-python-connexion-test-header-value}}{{^vendorExtensions.x-python-connexion-test-header-value}}{{{example}}}{{/vendorExtensions.x-python-connexion-test-header-value}}){{^-last}},{{/-last}}{{#-last}}]{{/-last}} {{/headerParams}} - {{#cookieParams}}{{#-first}}{{^headerParams}}headers = [] + {{#cookieParams}} + {{#-first}}{{^headerParams}}headers = [] {{/headerParams}}headers += [('Cookie', '; '.join([{{/-first}}'{{baseName}}=' + {{#vendorExtensions.x-python-connexion-test-header-value}}{{{vendorExtensions.x-python-connexion-test-header-value}}}{{/vendorExtensions.x-python-connexion-test-header-value}}{{^vendorExtensions.x-python-connexion-test-header-value}}str({{{example}}}){{/vendorExtensions.x-python-connexion-test-header-value}}{{^-last}}, {{/-last}}{{#-last}}]))]{{/-last}} {{/cookieParams}} {{#formParams}} diff --git a/modules/openapi-generator/src/main/resources/python-flask/controller_test.mustache b/modules/openapi-generator/src/main/resources/python-flask/controller_test.mustache index 92872aa1fb02..e1ae56ac5a93 100644 --- a/modules/openapi-generator/src/main/resources/python-flask/controller_test.mustache +++ b/modules/openapi-generator/src/main/resources/python-flask/controller_test.mustache @@ -32,7 +32,8 @@ class {{#operations}}Test{{classname}}(BaseTestCase): {{#isOAuth}}'Authorization': 'Bearer special-key',{{/isOAuth}}{{#isApiKey}}{{#isKeyInHeader}}'{{keyParamName}}': 'special-key',{{/isKeyInHeader}}{{/isApiKey}}{{#isBasicBasic}}'Authorization': 'Basic Zm9vOmJhcg==',{{/isBasicBasic}}{{#isBasicBearer}}'Authorization': 'Bearer special-key',{{/isBasicBearer}}{{/authMethods}}{{#vendorExtensions.x-test-apikey-cookie}} 'Cookie': '{{{vendorExtensions.x-test-apikey-cookie}}}',{{/vendorExtensions.x-test-apikey-cookie}} } - {{#cookieParams}}{{#-first}}headers['Cookie'] = '; '.join([{{/-first}}'{{{vendorExtensions.x-python-connexion-quoted-name}}}=' + {{#vendorExtensions.x-python-connexion-test-header-value}}{{{vendorExtensions.x-python-connexion-test-header-value}}}{{/vendorExtensions.x-python-connexion-test-header-value}}{{^vendorExtensions.x-python-connexion-test-header-value}}str({{{example}}}){{/vendorExtensions.x-python-connexion-test-header-value}}{{^-last}}, {{/-last}}{{#-last}}]){{/-last}} + {{#cookieParams}} + {{#-first}}headers['Cookie'] = '; '.join([{{/-first}}'{{{vendorExtensions.x-python-connexion-quoted-name}}}=' + {{#vendorExtensions.x-python-connexion-test-header-value}}{{{vendorExtensions.x-python-connexion-test-header-value}}}{{/vendorExtensions.x-python-connexion-test-header-value}}{{^vendorExtensions.x-python-connexion-test-header-value}}str({{{example}}}){{/vendorExtensions.x-python-connexion-test-header-value}}{{^-last}}, {{/-last}}{{#-last}}]){{/-last}} {{/cookieParams}} {{#formParams}} {{#-first}}data = { {{/-first}}{{^-first}} {{/-first}}'{{baseName}}': {{{example}}}{{^-last}},{{/-last}}{{#-last}} }{{/-last}} diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/python/AbstractPythonConnexionServerCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/python/AbstractPythonConnexionServerCodegenTest.java index 50db4a9252d7..3de1432b1059 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/python/AbstractPythonConnexionServerCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/python/AbstractPythonConnexionServerCodegenTest.java @@ -1015,6 +1015,51 @@ public void testJsonContentHeaderParamsGeneratedTestsCompile() throws IOExceptio } } + @Test(description = "statements in generated tests keep their 8-column indent whether or not the operation declares cookie params (an empty {{#cookieParams}} section must not leak the open line's indent into the next statement)") + public void testGeneratedTestsStatementIndentWithAndWithoutCookieParams() throws IOException { + // /ping declares no parameters at all: the empty cookieParams section + // must render nothing, so `response = ...` stays at the same indent as + // the statements around it — a mid-line {{#cookieParams}} tag used to + // leave its 8 leading spaces behind, which merged into the next + // statement and produced an unexpected-indent IndentationError + for (Map.Entry variant : generateConnexionVariants( + "src/test/resources/3_0/python/info-title-special-chars.yaml").entrySet()) { + if (variant.getKey().startsWith("python-aiohttp")) { + // aiohttp's cookie block already opens the section on its own + // line and emits a `cookies = {...}` dict instead of a header + continue; + } + Path testFile = generatedTestPath(variant.getKey(), variant.getValue()); + assertFileExists(testFile); + String ping = generatedTestMethodBody(testFile, "test_ping"); + assertStatementIndent(ping, "response = self.client.open(", 8); + assertStatementIndent(ping, "self.assert200(response,", 8); + } + + // get_cookie_only declares only cookie params: the Cookie statement + // must render at statement level too — flask mutates the headers dict, + // blueplanet appends a ('Cookie', ...) tuple (creating the list first + // when the operation has no header params to initialize it) + for (Map.Entry variant : generateConnexionVariants( + "src/test/resources/3_0/python/connexion-json-header-test-values.yaml").entrySet()) { + String variantKey = variant.getKey(); + if (variantKey.startsWith("python-aiohttp")) { + continue; + } + Path testFile = generatedTestPath(variantKey, variant.getValue()); + assertFileExists(testFile); + String cookieOnly = generatedTestMethodBody(testFile, "test_get_cookie_only"); + if (isBlueplanetVariant(variantKey)) { + assertStatementIndent(cookieOnly, "headers = []", 8); + assertStatementIndent(cookieOnly, "headers += [('Cookie',", 8); + } else { + assertStatementIndent(cookieOnly, "headers['Cookie'] = '; '.join([", 8); + } + assertStatementIndent(cookieOnly, "response = self.client.open(", 8); + assertStatementIndent(cookieOnly, "self.assert200(response,", 8); + } + } + @Test(description = "content (application/json) header/cookie params get X.from_dict() only for generated model classes; inline object schemas pass the json.loads result through") public void testJsonContentHeaderParamsConvertOnlyGeneratedModels() throws IOException { String specPath = "src/test/resources/3_0/python/connexion-json-header-test-values.yaml"; @@ -1276,6 +1321,28 @@ public void testBlueplanetRequirementsPinConnexion2() throws IOException { "swagger-ui-bundle >= 0.0.2"); } + /** + * Asserts every line in {@code text} containing {@code marker} starts + * with exactly {@code expectedIndent} spaces — a substring check cannot + * pin indentation, since a line indented too deeply still contains the + * marker. + */ + private static void assertStatementIndent(String text, String marker, int expectedIndent) { + List matching = text.lines() + .filter(line -> line.contains(marker)) + .collect(Collectors.toList()); + assertThat(matching).as("no generated line contains: %s", marker).isNotEmpty(); + for (String line : matching) { + int indent = 0; + while (indent < line.length() && line.charAt(indent) == ' ') { + indent++; + } + assertThat(indent) + .as("line must start at column %d: %s", expectedIndent, line.trim()) + .isEqualTo(expectedIndent); + } + } + /** * Asserts the file contains a physical line equal to {@code expected} * after trimming — a stronger check than substring containment, used to