diff --git a/src/test/kotlin/ch/icken/processor/PanacheEntityBaseProcessorCompileTests.kt b/src/test/kotlin/ch/icken/processor/PanacheEntityBaseProcessorCompileTests.kt new file mode 100644 index 0000000..8e35537 --- /dev/null +++ b/src/test/kotlin/ch/icken/processor/PanacheEntityBaseProcessorCompileTests.kt @@ -0,0 +1,197 @@ +/* + * Copyright 2024 Thijs Koppen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.icken.processor + +import com.tschuchort.compiletesting.SourceFile.Companion.kotlin +import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi +import org.junit.jupiter.api.Test + +@OptIn(ExperimentalCompilerApi::class) +class PanacheEntityBaseProcessorCompileTests : ProcessorCompileTestCommon() { + + @Test + fun testInterface() { + + // Given + val compilation = kotlinCompilation("NotAClass.kt", """ + import jakarta.persistence.Entity + + @Entity + interface NotAClass + """) + + // When + val result = compilation.compile() + + // Then + result.assertOk() + } + + @Test + fun testNotPanacheEntity() { + + // Given + val compilation = kotlinCompilation("Department.kt", """ + import jakarta.persistence.Entity + + @Entity + class Department + """) + + // When + val result = compilation.compile() + + // Then + result.assertOk() + } + + @Test + fun testPanacheEntityBaseWithoutProperties() { + + // Given + val compilation = kotlinCompilation("Employee.kt", """ + import io.quarkus.hibernate.orm.panache.kotlin.PanacheEntityBase + import jakarta.persistence.Entity + + @Entity + class Employee : PanacheEntityBase + """) + + // When + val result = compilation.compile() + + // Then + result.assertOk() + compilation.assertFileGenerated("EmployeeColumns.kt") + + val employeeColumns = result.loadGeneratedClass("EmployeeColumns") + employeeColumns.assertNumberOfProperties(0) + } + + @Test + fun testPanacheEntityWithTransientProperty() { + + // Given + val compilation = kotlinCompilation("Product.kt", """ + import io.quarkus.hibernate.orm.panache.kotlin.PanacheEntityBase + import jakarta.persistence.Entity + import jakarta.persistence.Transient + + @Entity + class Product( + + @Transient + var name: String + + ) : PanacheEntityBase + """) + + // When + val result = compilation.compile() + + // Then + result.assertOk() + compilation.assertFileGenerated("ProductColumns.kt") + + val productColumns = result.loadGeneratedClass("ProductColumns") + productColumns.assertNumberOfProperties(0) + } + + @Test + fun testPanacheEntitiesWithJoinColumnAndMappedProperty() { + + // Given + val compilation = compilation( + kotlin("Post.kt", """ + import io.quarkus.hibernate.orm.panache.kotlin.PanacheEntity + import jakarta.persistence.Entity + import jakarta.persistence.OneToMany + + @Entity + class Post( + + @OneToMany(mappedBy = "post") + val comments: MutableList = mutableListOf() + + ) : PanacheEntity() + """), + kotlin("Comment.kt", """ + import io.quarkus.hibernate.orm.panache.kotlin.PanacheEntity + import jakarta.persistence.Entity + import jakarta.persistence.JoinColumn + import jakarta.persistence.ManyToOne + + @Entity + class Comment( + + @JoinColumn(name = "post_id") + @ManyToOne(optional = false) + val post: Post + + ) : PanacheEntity() + """) + ) + + // When + val result = compilation.compile() + + // Then + result.assertOk() + compilation.assertFileGenerated("PostColumns.kt") + compilation.assertFileGenerated("CommentColumns.kt") + + val postColumns = result.loadGeneratedClass("PostColumns") + postColumns.assertNumberOfProperties(1) + postColumns.assertHasColumnOfType("id", Long::class) + + val commentColumns = result.loadGeneratedClass("CommentColumns") + commentColumns.assertNumberOfProperties(2) + commentColumns.assertHasColumnOfType("id", Long::class) + commentColumns.assertHasPropertyOfType("post", "PostColumnsBase") + } + + @Test + fun testPanacheEntityWithColumnTypeAnnotation() { + + // Given + val compilation = kotlinCompilation("User.kt", """ + import ch.icken.processor.ColumnType + import io.quarkus.hibernate.orm.panache.kotlin.PanacheEntity + import jakarta.persistence.Entity + + @Entity + class User( + + @ColumnType(CharSequence::class) + var username: String + + ) : PanacheEntity() + """) + + // When + val result = compilation.compile() + + // Then + result.assertOk() + compilation.assertFileGenerated("UserColumns.kt") + + val userColumns = result.loadGeneratedClass("UserColumns") + userColumns.assertNumberOfProperties(2) + userColumns.assertHasColumnOfType("id", Long::class) + userColumns.assertHasColumnOfType("username", CharSequence::class) + } +} diff --git a/src/test/kotlin/ch/icken/processor/PanacheEntityBaseProcessorMockTests.kt b/src/test/kotlin/ch/icken/processor/PanacheEntityBaseProcessorMockTests.kt index a094049..45c639b 100644 --- a/src/test/kotlin/ch/icken/processor/PanacheEntityBaseProcessorMockTests.kt +++ b/src/test/kotlin/ch/icken/processor/PanacheEntityBaseProcessorMockTests.kt @@ -253,12 +253,9 @@ class PanacheEntityBaseProcessorMockTests : ProcessorMockTestCommon() { val lastNameColumnTypeParameterName = mockk() every { lastNameColumnTypeParameterName.asString() } returns TYPE - val lastnameColumnTypeParameterValue = mockk() - every { lastnameColumnTypeParameterValue.toClassName() } returns StringClassName - val lastNameColumnTypeParameter = mockk() every { lastNameColumnTypeParameter.name } returns lastNameColumnTypeParameterName - every { lastNameColumnTypeParameter.value } returns lastnameColumnTypeParameterValue + every { lastNameColumnTypeParameter.value } returns null val lastNameColumnType = mockk() every { lastNameColumnType.arguments } returns listOf(lastNameColumnTypeParameter) diff --git a/src/test/kotlin/ch/icken/processor/ProcessorCompileTestCommon.kt b/src/test/kotlin/ch/icken/processor/ProcessorCompileTestCommon.kt new file mode 100644 index 0000000..2d447d2 --- /dev/null +++ b/src/test/kotlin/ch/icken/processor/ProcessorCompileTestCommon.kt @@ -0,0 +1,94 @@ +/* + * Copyright 2024 Thijs Koppen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.icken.processor + +import ch.icken.query.Column +import com.tschuchort.compiletesting.* +import org.intellij.lang.annotations.Language +import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi +import org.junit.jupiter.api.Assertions.* +import kotlin.reflect.KClass +import kotlin.reflect.full.memberProperties + +@OptIn(ExperimentalCompilerApi::class) +abstract class ProcessorCompileTestCommon { + + protected fun kotlinResource(resourceName: String, sourceFileName: String = resourceName) = + SourceFile.kotlin(sourceFileName, javaClass.classLoader.getResource(resourceName)?.readText() ?: "") + + protected fun compilation(vararg source: SourceFile) = KotlinCompilation().apply { + inheritClassPath = true + kspWithCompilation = true + sources = listOf(*source) + symbolProcessorProviders = listOf( + PanacheCompanionBaseProcessorProvider(), + PanacheEntityBaseProcessorProvider() + ) + verbose = false + } + + protected fun kotlinCompilation(sourceFileName: String, @Language("kotlin") contents: String) = + compilation(SourceFile.kotlin(sourceFileName, contents)) + + protected fun KotlinCompilation.Result.assertOk() = assertEquals(KotlinCompilation.ExitCode.OK, exitCode) + + protected fun KotlinCompilation.assertFileGenerated(fileName: String, outputDir: String = "generated") = + assertTrue(kspSourcesDir.resolve("kotlin").resolve(outputDir).resolve(fileName).isFile) + + protected fun KotlinCompilation.Result.loadGeneratedClass(className: String, outputPackage: String = "generated") = + this.classLoader.loadClass("$outputPackage.$className").kotlin + + protected fun KClass<*>.assertNumberOfProperties(expectedNumberOfProperties: Int) { + assertEquals(expectedNumberOfProperties, memberProperties.size) + } + + protected fun KClass<*>.assertHasPropertyOfType(propertyName: String, expectedTypeName: String) { + val property = memberProperties.find { it.name == propertyName } + assertNotNull(property) + + val returnType = property!!.returnType + returnType.classifier.let { classifier -> + assertNotNull(classifier) + assertTrue(classifier is KClass<*>) + assertEquals(expectedTypeName, (classifier as? KClass<*>)?.simpleName) + } + } + + protected fun KClass<*>.assertHasColumnOfType(columnName: String, expectedType: KClass<*>) { + val property = memberProperties.find { it.name == columnName } + assertNotNull(property) + + val returnType = property!!.returnType + returnType.classifier.let { classifier -> + assertNotNull(classifier) + assertTrue(classifier is KClass<*>) + assertEquals(Column::class, classifier) + } + + val arguments = returnType.arguments + assertNotNull(arguments) + assertEquals(1, arguments.size) + + val type = arguments[0].type + assertNotNull(type) + type!!.classifier.let { classifier -> + assertNotNull(classifier) + assertTrue(classifier is KClass<*>) + assertEquals(expectedType, classifier) + } + } +} diff --git a/src/test/kotlin/ch/icken/processor/ProcessorTests.kt b/src/test/kotlin/ch/icken/processor/ProcessorTests.kt deleted file mode 100644 index e90b2e8..0000000 --- a/src/test/kotlin/ch/icken/processor/ProcessorTests.kt +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright 2024 Thijs Koppen - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package ch.icken.processor - -import ch.icken.query.Column -import com.tschuchort.compiletesting.* -import org.intellij.lang.annotations.Language -import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi -import org.junit.jupiter.api.Assertions.* -import org.junit.jupiter.api.Test -import kotlin.reflect.KClass -import kotlin.reflect.full.memberProperties - -@OptIn(ExperimentalCompilerApi::class) -class ProcessorTests { - - //region PanacheEntityBaseProcessor - @Test - fun testInterface() { - - // Given - val compilation = kotlinCompilation("NotAClass.kt", - """ - import jakarta.persistence.Entity - - @Entity - interface NotAClass - """ - ) - - // When - val result = compilation.compile() - - // Then - result.assertOk() - } - - @Test - fun testNotPanacheEntity() { - - // Given - val compilation = kotlinCompilation("Department.kt", - """ - import jakarta.persistence.Entity - - @Entity - class Department - """ - ) - - // When - val result = compilation.compile() - - // Then - result.assertOk() - } - - @Test - fun testPanacheEntityBaseWithoutProperties() { - - // Given - val compilation = kotlinCompilation("Employee.kt", - """ - import io.quarkus.hibernate.orm.panache.kotlin.PanacheEntityBase - import jakarta.persistence.Entity - - @Entity - class Employee : PanacheEntityBase - """ - ) - - // When - val result = compilation.compile() - - // Then - result.assertOk() - compilation.assertFileGenerated("EmployeeColumns.kt") - - val employeeColumns = result.loadGeneratedClass("EmployeeColumns") - employeeColumns.assertHasNoColumns() - } - - @Test - fun testPanacheEntityWithTransientProperty() { - //TODO - } - - @Test - fun testPanacheEntityWithMappedProperty() { - //TODO - } - - @Test - fun testPanacheEntity() { - - // Given - val compilation = kotlinCompilation("User.kt", - """ - import io.quarkus.hibernate.orm.panache.kotlin.PanacheEntity - import jakarta.persistence.Entity - - @Entity - class User : PanacheEntity() - """ - ) - - // When - val result = compilation.compile() - - // Then - result.assertOk() - compilation.assertFileGenerated("UserColumns.kt") - - val userColumns = result.loadGeneratedClass("UserColumns") - userColumns.assertHasColumnOfType("id", Long::class) - } - - @Test - fun testPanacheEntityWithJoinColumn() { - //TODO - } - - @Test - fun testPanacheEntityWithColumnTypeAnnotation() { - //TODO - } - //endregion - - //region PanacheCompanionBaseProcessor - //TODO - //endregion - - //region Utils - private fun kotlinResource(resourceName: String, sourceFileName: String = resourceName) = - SourceFile.kotlin(sourceFileName, javaClass.classLoader.getResource(resourceName)?.readText() ?: "") - - private fun compilation(vararg source: SourceFile) = KotlinCompilation().apply { - inheritClassPath = true - kspWithCompilation = true - sources = listOf(*source) - symbolProcessorProviders = listOf( - PanacheCompanionBaseProcessorProvider(), - PanacheEntityBaseProcessorProvider() - ) - verbose = false - } - - private fun kotlinCompilation(sourceFileName: String, @Language("kotlin") contents: String) = - compilation(SourceFile.kotlin(sourceFileName, contents)) - - private fun KotlinCompilation.Result.assertOk() = assertEquals(KotlinCompilation.ExitCode.OK, exitCode) - - private fun KotlinCompilation.assertFileGenerated(fileName: String, outputDir: String = "generated") = - assertTrue(kspSourcesDir.resolve("kotlin").resolve(outputDir).resolve(fileName).isFile) - - private fun KotlinCompilation.Result.loadGeneratedClass(className: String, outputPackage: String = "generated") = - this.classLoader.loadClass("$outputPackage.$className").kotlin - - private fun KClass<*>.assertHasNoColumns() { - val numberOfColumns = memberProperties - .mapNotNull { it.returnType.classifier } - .filterIsInstance>() - .count { it == Column::class } - assertEquals(0, numberOfColumns) - } - - private fun KClass<*>.assertHasColumnOfType(columnName: String, expectedType: KClass<*>) { - val property = memberProperties.find { it.name == columnName } - assertNotNull(property) - - val returnType = property!!.returnType - returnType.classifier.let { classifier -> - assertNotNull(classifier) - assertTrue(classifier is KClass<*>) - assertEquals(Column::class, classifier) - } - - val arguments = returnType.arguments - assertNotNull(arguments) - assertEquals(1, arguments.size) - - val type = arguments[0].type - assertNotNull(type) - type!!.classifier.let { classifier -> - assertNotNull(classifier) - assertTrue(classifier is KClass<*>) - assertEquals(expectedType, classifier) - } - } - //endregion -}