diff --git a/build.gradle.kts b/build.gradle.kts index 2f50622..49d69f2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ buildscript { plugins { java - id("io.spring.dependency-management") version "1.0.11.RELEASE" + id("io.spring.dependency-management") version "1.1.6" } group = "com.github.viclovsky" @@ -18,7 +18,7 @@ val root = rootProject.projectDir val gradleScriptDir by extra("$root/gradle") java { - sourceCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_21 } configure(subprojects) { @@ -31,7 +31,7 @@ configure(subprojects) { configure { imports { - mavenBom("com.fasterxml.jackson:jackson-bom:2.9.8") + mavenBom("com.fasterxml.jackson:jackson-bom:2.17.2") } dependencies { dependency("org.freemarker:freemarker:2.3.31") @@ -43,8 +43,8 @@ configure(subprojects) { dependency("io.swagger.core.v3:swagger-models:2.2.0") dependency("io.swagger.parser.v3:swagger-parser:2.0.32") - dependency("org.slf4j:slf4j-api:1.7.32") - dependency("ch.qos.logback:logback-classic:1.2.10") + dependency("org.slf4j:slf4j-api:2.0.13") + dependency("ch.qos.logback:logback-classic:1.5.6") dependency("com.beust:jcommander:1.81") dependency("junit:junit:4.13.2") @@ -52,18 +52,18 @@ configure(subprojects) { dependency("com.jayway.jsonpath:json-path-assert:2.7.0") dependency("io.rest-assured:rest-assured:4.4.0") dependency("com.github.tomakehurst:wiremock:2.27.2") - dependency("com.fasterxml.jackson.core:jackson-core:2.12.3") - dependency("com.fasterxml.jackson.core:jackson-databind:2.12.3") - dependency("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.12.3") - dependency("com.fasterxml.jackson.core:jackson-annotations:2.12.3") + dependency("com.fasterxml.jackson.core:jackson-core:2.17.2") + dependency("com.fasterxml.jackson.core:jackson-databind:2.17.2") + dependency("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.17.2") + dependency("com.fasterxml.jackson.core:jackson-annotations:2.17.2") dependency("org.springframework:spring-web:5.3.7") - dependency("com.intuit.karate:karate-core:1.2.0.RC1") + dependency("io.karatelabs:karate-core:2.0.0") } } java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 } val sourceJar by tasks.creating(Jar::class) { diff --git a/gradle.properties b/gradle.properties index bd60cdf..8237cf6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=1.0-SNAPSHOT \ No newline at end of file +version=1.5.1-karate-v2 diff --git a/gradle/maven-publish.gradle b/gradle/maven-publish.gradle index 71b1d6e..cf38d13 100644 --- a/gradle/maven-publish.gradle +++ b/gradle/maven-publish.gradle @@ -56,5 +56,6 @@ publishing { } signing { + required { !gradle.taskGraph.hasTask("publishToMavenLocal") && !gradle.taskGraph.allTasks.any { it.name.contains("MavenLocal") } } sign publishing.publications } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 5083229..2617362 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/swagger-coverage-karate/build.gradle.kts b/swagger-coverage-karate/build.gradle.kts index bae3027..788a8b6 100644 --- a/swagger-coverage-karate/build.gradle.kts +++ b/swagger-coverage-karate/build.gradle.kts @@ -14,15 +14,7 @@ dependencies { api(project(":swagger-coverage-commandline")) implementation("io.swagger:swagger-models") implementation("io.swagger.core.v3:swagger-models") - implementation("com.intuit.karate:karate-core") - - //needed for karate runner - implementation("com.linecorp.armeria:armeria:1.14.1") - implementation("io.netty:netty-all:4.1.74.Final") - implementation("org.thymeleaf:thymeleaf:3.0.15.RELEASE") - implementation("io.github.classgraph:classgraph:4.8.108") - implementation("org.antlr:antlr4-runtime:4.9.3") - implementation("org.apache.httpcomponents:httpclient:4.5.13") + implementation("io.karatelabs:karate-core") testImplementation("junit:junit") testImplementation("org.hamcrest:hamcrest") @@ -31,6 +23,6 @@ dependencies { tasks { test { - workingDir(buildDir) + workingDir(layout.buildDirectory.get().asFile) } } \ No newline at end of file diff --git a/swagger-coverage-karate/src/main/java/com/github/viclovsky/swagger/coverage/karate/SwaggerCoverageRunner.java b/swagger-coverage-karate/src/main/java/com/github/viclovsky/swagger/coverage/karate/SwaggerCoverageRunner.java index 4980aa7..c1ab665 100644 --- a/swagger-coverage-karate/src/main/java/com/github/viclovsky/swagger/coverage/karate/SwaggerCoverageRunner.java +++ b/swagger-coverage-karate/src/main/java/com/github/viclovsky/swagger/coverage/karate/SwaggerCoverageRunner.java @@ -6,73 +6,192 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.HashMap; +import java.util.Comparator; import java.util.List; -import java.util.Map; import java.util.NoSuchElementException; import java.util.Optional; import com.github.viclovsky.swagger.coverage.SwaggerCoverageConstants; import com.github.viclovsky.swagger.coverage.core.generator.Generator; -import com.intuit.karate.FileUtils; -import com.intuit.karate.Logger; import com.intuit.karate.Results; import com.intuit.karate.Runner; import com.intuit.karate.core.MockServer; -import com.intuit.karate.job.JobConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -public class SwaggerCoverageRunner extends Runner { - private static final Logger logger = new Logger(); +/** + * Swagger Coverage runner for Karate v2. + * Uses delegation around Runner.Builder (Runner is final in Karate v2). + */ +public class SwaggerCoverageRunner { + + private static final Logger logger = LoggerFactory.getLogger(SwaggerCoverageRunner.class); private static MockServer mockServer; - private static int startProxy(Map args){ + private static int startProxy(boolean oas3, String workingDir) { mockServer = MockServer - .feature("classpath:httpProxy.feature") - .args(args) - .http(0).build(); - + .feature("classpath:httpProxy.feature") + .arg("oas3", oas3) + .arg("workingDir", workingDir) + .http(0).build(); return mockServer.getPort(); } - private static void stopProxy(){ - if (mockServer != null){ + private static void stopProxy() { + if (mockServer != null) { mockServer.stop(); } } - public static class SwaggerCoverageBuilder extends Builder { - final String SPECIFICATION_NAME = "swagger-specification"; - final String CONFIG_NAME = "swagger-coverage-config.json"; + public static class SwaggerCoverageBuilder { + + private static final String SPECIFICATION_NAME = "swagger-specification"; + private static final String CONFIG_NAME = "swagger-coverage-config.json"; + + private final Runner.Builder delegate; + + private boolean oas3; + private URI specificationPath; + private String configPath; + private String coverageDir; + private boolean backupCoverageOutput; + + private SwaggerCoverageBuilder(Runner.Builder delegate) { + this.delegate = delegate; + } + + // ---- Swagger Coverage specific methods ---- + + public SwaggerCoverageBuilder swagger() { + oas3 = false; + return this; + } + + public SwaggerCoverageBuilder oas3() { + oas3 = true; + return this; + } + + public SwaggerCoverageBuilder swaggerSpec(URI path) { + specificationPath = path; + return this; + } + + public SwaggerCoverageBuilder swaggerCoverageConfig(String configPath) { + this.configPath = configPath; + return this; + } + + public SwaggerCoverageBuilder coverageDir(String coverageDir) { + this.coverageDir = coverageDir; + return this; + } + + public SwaggerCoverageBuilder backupCoverageOutput(boolean value) { + this.backupCoverageOutput = value; + return this; + } + + // ---- Delegated Karate Runner.Builder methods ---- + + public SwaggerCoverageBuilder tags(String... tags) { + delegate.tags(tags); + return this; + } + + public SwaggerCoverageBuilder tags(List tags) { + delegate.tags(tags); + return this; + } + + public SwaggerCoverageBuilder karateEnv(String env) { + delegate.karateEnv(env); + return this; + } + + public SwaggerCoverageBuilder reportDir(String dir) { + delegate.reportDir(dir); + return this; + } + + public SwaggerCoverageBuilder outputDir(String dir) { + delegate.outputDir(dir); + return this; + } + + public SwaggerCoverageBuilder backupReportDir(boolean value) { + delegate.backupReportDir(value); + return this; + } - boolean oas3; - URI specificationPath; - String configPath; - String inputPath; - String coverageDir; - boolean backupCoverageOutput; + public SwaggerCoverageBuilder backupOutputDir(boolean value) { + delegate.backupOutputDir(value); + return this; + } - private void prepareTests(){ - if(backupCoverageOutput){ + public SwaggerCoverageBuilder scenarioName(String name) { + delegate.scenarioName(name); + return this; + } + + public SwaggerCoverageBuilder configDir(String dir) { + delegate.configDir(dir); + return this; + } + + public SwaggerCoverageBuilder outputHtmlReport(boolean value) { + delegate.outputHtmlReport(value); + return this; + } + + public SwaggerCoverageBuilder outputCucumberJson(boolean value) { + delegate.outputCucumberJson(value); + return this; + } + + public SwaggerCoverageBuilder systemProperty(String name, String value) { + delegate.systemProperty(name, value); + return this; + } + + // ---- Lifecycle ---- + + public Results parallel(int threadCount) { + prepareTests(); + Results results = delegate.parallel(threadCount); + generateReport(); + stopProxy(); + return results; + } + + private void prepareTests() { + if (backupCoverageOutput) { backupCoverageOutput(); } File outputDir = new File(coverageDir, SwaggerCoverageConstants.OUTPUT_DIRECTORY); - if (outputDir.exists()) FileUtils.deleteDirectory(outputDir); + if (outputDir.exists()) { + try { + Files.walk(outputDir.toPath()) + .sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } catch (IOException e) { + logger.warn("Failed to clean coverage output dir: {}", e.getMessage()); + } + } - Map args = new HashMap(); - args.put("oas3", oas3); - args.put("workingDir", coverageDir); - int proxyPort = startProxy(args); - systemProperty("proxy.port", proxyPort + ""); + int proxyPort = startProxy(oas3, coverageDir); + delegate.systemProperty("proxy.port", String.valueOf(proxyPort)); logger.info("Started proxy at port: {}", proxyPort); } - - private void backupCoverageOutput(){ + + private void backupCoverageOutput() { File file = new File(coverageDir, SwaggerCoverageConstants.OUTPUT_DIRECTORY); if (file.exists()) { File newDir = new File(coverageDir, SwaggerCoverageConstants.OUTPUT_DIRECTORY + "_" + System.currentTimeMillis()); - if (file.renameTo(newDir)){ + if (file.renameTo(newDir)) { logger.info("backed up existing swagger-coverage-output dir to: {}", newDir); } else { logger.warn("failed to backup existing swagger-coverage-output dir: {}", file); @@ -85,108 +204,52 @@ private void generateReport() { Path inputPath = Paths.get(coverageDir, SwaggerCoverageConstants.OUTPUT_DIRECTORY); - if (!inputPath.toFile().exists()){ + if (!inputPath.toFile().exists()) { try { Files.createDirectory(inputPath); } catch (IOException e) { - e.printStackTrace(); logger.warn("Failed to create empty directory for coverage input: {}. Report could not be generated.", inputPath); return; } } - Generator generator = new Generator() - .setInputPath(inputPath); - + Generator generator = new Generator().setInputPath(inputPath); + if (specificationPath != null) { generator.setSpecPath(specificationPath); - } - else{ + } else { File specFile = Optional.of(Paths.get(coverageDir, SPECIFICATION_NAME + ".json").toFile()) - .filter((file) -> file.exists()) - .orElseGet(()-> Paths.get(coverageDir, SPECIFICATION_NAME + ".yaml").toFile()); - - if (!specFile.exists()){ - throw new NoSuchElementException(); - } + .filter(File::exists) + .orElseGet(() -> Paths.get(coverageDir, SPECIFICATION_NAME + ".yaml").toFile()); - generator.setSpecPath(specFile.toURI()); + if (!specFile.exists()) { + throw new NoSuchElementException("Swagger specification file not found in: " + coverageDir); + } + generator.setSpecPath(specFile.toURI()); } - if (configPath != null){ + if (configPath != null) { generator.setConfigurationPath(Paths.get(configPath)); - } - else{ + } else { File configFile = Paths.get(coverageDir, CONFIG_NAME).toFile(); - if (configFile.exists()){ + if (configFile.exists()) { generator.setConfigurationPath(configFile.toPath()); } } generator.run(); } - - public SwaggerCoverageBuilder swagger(){ - oas3 = false; - return this; - } - - public SwaggerCoverageBuilder oas3(){ - oas3 = true; - return this; - } - - public SwaggerCoverageBuilder swaggerSpec(URI path){ - specificationPath = path; - return this; - } - - public SwaggerCoverageBuilder swaggerCoverageConfig(String configPath){ - this.configPath = configPath; - return this; - } - - public SwaggerCoverageBuilder coverageDir(String coverageDir){ - this.coverageDir = coverageDir; - return this; - } - - public SwaggerCoverageBuilder backupCoverageOutput(boolean value){ - this.backupCoverageOutput = value; - return this; - } - - @Override - public Results parallel(int threadCount){ - prepareTests(); - Results results = super.parallel(threadCount); - generateReport(); - stopProxy(); - return results; - } - - @Override - public Results jobManager(JobConfig value){ - prepareTests(); - Results results = super.jobManager(value); - generateReport(); - stopProxy(); - return results; - } } - public static SwaggerCoverageBuilder path(String... paths) { - SwaggerCoverageBuilder builder = new SwaggerCoverageBuilder(); - return builder.path(paths); + return new SwaggerCoverageBuilder(Runner.path(paths)); } public static SwaggerCoverageBuilder path(List paths) { - SwaggerCoverageBuilder builder = new SwaggerCoverageBuilder(); - return builder.path(paths); + return new SwaggerCoverageBuilder(Runner.path(paths)); } public static SwaggerCoverageBuilder builder() { - return new SwaggerCoverageBuilder(); + return new SwaggerCoverageBuilder(Runner.builder()); } }