diff --git a/pom.xml b/pom.xml index 1f88371..cb50222 100644 --- a/pom.xml +++ b/pom.xml @@ -57,6 +57,19 @@ slf4j-simple 1.7.30 + + + org.junit.jupiter + junit-jupiter + 5.7.0 + test + + + org.assertj + assertj-core + 3.17.2 + test + diff --git a/src/main/kotlin/starter/Models.kt b/src/main/kotlin/starter/Models.kt index ae9e410..b554376 100644 --- a/src/main/kotlin/starter/Models.kt +++ b/src/main/kotlin/starter/Models.kt @@ -12,13 +12,12 @@ data class Dependency( val name: String, val groupId: String, val artifactId: String, - val version: String, + val version: Version, val default: Boolean, val category: Category, val scope: Scope, val logger: String?, - val repository: String?, - val versionKey: String, + val repository: Repository?, ) data class Repository(val name: String, val url: String) @@ -30,7 +29,7 @@ data class Project( val basePackage: String, val inputs: List, val dependencies: List, - val repositories: List, + val repositories: Collection, ) data class Version(val name: String, val value: String) diff --git a/src/main/kotlin/starter/config/Config.kt b/src/main/kotlin/starter/config/Config.kt index a5f96e6..e7aa557 100644 --- a/src/main/kotlin/starter/config/Config.kt +++ b/src/main/kotlin/starter/config/Config.kt @@ -8,7 +8,6 @@ import com.electronwill.nightconfig.core.Config as NightConfig data class StarterConfig( val dependencies: List, val inputs: List, - val repositories: List, ) class Config { @@ -23,22 +22,29 @@ class Config { @Suppress("UNCHECKED_CAST") val versions = cfg.get("versions").valueMap() as Map + val repositories = cfg.configMap("repositories") + .map { (name, values) -> + Repository(name, values["url"]) + } + val dependencies = cfg.configMap("dependencies") .map { (name, values) -> val versionKey: String = values["version"] ?: name val version = versions[versionKey] ?: error("Missing version for $name") + val repositoryName: String? = values["repository"] + val repo = repositoryName?.let { repoName -> repositories.find { it.name == repoName } } + Dependency( - name, - values["groupId"], - values["artifactId"], - version, - values.getOrElse("default", false), - values.getEnumOrElse("category", Category.Other), - values.getEnumOrElse("scope", Scope.Compile), - values["logger"], - values["repository"], - versionKey + name = name, + groupId = values["groupId"], + artifactId = values["artifactId"], + version = Version(versionKey, version), + default = values.getOrElse("default", false), + category = values.getEnumOrElse("category", Category.Other), + scope = values.getEnumOrElse("scope", Scope.Compile), + logger = values["logger"], + repository = repo, ) } @@ -47,11 +53,6 @@ class Config { Input(name, values["display"], values["default"]) } - val repositories = cfg.configMap("repositories") - .map { (name, values) -> - Repository(name, values["url"]) - } - - return StarterConfig(dependencies, inputs, repositories) + return StarterConfig(dependencies, inputs) } } diff --git a/src/main/kotlin/starter/modules/PebbleModule.kt b/src/main/kotlin/starter/modules/PebbleModule.kt index c8cc9f4..aa2950d 100644 --- a/src/main/kotlin/starter/modules/PebbleModule.kt +++ b/src/main/kotlin/starter/modules/PebbleModule.kt @@ -37,7 +37,7 @@ class DepAsXmlPebbleFunction : PebbleFunction { |${startTag("dependency")} | ${tag("groupId", dep.groupId)} | ${tag("artifactId", dep.artifactId)} - | ${tag("version", dep.version)} + | ${tag("version", dep.version.value)} |${endTag("dependency")} """.trimMargin() diff --git a/src/main/kotlin/starter/routes/ZipRouteSupplier.kt b/src/main/kotlin/starter/routes/ZipRouteSupplier.kt index 2d5f35e..5978b54 100644 --- a/src/main/kotlin/starter/routes/ZipRouteSupplier.kt +++ b/src/main/kotlin/starter/routes/ZipRouteSupplier.kt @@ -38,9 +38,7 @@ class ZipRouteSupplier( if (basePackage.contains("/") || basePackage.contains("..")) { Response.badRequest() } else { - val repositories = conf.repositories - .filter { repo -> repo.name in deps.mapNotNull { it.repository } } - + val repositories = deps.mapNotNull { it.repository }.toSet() val project = Project(projectName, basePackage, inputs, deps, repositories) val outputStream = projectZip.createZip(project) diff --git a/src/main/kotlin/starter/templates/PomTemplate.kt b/src/main/kotlin/starter/templates/PomTemplate.kt index a0f56ca..b33a444 100644 --- a/src/main/kotlin/starter/templates/PomTemplate.kt +++ b/src/main/kotlin/starter/templates/PomTemplate.kt @@ -14,7 +14,7 @@ class PomTemplate(private val engine: PebbleEngine) : Template { "dependencies" to project.dependencies.sortedBy { it.scope }, "repositories" to project.repositories, "kotlinxSerialization" to project.dependencies.any { it.name == "kotlinx-serialization" }, - "versions" to project.dependencies.map { Version(name = it.versionKey, value = it.version) }.toSet() + "versions" to project.dependencies.map { it.version }.toSet() ) project.inputs.forEach { diff --git a/src/main/resources/starter/pom/@dependencies.twig b/src/main/resources/starter/pom/@dependencies.twig index cbc8ebe..12478ef 100644 --- a/src/main/resources/starter/pom/@dependencies.twig +++ b/src/main/resources/starter/pom/@dependencies.twig @@ -8,7 +8,7 @@ {{ dep.groupId }} {{ dep.artifactId }} - {{ "${" }}{{ dep.versionKey }}{{ ".version}" }} + {{ "${" }}{{ dep.version.name }}{{ ".version}" }} {% if dep.scope == "Test" %} test {% endif %} diff --git a/src/test/kotlin/starter/templates/PomTemplateTest.kt b/src/test/kotlin/starter/templates/PomTemplateTest.kt new file mode 100644 index 0000000..88fbd8b --- /dev/null +++ b/src/test/kotlin/starter/templates/PomTemplateTest.kt @@ -0,0 +1,121 @@ +package starter.templates + +import org.assertj.core.api.Assertions.assertThat +import org.intellij.lang.annotations.Language +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance +import org.koin.dsl.koinApplication +import org.w3c.dom.Node +import org.w3c.dom.NodeList +import starter.Project +import starter.config.StarterConfig +import starter.modules.mainModule +import starter.modules.pebbleModule +import starter.modules.templateModule +import javax.xml.parsers.DocumentBuilderFactory +import javax.xml.xpath.XPathConstants +import javax.xml.xpath.XPathFactory + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +internal class PomTemplateTest { + + private val koin = koinApplication { + modules(mainModule, pebbleModule, templateModule) + }.koin + + private val pomTemplate = koin.get() + private val conf = koin.get() + + private val project = Project( + name = "Test", + basePackage = "org.test", + inputs = conf.inputs, + dependencies = conf.dependencies, + repositories = conf.dependencies.mapNotNull { it.repository }.toSet() + ) + + private val docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder() + private val xPath = XPathFactory.newInstance().newXPath() + + // region xml utils + private fun String.extract(expression: String): String { + val doc = docBuilder.parse(this.byteInputStream()) + xPath.compile(expression).evaluate(doc, XPathConstants.NODESET) + return xPath.compile(expression).evaluate(doc) + } + + private fun String.extractAll(expression: String, mapper: (Node) -> T): List { + val doc = docBuilder.parse(this.byteInputStream()) + val res = xPath.compile(expression).evaluate(doc, XPathConstants.NODESET) as NodeList + return res.asList().map(mapper) + } + + private fun NodeList.asList(): List = (0 until length).map { item(it) } + // endregion + + @Test + fun javaVersion() { + val xml = pomTemplate.render(project) + assertThat(xml.extract("/project/properties/java.version")) + .isEqualTo(project.inputs.find { it.name == "javaVersion" }?.value) + + println(xml) + } + + @Test + fun dependencies() { + val xml = pomTemplate.render(project) + + val deps = xml.extractAll("/project/dependencies/dependency") { + val map = it.childNodes.asList() + .filter { it.nodeType == Node.ELEMENT_NODE } + .associate { it.nodeName to it.firstChild.nodeValue } + + Triple(map["groupId"]!!, map["artifactId"]!!, map["version"]!!) + + }.filterNot { it.second == "kotlin-stdlib-jdk8" } + + println(deps.joinToString("\n")) + + val expectedDependencies = project.dependencies + .map { Triple(it.groupId, it.artifactId, "\${" + it.version.name + ".version}") } + + assertThat(expectedDependencies).containsExactlyInAnyOrderElementsOf(deps) + } + + @Test + fun versions() { + val xml = pomTemplate.render(project) + + val versions = xml.extractAll("/project/properties") { + it.childNodes.asList() + .filter { it.nodeType == Node.ELEMENT_NODE } + .filter { it.nodeName.endsWith(".version") } + .filterNot { it.nodeName in listOf("java.version", "kotlin.version") } + .associate { it.nodeName.substringBefore(".version") to it.firstChild.nodeValue } + }.first() + + val expected = project.dependencies.associate { it.version.name to it.version.value } + + assertThat(versions).containsExactlyInAnyOrderEntriesOf(expected) + + println(versions) + } + + @Test + fun kotlinxSerialization() { + val xml = pomTemplate.render(project) + + @Language("XPath") + val kotlinMavenPlugin = "/project/build/plugins/plugin[artifactId='kotlin-maven-plugin']" + + val kotlinxPlugin = "$kotlinMavenPlugin/configuration/compilerPlugins/plugin" + assertThat(xml.extract(kotlinxPlugin)) + .isEqualTo("kotlinx-serialization") + + val kotlinxPluginDep = "$kotlinMavenPlugin/dependencies/dependency/artifactId" + assertThat(xml.extract(kotlinxPluginDep)) + .isEqualTo("kotlin-maven-serialization") + + } +}