Skip to content

Commit 186f442

Browse files
committed
Tests FTW
Wanted to make sure all the properties could be set correctly and used so here is a test suite to do so. So to do so I used gradle test kit to be able to run gradle task and inject the current version of the plugin code. And run it with all the different options. Validating as it goes. Some of the tests were not possible without forkProperties so that helped. The strategy for testing is there is a stubbed out spring boot app under src/test/resources. For each test case it will copy that to a temp dir, execute the gradle tasks for the plugin passing different argument and validate the results.
1 parent f4c37a2 commit 186f442

File tree

11 files changed

+321
-0
lines changed

11 files changed

+321
-0
lines changed

build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ dependencies {
4141
implementation(group = "com.google.code.gson", name = "gson", version = "2.8.6")
4242
implementation(group = "org.awaitility", name = "awaitility-kotlin", version = "4.0.2")
4343
implementation(files("$projectDir/libs/gradle-processes-0.5.0.jar"))
44+
45+
testImplementation(gradleTestKit())
46+
testImplementation("junit:junit:4.13")
47+
testImplementation("com.beust:klaxon:5.2")
4448
}
4549

4650
gradlePlugin {
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
package org.springdoc.openapi.gradle.plugin
2+
3+
import com.beust.klaxon.JsonObject
4+
import com.beust.klaxon.Klaxon
5+
import com.beust.klaxon.Parser
6+
import org.gradle.internal.impldep.org.apache.commons.lang.RandomStringUtils
7+
import org.gradle.testkit.runner.BuildResult
8+
import org.gradle.testkit.runner.BuildTask
9+
import org.gradle.testkit.runner.GradleRunner
10+
import org.gradle.testkit.runner.TaskOutcome
11+
import org.gradle.testkit.runner.internal.FeatureCheckBuildResult
12+
import org.junit.Assert.assertEquals
13+
import org.junit.Assert.assertTrue
14+
import org.junit.Before
15+
import org.junit.Rule
16+
import org.junit.Test
17+
import org.junit.rules.TemporaryFolder
18+
import java.io.File
19+
import java.io.FileReader
20+
21+
class OpenApiGradlePluginTest {
22+
@Rule
23+
@JvmField
24+
var testProjectDir: TemporaryFolder = TemporaryFolder()
25+
26+
private lateinit var projectTestDir: File
27+
private lateinit var buildFile: File
28+
private lateinit var projectBuildDir: File
29+
30+
private var baseBuildGradle = """plugins {
31+
id 'org.springframework.boot' version '2.2.0.RELEASE'
32+
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
33+
id 'java'
34+
id "com.github.johnrengelman.processes" version "0.5.0"
35+
id("org.springdoc.openapi-gradle-plugin")
36+
}
37+
38+
group = 'com.example'
39+
version = '0.0.1-SNAPSHOT'
40+
sourceCompatibility = '8'
41+
42+
repositories {
43+
mavenCentral()
44+
}
45+
46+
dependencies {
47+
implementation 'org.springframework.boot:spring-boot-starter-web'
48+
implementation group: 'org.springdoc', name: 'springdoc-openapi-webmvc-core', version: '1.4.0'
49+
}
50+
""".trimIndent()
51+
52+
@Before
53+
fun setup() {
54+
val acceptanceTestProject = File(this.javaClass.classLoader.getResource("acceptance-project")!!.path)
55+
projectTestDir = File(testProjectDir.newFolder(), "acceptence-project")
56+
57+
acceptanceTestProject.copyRecursively(projectTestDir)
58+
buildFile = File(projectTestDir, "build.gradle")
59+
60+
projectBuildDir = File(projectTestDir, "build")
61+
println("!!!!! $projectBuildDir !!!!!!!")
62+
}
63+
64+
@Test
65+
fun `default build no options`() {
66+
buildFile.writeText(baseBuildGradle)
67+
68+
val result = GradleRunner.create()
69+
.withProjectDir(projectTestDir)
70+
.withArguments("clean", "generateOpenApiDocs")
71+
.withPluginClasspath()
72+
.build()
73+
74+
assertEquals(TaskOutcome.SUCCESS, getTaskByName(result, "generateOpenApiDocs")?.outcome)
75+
76+
val openApiJsonFile = File(projectBuildDir, DEFAULT_OPEN_API_FILE_NAME)
77+
assertOpenApiJsonFileIsAsExpected(openApiJsonFile, 1)
78+
}
79+
80+
@Test
81+
fun `different output dir`() {
82+
var specialOutputDir = File(projectTestDir, "specialDir")
83+
specialOutputDir.mkdirs()
84+
85+
buildFile.writeText("""$baseBuildGradle
86+
openApi{
87+
outputDir = file("${specialOutputDir.path}")
88+
}
89+
""".trimMargin())
90+
91+
val result = GradleRunner.create()
92+
.withProjectDir(projectTestDir)
93+
.withArguments("clean", "generateOpenApiDocs")
94+
.withPluginClasspath()
95+
.build()
96+
97+
assertEquals(TaskOutcome.SUCCESS, getTaskByName(result, "generateOpenApiDocs")?.outcome)
98+
99+
val openApiJsonFile = File(specialOutputDir, DEFAULT_OPEN_API_FILE_NAME)
100+
assertOpenApiJsonFileIsAsExpected(openApiJsonFile, 1)
101+
}
102+
103+
@Test
104+
fun `different output file name`() {
105+
var specialOutputJsonFileName = RandomStringUtils.randomAlphanumeric(15)
106+
107+
buildFile.writeText("""$baseBuildGradle
108+
openApi{
109+
outputFileName = "$specialOutputJsonFileName"
110+
}
111+
""".trimMargin())
112+
113+
val result = GradleRunner.create()
114+
.withProjectDir(projectTestDir)
115+
.withArguments("clean", "generateOpenApiDocs")
116+
.withPluginClasspath()
117+
.build()
118+
119+
assertEquals(TaskOutcome.SUCCESS, getTaskByName(result, "generateOpenApiDocs")?.outcome)
120+
121+
val openApiJsonFile = File(projectBuildDir, specialOutputJsonFileName)
122+
assertOpenApiJsonFileIsAsExpected(openApiJsonFile, 1)
123+
}
124+
125+
@Test
126+
fun `using forked properties`() {
127+
buildFile.writeText("""$baseBuildGradle
128+
openApi{
129+
forkProperties = "-Dspring.profiles.active=multiple-endpoints"
130+
}
131+
""".trimMargin())
132+
133+
val result = GradleRunner.create()
134+
.withProjectDir(projectTestDir)
135+
.withArguments("clean", "generateOpenApiDocs")
136+
.withPluginClasspath()
137+
.build()
138+
139+
assertEquals(TaskOutcome.SUCCESS, getTaskByName(result, "generateOpenApiDocs")?.outcome)
140+
141+
val openApiJsonFile = File(projectBuildDir, DEFAULT_OPEN_API_FILE_NAME)
142+
assertOpenApiJsonFileIsAsExpected(openApiJsonFile, 2)
143+
}
144+
145+
@Test
146+
fun `using forked properties via System properties`() {
147+
buildFile.writeText("""$baseBuildGradle
148+
openApi{
149+
forkProperties = System.properties
150+
}
151+
""".trimMargin())
152+
153+
val result = GradleRunner.create()
154+
.withProjectDir(projectTestDir)
155+
.withArguments("clean", "generateOpenApiDocs", "-Dspring.profiles.active=multiple-endpoints")
156+
.withPluginClasspath()
157+
.build()
158+
159+
assertEquals(TaskOutcome.SUCCESS, getTaskByName(result, "generateOpenApiDocs")?.outcome)
160+
161+
val openApiJsonFile = File(projectBuildDir, DEFAULT_OPEN_API_FILE_NAME)
162+
assertOpenApiJsonFileIsAsExpected(openApiJsonFile, 2)
163+
}
164+
165+
@Test
166+
fun `configurable wait time`() {
167+
buildFile.writeText("""$baseBuildGradle
168+
openApi{
169+
forkProperties = "-Dspring.profiles.active=slower"
170+
waitTimeInSeconds = 60
171+
}
172+
""".trimMargin())
173+
174+
val result = GradleRunner.create()
175+
.withProjectDir(projectTestDir)
176+
.withArguments("clean", "generateOpenApiDocs")
177+
.withPluginClasspath()
178+
.build()
179+
180+
assertEquals(TaskOutcome.SUCCESS, getTaskByName(result, "generateOpenApiDocs")?.outcome)
181+
182+
val openApiJsonFile = File(projectBuildDir, DEFAULT_OPEN_API_FILE_NAME)
183+
assertOpenApiJsonFileIsAsExpected(openApiJsonFile, 1)
184+
}
185+
186+
@Test
187+
fun `using different api url`() {
188+
buildFile.writeText("""$baseBuildGradle
189+
openApi{
190+
apiDocsUrl = "http://localhost:8080/secret-api-docs"
191+
forkProperties = "-Dspring.profiles.active=different-url"
192+
}
193+
""".trimMargin())
194+
195+
val result = GradleRunner.create()
196+
.withProjectDir(projectTestDir)
197+
.withArguments("clean", "generateOpenApiDocs")
198+
.withPluginClasspath()
199+
.build()
200+
201+
assertEquals(TaskOutcome.SUCCESS, getTaskByName(result, "generateOpenApiDocs")?.outcome)
202+
203+
val openApiJsonFile = File(projectBuildDir, DEFAULT_OPEN_API_FILE_NAME)
204+
assertOpenApiJsonFileIsAsExpected(openApiJsonFile, 1)
205+
}
206+
207+
private fun assertOpenApiJsonFileIsAsExpected(openApiJsonFile: File, expectedNumberOfPaths: Int) {
208+
val openApiJson = getOpenApiJsonAtLocation(openApiJsonFile)
209+
assertEquals("3.0.1", openApiJson!!.string("openapi"))
210+
assertEquals(expectedNumberOfPaths, openApiJson.obj("paths")!!.size)
211+
}
212+
213+
private fun getOpenApiJsonAtLocation(path: File): JsonObject? {
214+
return Parser.default().parse(FileReader(path)) as JsonObject
215+
}
216+
217+
private fun getTaskByName(result: BuildResult, name: String): BuildTask? {
218+
return result.tasks.find { it.path.contains(name) }
219+
}
220+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
HELP.md
2+
.gradle
3+
build/
4+
!gradle/wrapper/gradle-wrapper.jar
5+
!**/src/main/**
6+
!**/src/test/**
7+
8+
### STS ###
9+
.apt_generated
10+
.classpath
11+
.factorypath
12+
.project
13+
.settings
14+
.springBeans
15+
.sts4-cache
16+
17+
### IntelliJ IDEA ###
18+
.idea
19+
*.iws
20+
*.iml
21+
*.ipr
22+
out/
23+
24+
### NetBeans ###
25+
/nbproject/private/
26+
/nbbuild/
27+
/dist/
28+
/nbdist/
29+
/.nb-gradle/
30+
31+
### VS Code ###
32+
.vscode/
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rootProject.name = 'demo'
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.example.demo;
2+
3+
import org.springframework.beans.factory.annotation.Value;
4+
import org.springframework.boot.SpringApplication;
5+
import org.springframework.boot.autoconfigure.SpringBootApplication;
6+
7+
import javax.annotation.PostConstruct;
8+
import java.time.Duration;
9+
10+
@SpringBootApplication
11+
public class DemoApplication {
12+
13+
@Value("${slower:false}")
14+
boolean slower;
15+
16+
public static void main(String[] args) {
17+
SpringApplication.run(DemoApplication.class, args);
18+
}
19+
20+
@PostConstruct
21+
public void afterBeanStuff() throws InterruptedException {
22+
if(slower) {
23+
Duration waitTime = Duration.ofSeconds(40);
24+
System.out.println("Waiting for " + waitTime + " before starting");
25+
Thread.sleep(waitTime.toMillis());
26+
}
27+
}
28+
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.example.demo.endpoints;
2+
3+
import org.springframework.web.bind.annotation.GetMapping;
4+
import org.springframework.web.bind.annotation.RestController;
5+
6+
@RestController("/hello")
7+
public class HelloWorldController {
8+
9+
@GetMapping("/world")
10+
public String helloWorld(){
11+
return "Hello World!";
12+
}
13+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.example.demo.endpoints;
2+
3+
import org.springframework.beans.factory.annotation.Value;
4+
import org.springframework.context.annotation.Profile;
5+
import org.springframework.web.bind.annotation.GetMapping;
6+
import org.springframework.web.bind.annotation.RestController;
7+
8+
@Profile("multiple-endpoints")
9+
@RestController("/special")
10+
public class ProfileController {
11+
12+
@Value("${test.props}")
13+
String profileRelatedValue;
14+
15+
@GetMapping("/")
16+
public String special(){
17+
return profileRelatedValue;
18+
}
19+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
springdoc.api-docs.path=/secret-api-docs
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
test.props=So very special
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
slower=true

0 commit comments

Comments
 (0)