Skip to content

Commit 0a83b1c

Browse files
committed
fix tests
1 parent f5b3e5a commit 0a83b1c

2 files changed

Lines changed: 308 additions & 0 deletions

File tree

modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5946,4 +5946,87 @@ public void testOneOfRefEnumDiscriminatorResolvesType() throws IOException {
59465946
"override val vehicleType: VehicleType = VehicleType.TRUCK"
59475947
);
59485948
}
5949+
5950+
5951+
// ── Issue 20502: string-escaping fixes ─────────────────────────────────────────────────────────
5952+
5953+
/**
5954+
* Issue 1: path parameters containing {@code $} (e.g. {@code {item$Id}}) were emitted as-is into
5955+
* Kotlin {@code const val} string literals, triggering Kotlin string interpolation and causing a
5956+
* compile error. The path must be rendered with {@code $} escaped to {@code \$}.
5957+
*/
5958+
@Test(description = "Issue 20502 #1: $ in path param names must be escaped to \\$ in PATH const val")
5959+
public void issue20502_dollarEscapedInPathConstVal() throws IOException {
5960+
File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
5961+
output.deleteOnExit();
5962+
5963+
KotlinSpringServerCodegen codegen = new KotlinSpringServerCodegen();
5964+
codegen.setOutputDir(output.getAbsolutePath());
5965+
5966+
new DefaultGenerator()
5967+
.opts(new ClientOptInput()
5968+
.openAPI(TestUtils.parseSpec("src/test/resources/3_0/kotlin/issue20502-kotlin-string-escaping.yaml"))
5969+
.config(codegen))
5970+
.generate();
5971+
5972+
Path apiFile = Paths.get(output + "/src/main/kotlin/org/openapitools/api/ItemsApiController.kt");
5973+
// The path "/items/{item$Id}/sub/{item$SubId}" must have $ escaped as \$ in the const val
5974+
assertFileContains(apiFile, "\"/items/{item\\$Id}/sub/{item\\$SubId}\"");
5975+
}
5976+
5977+
/**
5978+
* Issue 2: {@code $}, {@code "}, and {@code \} in operation summaries, descriptions, response
5979+
* messages, and parameter descriptions were not properly escaped when placed inside Kotlin
5980+
* annotation double-quoted strings, producing invalid Kotlin code.
5981+
*/
5982+
@Test(description = "Issue 20502 #2: $, \", \\ in annotation strings must be properly escaped")
5983+
public void issue20502_specialCharsEscapedInAnnotationStrings() throws IOException {
5984+
File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
5985+
output.deleteOnExit();
5986+
5987+
KotlinSpringServerCodegen codegen = new KotlinSpringServerCodegen();
5988+
codegen.setOutputDir(output.getAbsolutePath());
5989+
5990+
new DefaultGenerator()
5991+
.opts(new ClientOptInput()
5992+
.openAPI(TestUtils.parseSpec("src/test/resources/3_0/kotlin/issue20502-kotlin-string-escaping.yaml"))
5993+
.config(codegen))
5994+
.generate();
5995+
5996+
Path apiFile = Paths.get(output + "/src/main/kotlin/org/openapitools/api/ItemsApiController.kt");
5997+
// Summary/message annotation strings must have $ escaped to \$
5998+
assertFileContains(apiFile, "\\$some");
5999+
// Notes/description uses triple-quoted strings: $ becomes ${'$'}
6000+
assertFileContains(apiFile, "${'$'}some");
6001+
}
6002+
6003+
/**
6004+
* Issue 3: {@code toDefaultValue()} previously called {@code escapeText()} which internally
6005+
* calls {@code escapeUnsafeCharacters()}, corrupting star-slash to star-underscore-slash inside
6006+
* Kotlin string literals. Default values must be emitted verbatim (modulo string-literal
6007+
* escaping).
6008+
*/
6009+
@Test(description = "Issue 20502 #3: */ in string defaults must not be corrupted to *_/")
6010+
public void issue20502_starSlashNotCorruptedInModelDefaults() throws IOException {
6011+
File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
6012+
output.deleteOnExit();
6013+
6014+
KotlinSpringServerCodegen codegen = new KotlinSpringServerCodegen();
6015+
codegen.setOutputDir(output.getAbsolutePath());
6016+
6017+
new DefaultGenerator()
6018+
.opts(new ClientOptInput()
6019+
.openAPI(TestUtils.parseSpec("src/test/resources/3_0/kotlin/issue20502-kotlin-string-escaping.yaml"))
6020+
.config(codegen))
6021+
.generate();
6022+
6023+
Path modelFile = Paths.get(output + "/src/main/kotlin/org/openapitools/model/ItemWithAllEscapingEdgeCases.kt");
6024+
// The default "starts /* comment and ends */" must not be mangled to *_/
6025+
assertFileContains(modelFile, "\"starts /* comment and ends */\"");
6026+
// $ in string defaults must be escaped to \$ (not left raw, which would cause interpolation)
6027+
assertFileContains(modelFile, "\"\\$one");
6028+
// baseName with $ must be escaped in @get:JsonProperty (verifies lambda resolves baseName correctly)
6029+
assertFileContains(modelFile, "@get:JsonProperty(\"\\$id\")");
6030+
assertFileContains(modelFile, "@get:JsonProperty(\"name\\$Value\")");
6031+
}
59496032
}
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
openapi: 3.0.0
2+
info:
3+
title: "Kotlin string-escaping worst-case: SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
4+
description: |
5+
Multi-line description with every problematic sequence:
6+
double-quote: "
7+
single-backslash: \
8+
double-backslash: \\
9+
dollar: $interpolated
10+
comment-close: */
11+
comment-open: /*
12+
triple-quote: """
13+
all combined: SQ="; SBS=\; DBS=\\; SD=$some; CC=*/; TQ="""
14+
version: "1.0.0"
15+
16+
servers:
17+
- url: 'http://localhost/v1'
18+
19+
# External path-level docs to show externalDocs on an operation
20+
externalDocs:
21+
description: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some"
22+
url: 'http://example.com/docs'
23+
24+
paths:
25+
26+
# ── Issue 1: $ in path parameter names ────────────────────────────────────
27+
/items/{item$Id}/sub/{item$SubId}:
28+
get:
29+
operationId: getItemWithDollarPathParams
30+
summary: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
31+
description: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
32+
externalDocs:
33+
description: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some"
34+
url: 'http://example.com/ops/getItem'
35+
parameters:
36+
# $ in path param name — double-quote YAML form
37+
- name: "item$Id"
38+
in: path
39+
required: true
40+
description: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
41+
schema:
42+
type: string
43+
example: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
44+
45+
# $ in path param name — single-quote YAML form
46+
- name: "item$SubId"
47+
in: path
48+
required: true
49+
description: 'SQ="; SBS=\; DBS=\\; SD=$some; CC=*/; TQ="""'
50+
schema:
51+
type: string
52+
example: 'SQ="; SBS=\; DBS=\\; SD=$some; CC=*/; TQ="""'
53+
54+
# $ in query param name, with default
55+
- name: "filter$Type"
56+
in: query
57+
required: false
58+
description: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
59+
schema:
60+
type: string
61+
example: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
62+
default: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
63+
64+
# deprecated query param
65+
- name: "filter$SubType"
66+
in: query
67+
required: false
68+
deprecated: true
69+
description: 'SQ="; SBS=\; DBS=\\; SD=$some; CC=*/; TQ="""'
70+
schema:
71+
type: string
72+
example: 'SQ="; SBS=\; DBS=\\; SD=$some; CC=*/; TQ="""'
73+
default: 'SQ="; SBS=\; DBS=\\; SD=$some; CC=*/; TQ="""'
74+
75+
# $ in header param name
76+
- name: "X-Custom$Header"
77+
in: header
78+
required: false
79+
description: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
80+
schema:
81+
type: string
82+
example: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
83+
84+
# $ in cookie param name
85+
- name: "session$Token"
86+
in: cookie
87+
required: false
88+
description: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
89+
schema:
90+
type: string
91+
example: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
92+
93+
responses:
94+
'200':
95+
description: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
96+
content:
97+
application/json:
98+
schema:
99+
$ref: '#/components/schemas/ItemWithAllEscapingEdgeCases'
100+
101+
# ── Issue 2: $ / " / \ in form body ───────────────────────────────────────
102+
/items:
103+
post:
104+
operationId: createItemWithDollarFormParams
105+
summary: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
106+
description: |
107+
Multi-line description:
108+
double-quote: "
109+
single-backslash: \
110+
double-backslash: \\
111+
dollar-interpolation: $some
112+
comment-closer: */
113+
comment-opener: /*
114+
triple-quote: """
115+
requestBody:
116+
required: true
117+
content:
118+
application/x-www-form-urlencoded:
119+
schema:
120+
type: object
121+
properties:
122+
form$Name:
123+
type: string
124+
description: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
125+
example: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
126+
form$Value:
127+
type: string
128+
description: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
129+
example: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
130+
default: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
131+
responses:
132+
'201':
133+
description: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
134+
content:
135+
application/json:
136+
schema:
137+
$ref: '#/components/schemas/ItemWithAllEscapingEdgeCases'
138+
139+
components:
140+
schemas:
141+
142+
# ── Issue 3: $ / " / \ in property names, descriptions, defaults, examples
143+
ItemWithAllEscapingEdgeCases:
144+
type: object
145+
description: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
146+
properties:
147+
148+
# property name starts with $
149+
$id:
150+
type: string
151+
description: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
152+
example: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
153+
default: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
154+
155+
# property name has $ in middle
156+
name$Value:
157+
type: string
158+
description: 'SQ="; SBS=\; DBS=\\; SD=$some; CC=*/; TQ="""'
159+
example: 'SQ="; SBS=\; DBS=\\; SD=$some; CC=*/; TQ="""'
160+
default: 'SQ="; SBS=\; DBS=\\; SD=$some; CC=*/; TQ="""'
161+
162+
# just backslashes (edge case: odd vs even number)
163+
backslashValue:
164+
type: string
165+
description: "one-bs: \\; two-bs: \\\\"
166+
example: "one-bs: \\; two-bs: \\\\"
167+
default: "one-bs: \\; two-bs: \\\\"
168+
169+
# just dollar signs
170+
dollarValue:
171+
type: string
172+
description: "$one $$two $$$three"
173+
example: "$one $$two $$$three"
174+
default: "$one $$two $$$three"
175+
176+
# comment-closing sequence (the escapeUnsafeCharacters bug)
177+
commentCloseValue:
178+
type: string
179+
description: "starts /* comment and ends */"
180+
example: "starts /* comment and ends */"
181+
default: "starts /* comment and ends */"
182+
183+
# triple-quote sequence (would break """ Kotlin strings)
184+
tripleQuoteValue:
185+
type: string
186+
description: 'contains """ triple quotes'
187+
example: 'contains """ triple quotes'
188+
default: 'contains """ triple quotes'
189+
190+
# multiline description (YAML literal block)
191+
multilineDescription:
192+
type: string
193+
description: |
194+
Line one with $dollar and "quote" and \backslash
195+
Line two with */ comment-close and /* comment-open
196+
Line three with """ triple-quote
197+
example: "single line example"
198+
199+
# integer with default (should not be quoted in output)
200+
intWithDefault:
201+
type: integer
202+
format: int32
203+
description: "integer property, default should not be quoted"
204+
default: 42
205+
206+
# boolean with default
207+
boolWithDefault:
208+
type: boolean
209+
description: "boolean property"
210+
default: false
211+
212+
# nested object with $ properties
213+
details$Info:
214+
type: object
215+
description: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
216+
properties:
217+
detail$One:
218+
type: string
219+
description: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
220+
example: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
221+
default: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some; CC=*/; TQ=\"\"\""
222+
detail$Two:
223+
type: integer
224+
description: "SQ=\"; SBS=\\; DBS=\\\\; SD=$some"
225+
example: 42

0 commit comments

Comments
 (0)