assign "val" for unit test in kotlin - kotlin

I am writing unit tests in kotlin, for this purpose I need to assign value on a "val", here is the simplified version of the code:
#Entity
#Table(name = "Request")
data class Request(
#Column(name = "Name")
val name: String,
) {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
var id: Long? = null
#CreationTimestamp
#Column(name = "Created")
val created: LocalDateTime = LocalDateTime.now()
}
#Test
fun `test one`() {
val name = RandomStringUtils.randomNumeric(10)
val id = Random.nextLong(100)
val created = LocalDateTime.now().minusHours(48)
val request = Request(playerUid = playerUid).apply {
id = id
created = created
}
}
it has an compile error when assigning "created" in the test. How should I manage this unit test since I need to set my desire "created" value? (I can not touch any part of the "Request class")

If you cannot change any part of the Request class then you will not be able to change created.
You will either need to test created by using an approximate test range (created needs to be 0<now<2s sort of thing)
It is a design flaw to encode a static accessor to functions like LocalDateTime.now() - this should be set externally in a service class. If you really cannot do that, then here is another hacky approach:
add a CLOCK object somewhere (does not need to be in a companion object) but ultimately you have to change the created assignment:
#Entity
#Table(name = "Request")
data class Request(
#Column(name = "Name")
val name: String,
) {
companion object {
/** used for carrying a Clock only in the case of tests **/
val CLOCK = ThreadLocal<Clock>()
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
var id: Long? = null
#CreationTimestamp
#Column(name = "Created")
val created: LocalDateTime = LocalDateTime.now(CLOCK.get() ?: Clock.systemUTC())
}
Normally you don't touch that CLOCK but in the Unit Tests you define a
private val fixedClock = Clock.fixed(Instant.parse("2022-08-29T08:20:50Z"), ZoneOffset.UTC)
then you need
#BeforeEach
fun beforeEach() {
// this is necessary because the serialization of MemberMentorCommon.weeksOnPlan uses a clock
CLOCK.getOrSet { fixedClock }
}
#AfterEach
fun afterEach() {
CLOCK.remove()
}
Yes, ThreadLocals are nasty, but this allows you to change the behaviour of the Request class to override the now() function

Related

net.corda.nodeapi.internal.persistence.CouldNotCreateDataSourceException: Could not create the DataSource

I'm trying to develop a CorDapp using the example here. I've added two modules into my project, one for contracts and the other one for flows. I've added test cases for my contract and it works fine, but test cases for flow fail on setup stage. Here's the code of my test class
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
class SharedInformationFlowTests {
private lateinit var network: MockNetwork
private lateinit var a: StartedMockNode
private val proposal = LedgerUpdateProposal.testProposal()
#BeforeAll
fun setup() {
val params = MockNetworkParameters(cordappsForAllNodes = listOf(
TestCordapp.findCordapp("com.something.contract")
))
network = MockNetwork(params) //fails here
a = network.createPartyNode()
network.runNetwork()
}
#AfterAll
fun tearDown() {
network.stopNodes()
}
And here are the error messages I get:
[WARN] 13:42:52,620 [main] spi.SqlExceptionHelper. - SQL Error: 0, SQLState: null {changeSet=migration/vault-schema.changelog-v9.xml::update-vault-states::R3.Corda, databaseChangeLog=master.changelog.json}
[ERROR] 13:42:52,620 [main] spi.SqlExceptionHelper. - Connection is closed {changeSet=migration/vault-schema.changelog-v9.xml::update-vault-states::R3.Corda, databaseChangeLog=master.changelog.json}
net.corda.nodeapi.internal.persistence.CouldNotCreateDataSourceException: Could not create the
DataSource: Migration failed for change set migration/vault-schema.changelog-v9.xml::update-vault-states::R3.Corda:
Reason: net.corda.nodeapi.internal.persistence.HibernateConfigException: Could not create Hibernate configuration: Unable to open JDBC Connection for DDL execution
I think there's something wrong with the liquibase. I've tried adding changelog file to my resources/migration directory as advised here, but it doesn't seem to have any effect. Please help.
UPDATE Added schema
/**
* The family of com.sentinel.schemas for SharingInformationState.
*/
object SharingInformationSchema
/**
* An SharingInformationState schema.
*/
object SharingInformationSchemaV1 : MappedSchema(
schemaFamily = SharingInformationSchema.javaClass,
version = 1,
mappedTypes = listOf(PersistentSharingInformation::class.java)) {
override val migrationResource: String? = "sharing-information-schema-v1.changelog-master.xml"
#Entity
#Table(name = "persistent_sharing_information")
class PersistentSharingInformation(
#Column(name = "owner_id")
var dataOwnerId: Long,
#Column(name = "buyer_id")
var dataBuyerId: Long,
#Column(name = "start_date")
val startDate: String,
#Column(name = "end_date")
val endDate: String,
#Column(name = "shared_fields")
val sharedFieldsIds: String,
#Column(name = "agreement_status")
val agreementStatus: String,
#Column(name = "contract_type")
val contractType: String,
#Column(name = "linear_id")
var linearId: UUID
) : PersistentState() {
// Default constructor required by hibernate.
constructor() : this(0L, 0L,
"", "", "[]", "", "", UUID.randomUUID())
}
}
#BelongsToContract(com.package.contract.SharingInformationContract::class)
class SharingInformationState(val ourParty: Party,
val proposal: LedgerUpdateProposal,
override val linearId: UniqueIdentifier = UniqueIdentifier()) : LinearState, QueryableState {
override val participants: List<AbstractParty> = listOf(ourParty)
override fun generateMappedObject(schema: MappedSchema): PersistentState {
return when (schema) {
SharingInformationSchemaV1 -> SharingInformationSchemaV1.PersistentSharingInformation(
proposal.ownerId,
proposal.buyerId,
proposal.startDate,
proposal.endDate,
proposal.sharedFieldsIds.toString(),
proposal.agreementStatus.name,
proposal.contractType.name,
linearId.id
)
else -> throw IllegalArgumentException("Unrecognised schema $schema")
}
}
override fun supportedSchemas(): Iterable<MappedSchema> = listOf(SharingInformationSchemaV1)
}
#CordaSerializable
enum class AgreementStatus { APPROVED, REJECTED }
#CordaSerializable
enum class ContractType { CORPORATE, CONSUMER, MARKETING, BINDING }

Use Kotlin's data class in service-proxy of Vert.x

I'm trying to pass data class to the service-proxy of Vert.x like this:
data class Entity(val field: String)
#ProxyGen
#VertxGen
public interface DatabaseService {
DatabaseService createEntity(Entity entity, Handler<AsyncResult<Void>> resultHandler);
}
However, the service-proxy requires a DataObject as the parameter type.
Below are what I've tried so far.
First, I rewrite the data class as:
#DataObject
data class Entity(val field: String) {
constructor(json: JsonObject) : this(
json.getString("field")
)
fun toJson(): JsonObject = JsonObject.mapFrom(this)
}
Although this works, the code is redundant, so I tried the kapt with the following generator:
override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {
roundEnv.getElementsAnnotatedWith(ProxyDataObject::class.java).forEach { el ->
val className = el.simpleName.toString()
val pack = processingEnv.elementUtils.getPackageOf(el).toString()
val filename = "Proxy$className"
val classBuilder = TypeSpec.classBuilder(filename)
val primaryConstructorBuilder = FunSpec.constructorBuilder()
val secondaryConstructorBuilder = FunSpec.constructorBuilder().addParameter("json", JsonObject::class)
val secondaryConstructorCodeBlocks = mutableListOf<CodeBlock>()
el.enclosedElements.forEach {
if (it.kind == ElementKind.FIELD) {
val name = it.simpleName.toString()
val kClass = getClass(it) // get the corresponding Kotlin class
val jsonTypeName = getJsonTypeName(it) // get the corresponding type name in methods of JsonObject
classBuilder.addProperty(PropertySpec.builder(name, kClass).initializer(name).build())
primaryConstructorBuilder.addParameter(name, kClass)
secondaryConstructorCodeBlocks.add(CodeBlock.of("json.get$jsonTypeName(\"$name\")"))
}
}
secondaryConstructorBuilder.callThisConstructor(secondaryConstructorCodeBlocks)
classBuilder
.addAnnotation(DataObject::class)
.addModifiers(KModifier.DATA)
.primaryConstructor(primaryConstructorBuilder.build())
.addFunction(secondaryConstructorBuilder.build())
.addFunction(
FunSpec.builder("toJson").returns(JsonObject::class).addStatement("return JsonObject.mapFrom(this)").build()
)
val generatedFile = FileSpec.builder(pack, filename).addType(classBuilder.build()).build()
generatedFile.writeTo(processingEnv.filer)
}
return true
}
Then I can get the correct generated file by simply writing the original data class, but when I execute the building after cleaning, I still get the following error:
Could not generate model for DatabaseService#createEntity(ProxyEntity,io.vertx.core.Handler<io.vertx.core.AsyncResult<java.lang.Void>>): type ProxyEntity is not legal for use for a parameter in proxy
It seems that the generated annotation #DataObject is not processed.
So what should I do? Is there a better solution?

Get HashMap<Model1, List<Model2>> in an MVVM+Repostiory+LiveData setting?

So I was working on this silly little app for practicing MVVM and Repository Pattern. I have two model classes at the moment. They are Category and SubCategory for which I have defined the following data classes:
#Entity(tableName = "categories")
data class Category(
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "id")
val id: Int,
#ColumnInfo(name = "name")
val name: String
) {
}
And
/**
* One to many Relationship from Category to SubCategory
*/
#Entity(
tableName = "sub_categories", foreignKeys = arrayOf(
ForeignKey(
entity = Category::class,
parentColumns = arrayOf("id"),
childColumns = arrayOf("category_id")
)
)
)
data class SubCategory(
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "id")
val id: Int,
#ColumnInfo(name = "name")
val name: String,
#ColumnInfo(name = "category_id")
val categoryId: Int
) {
}
As you can see, I have modeled the resources such that we will need categoryId to be passed to get SubCategories related to a Category.
Now I am pretty new with this MVVM and LiveData and Repository Pattern.
My Problem is that I am using an ExpandableListView to populate SubCategories under Categories and the Adapter for it requires a HashMap<Category, List<SubCategory> for it to display the expandable listview.
So my question is how do I get a HashMap<Category, List<SubCategory> from my database using an approach of db->dao->repository->viewmodel and wherever the adpater goes.
I suppose creating a separate repository like CategorySubCategoryRespository whereby I can do something like following is not going to help?:
class CategorySubCategoryRepository(
private val categoryDao: CategoryDao,
private val subCategoryDao: SubCategoryDao
) {
val allCategoriesSubCategories: LiveData<HashMap<Category, List<SubCategory>>>
get() {
val hashMap: HashMap<Category, List<SubCategory>> = hashMapOf()
for (category in categoryDao.getList()) {
hashMap[category] = subCategoryDao.getSubCategoriesListForCategory(category.id)
}
return hashMap
}
}
}
PS: I think I want to use LiveData wherever possible
So what I ended up doing was, in my CategorySubcategoryRepository I constructed the Hashmap from the CategoryDao and SubcategoryDao as follows:
class CategorySubCategoryRepository(
private val categoryDao: CategoryDao,
private val subCategoryDao: SubCategoryDao
) {
fun getHashMap(): LiveData<HashMap<Category, List<SubCategory>>> {
val data = MutableLiveData<HashMap<Category, List<SubCategory>>>()
val hashMap: HashMap<Category, List<SubCategory>> = hashMapOf()
Executors.newSingleThreadExecutor().execute {
for (category in categoryDao.getList()) {
hashMap[category] = subCategoryDao.getSubCategoriesListForCategory(category.id)
}
}
data.value = hashMap
return data
}
}
Then I used this in my viewmodel's init{} block like:
hashMap = categorySubCategoryRepository.getHashMap()
Then I observed it in my Fragment's onCreateView as:
myViewModel.hashMap.observe(this, Observer {
adapter.setCategoryList(it.keys.toList())
adapter.setCategorySubCategoriesMap(it)
elv_categories.setAdapter(adapter)
adapter.notifyDataSetChanged()
})
Do Comment if this is not the right thing to do. I am doing this only to increase my skills and would love to hear if there's a better way to go about things or if my approach is completely absurd.
Edit:
As per #SanlokLee's comment. The getHashMap function has been changed to:
fun getHashMap(): LiveData<HashMap<Category, List<SubCategory>>> {
val data = MutableLiveData<HashMap<Category, List<SubCategory>>>()
Executors.newSingleThreadExecutor().execute {
val hashMap: HashMap<Category, List<SubCategory>> = hashMapOf()
for (category in categoryDao.getList()) {
hashMap[category] = subCategoryDao.getSubCategoriesListForCategory(category.id)
}
data.postValue(hashMap)
}
return data
}

Kotlin generate constructor that sets default values to nulled arguments

Let's take the class of a data class:
data class User(
val userNumber: Int = -1,
val name: String,
val userGroups; List<String> = emptyList(),
val screenName: String = "new-user"
)
When calling this function from Kotlin, it is pretty straightforward. I can simply use the named-argument syntax to do so. Calling from Java, I have to specify all values, or use the #JvmOverloads annotation, which generates the following constructors (in addition to the constructor that kotlin generates with the bit-mask for default values):
User(int userNumber, #NotNull String name, #NotNull List userGroups,
#NotNull String screenName)
User(int userNumber, #NotNull String name, #NotNull List userGroups)
User(int userNumber, #NotNull String name)
User(#NotNull String name)
Now, if I want to create a User object in Java equivalent to User(name="John Doe", userGroups=listOf("admin", "super") I can't do it with the above constructors. I CAN however do it if I put val userNumber: Int = -1 at the end in the data class declaration (the generation of constructors seems to depend on the order the optional arguments are defined in). Which is fine, because expecting kotlin to generate all permutations is going to heavily bloat some classes.
The biggest problem that tools like Jackson simply don't work as they have no idea which constructor to use (and not like I can annotate one of the generated ones specially).
So, is there a way to generate a (single) constructor like:
User(Integer userNumber, String name, List<String> userGroups, String screenName) {
this.userNumber = (userNumber == null) ? -1 : userNumber;
this.userGroups = (userGroups == null) ? Collections.emptyList() : userGroups;
//...
}
Currently I am using the above approach, but manually defining the constructors where I need them.
EDIT
I should clarify, creating a similar constructor doesn't work, obviously because both the signatures would clash on the JVM. This is what it would like in my case:
data class User(
val userNumber: Int = -1,
val name: String,
val userGroups; List<String> = emptyList(),
val screenName: String = "new-user"
) {
companion object {
#JvmStatic
#JsonCreator
fun constructionSupport(
#JsonProperty("userNumber") userNumber : Int?,
#JsonProperty("name") name : String,
#JsonProperty("userGroups") userGroups : List<String>?,
#JsonProperty("screenName") screenName : String?
) = User(
userNumber = userNumber ?: -1,
name = name,
userGroups = userGroups ?: emptyList(),
screenName = screenName ?: "new-user"
)
}
}
Also note the redundancy where I have to write the default values for the properties twice. I Now that I look at it, I doubt there exists a solution for this. Maybe this is a good use-case for a kapt based side-project of mine :)
Better solution is to add possibility to library understand Kotlin functional. For example, for Jackson exists jackson-module-kotlin. With this library we can use default arguments in data classes.
Example:
data class User(
val userNumber: Int = -1,
val name: String,
val userGroups: List<String> = emptyList(),
val screenName: String = "new-user"
)
fun main(args: Array<String>) {
val objectMapper = ObjectMapper()
.registerModule(KotlinModule())
val testUser = User(userNumber = 5, name = "someName")
val stringUser = objectMapper.writeValueAsString(testUser)
println(stringUser)
val parsedUser = objectMapper.readValue<User>(stringUser)
println(parsedUser)
assert(testUser == parsedUser) {
println("something goes wrong")
}
}
After kicking this around for a minute, I think I found a solution that may work well here. Simply define a top level function in the same source file, that will build the object. Perhaps like so:
fun build_user(userNumber: Int?, name: String, userGroups: List<String>?, screenName: String?) : User {
return User(if(userNumber !== null) userNumber else -1, name, if(userGroups !== null) userGroups else emptyList(),
if(screenName !== null) screenName else "new-user")
}
Then when you need it, you simply call it from Java:
User user = UserKt.build_user(null, "Hello", null, "Porterhouse Steak");
System.out.println(user);
Output from the example:
User(userNumber=-1, name=Hello, userGroups=[], screenName=Porterhouse Steak)
The method is somewhere between a constructor and a builder. It beats hammering out a full-blown Builder object, and avoids cluttering your data class with unnecessary Java-interop glue code messiness.
See Package Level Functions for more information.

Locking down the serialVersionUID in Kotlin

I've been battling the whole morning to lock down the serialVersionUID in a Kotlin class. I have a BaseModel which is extended by Project
abstract class BaseModel<T>(
var id: Int? = null,
private val fileName: String,
private val data: MutableList<T>,
private val indices: MutableMap<Int, T>
) : Serializable {
...
protected fun writeToDisk() {
val oos = ObjectOutputStream(BufferedOutputStream(FileOutputStream(fetchFileName())) )
oos.writeObject(fetchData());
oos.close();
}
}
And the project class:
class Project(
var name: String = "",
var repo: String = ""
) : BaseModel<Project>(
data = Data.projects,
indices = Data.projectsIndex,
fileName = "data/projects.dat"
), Serializable {
...
override fun toString(): String {
return "Project: id=${id}, name=${name}, repo=${repo}"
}
}
Every time I write to Disk and then change anything in the class and try to read it back again, I would get:
java.io.InvalidClassException: com.jvaas.bob.model.Project; local
class incompatible: stream classdesc serialVersionUID =
4156405178259085766, local class serialVersionUID =
2024101567466310467
I've tried adding:
private val serialVersionUID: Long = 1
to all classes with no effect.
Some examples on StackOverflow were using serialVersionUid which had no effect either (I believe this is intelliJ lowercasing the last two letters for some reason)
#JvmStatic doesn't work here since it's not an object, I've tried making it non-private with no success.
You can define serialVersionUID as a constant in a companion object:
abstract class BaseModel<T> : Serializable {
companion object {
private const val serialVersionUID: Long = -1
}
}
Constants are compiled to fields, and fields of a companion are stored as static fields of the class that contains companion. Therefore you get what you need – a private static field serialVersionUID in your serializable class.
The solution was actually much simpler than I thought, use a companion object. This now serializes perfectly and if I add more fields, it still serializes to disk and deserializes unless I change the serialVersionUID
Base:
abstract class BaseModel<T>(
var id: Int? = null,
private val fileName: String,
private val data: MutableList<T>,
private val indices: MutableMap<Int, T>
) : Serializable {
companion object {
#JvmStatic private val serialVersionUID: Long = 1
}
...
}
Project:
class Project(
var name: String = "",
var repo: String = ""
) : BaseModel<Project>(
data = Data.projects,
indices = Data.projectsIndex,
fileName = "data/projects.dat"
), Serializable {
companion object {
#JvmStatic private val serialVersionUID: Long = 1
}
override fun toString(): String {
return "Project: id=${id}, name=${name}, repo=${repo}"
}
}
Install this plugin: GenerateSerialVersionUID,use plugin to auto generate default serial version uid,usage: Click here.