Test throw case in RxJava 3 - kotlin

I'm trying to test case when my rx chain should throw an exception but facing problem with it. When I'm trying to call assertError(error) it says that no error found. Here's example:
fun isAvailable(): Single<String> {
return mapper.getSymbol().map { symbol ->
if (repository.isAvailable(symbol)) {
symbol
} else {
throw Exception("Symbol is not available")
}
}
}
In test I mock repository to return false and after do something like:
val error = Exception("Symbol is not available")
whenever(mapper.getSymbol()).thenReturn(
Single.just(
symbol
)
)
whenever(repository.isAvailable(symbol)).thenReturn(false)
val test = symbolsRepository.isAvailable().test()
test.await()
.assertError(error)
But when I run test I see
Error not present (latch = 0, values = 0, errors = 1, completions = 0)
Caused by: java.lang.Exception: Symbol is not available

As #gpunto said, assertError does equals check on the Exception and those are not implemented by default beyond reference equality in standard Java.
You could use two other overloads of assertError to check for both the exception class and its message:
test.awaitDone()
.assertError(error.getClass())
.assertError({ it.getMessage().equals(error.getMessage()) })

Related

Javalin 4: How to write properly check in call to Validator.check()?

I am trying to adopt existing code of parameter validation from Javalin 3 to Javalin 4. It uses Javalin's Validator class. Here's the code I've got (rewritten):
val messageId = ctx.pathParamAsClass<String>("messageId")
.check(check = { it.trim().matches(Uuid.REGEX) }, errorMessage = "message id must be a UUID")
.get()
.trim()
And I am getting compile error for the check() call:
e:
/home/ivan/.../SourceFile.kt: (53, 6): None of the following functions can be called with the arguments
supplied:
public final fun check(check: Check<String> /* = (String) -> Boolean */, error: ValidationError<String>):
Validator<String> defined in io.javalin.core.validation.Validator
public final fun check(check: Check<String> /* = (String) -> Boolean */, error: String): Validator<String> defined in
io.javalin.core.validation.Validator
I can't understand why there is an error. I assume I should have matched second overload of check(). How to write it correctly?
Note: I have read Javalin 3 to 4 migration guide, which gives example like this:
ctx.queryParamAsClass<Int>("age")
.check({ it >= 18 }, ValidationError("AGE_TOO_LOW", args = mapOf("minAge" to 18)))
.get()
which I seem to follow, except I give it error message as string, but there's matching overload. So what is wrong here?
The cause was that second parameter of check() is called error, not errorMessage, i.e. correct code is like this:
...
.check(check = { it.trim().matches(Uuid.REGEX) }, error = "...")
...

Change MutableList during Iteration

Ok, so. I'm absolutely stumped by this problem.
I have a function, which sorts a list of objects and gives it back, à la
1, 1.1, 1.2, 1.2.1, 2, 2.2, 3.
fun getFolderHierachy(allFolders: List<FolderEntity>): List<FolderEntity>? {
//puts the entrypoints( 1, 2, 3) in a list
if (allFolders.isNotEmpty()) {
val foldersSorted: MutableList<FolderEntity> = mutableListOf()
allFolders.forEach {
if (it.parentDir == null) {
foldersSorted.add(it)
}
}
// should insert an element, after the matching one ( 1, 1.1, 2, 3)
while (foldersSorted.size != allFolders.size) {
foldersSorted.forEach { parentFolder ->
allFolders.forEach {
if (!foldersSorted.contains(it)) {
if (it.parentDir == parentFolder.uid) {
val index = foldersSorted.indexOf(parentFolder)
foldersSorted.add(index + 1, it)
}
}
}
}
}
println(foldersSorted)
return foldersSorted
}
return null
}
I know should work, because it already delievered me the correct results. However after the last update from my master, it throws this error:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: de.ur.mi.audidroid, PID: 16634
java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.next(ArrayList.java:860)
at de.ur.mi.audidroid.viewmodels.FolderViewModel.getFolderHierachy(FolderViewModel.kt:206)
at de.ur.mi.audidroid.viewmodels.FolderViewModel$initFolderSorting$1.onChanged(FolderViewModel.kt:45)
at de.ur.mi.audidroid.viewmodels.FolderViewModel$initFolderSorting$1.onChanged(FolderViewModel.kt:20)
at androidx.lifecycle.MediatorLiveData$Source.onChanged(MediatorLiveData.java:152)
at androidx.lifecycle.LiveData.considerNotify(LiveData.java:131)
at androidx.lifecycle.LiveData.dispatchingValue(LiveData.java:149)
at androidx.lifecycle.LiveData.setValue(LiveData.java:307)
at androidx.lifecycle.LiveData$1.run(LiveData.java:91)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
The update didn't even change anything relevant to this function. I know it hast something to do with the change of the list while iterating, but shouldn't MutableList take care of that?
I'm so confused at how I should change it to make it work again, please help.
When using foreach you use the List's iterator which doesn't allow concurrent modification until the loop exists. To get around this, use a standard for loop.
while (foldersSorted.size != allFolders.size) {
for (i in 0 until foldersSorted.size) {
val parentFolder = foldersSorted[i]
// Rest of code
}
}

kotlin when expression autocast

I would like the following kotlin code to work:
val result: Try<Option<String>> = Success(Some("test"))
val test = when {
result is Success && result.value is Some -> result.value.t // not working
result is Success && result.value is None -> "Empty result"
result is Failure -> "Call failed!"
else -> "no match!"
}
I use the arrow library for the Try and Option monad.
Unfortunately, I can only access the value of the first condition "is Success" and not the second condition "is Some". So, I can only do "result.value", I then get an Option of String.
Am I missing something? This will save me alot of inner ".map" and ".fold" calls.
Update:
I need to cast it first, which is ugly:
result is Success && result.value is Some -> (result.value as Some<String>).t
I tried your example in IntelliJ with Kotlin 1.3.21.
It shows the reason of the problem:
You need to extract the result.value as a variable to make it work. I found the following snippet to solve it
val result: Try<Option<String>> = Success(Some("test"))
val test = when (result) {
is Success -> when(val value = result.value) {
is Some -> value.t
is None -> "None"
}
is Failure -> "Call failed!"
else -> "no match!"
}
I use Kotlin 1.3.x when with declaration syntax.
You may also use Arrow API to get similar result:
val test = result.fold(
ifSuccess = { it.getOrElse { "None" }},
ifFailure = { "Call failed!" }
)
Here you do not need to have the else clause in when.
You can simplify the pattern matching like this:
val test = result
.map { it.getOrElse { "Empty result"} }
.getOrElse { "Call failed!" }
Which is a bit more exhaustive and doesn't require an else alternative
Alternatively, if you don't care about the exception that is thrown you can use toOption on the Try:
val test = result
.toOption()
.getOrElse { "No value!!" }
However, that has some obvious loss of information.
I personally would bubble up the Try instance to the consumer of the result collapsing the inner Option with a .map so that the final result is of type Try<String> and let the consumer handle the error.
However, it depends a lot of the actual context of the problem.

kotlin with fastjson parse object error: default constructor not found

I am trying use fastjson parse object in Kotlin code. but exception happened when I use JSON.parseObject, here are detail:
My data class:
import com.alibaba.fastjson.JSONObject
data class StatesMessage #JvmOverloads constructor(val command: String =
"states", var states: States = States()) {
fun toJsonString(): String {
return JSONObject.toJSONString(this)
}
data class States(var x: Double = 0.0, var y: Double = 0.0)
}
Then I try to get object from string:
val state = JSON.parseObject(s, StatesMessage::class.java)
But exception throw from fastjson:
Caused by: com.alibaba.fastjson.JSONException: default constructor not found.
class com.example.demo.StatesMessage
at com.alibaba.fastjson.util.JavaBeanInfo.build(JavaBeanInfo.java:475)
at com.alibaba.fastjson.util.JavaBeanInfo.build(JavaBeanInfo.java:221)
at com.alibaba.fastjson.parser.ParserConfig.createJavaBeanDeserializer(ParserConfig.java:670)
at com.alibaba.fastjson.parser.ParserConfig.getDeserializer(ParserConfig.java:587)
at com.alibaba.fastjson.parser.ParserConfig.getDeserializer(ParserConfig.java:398)
at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:665)
at com.alibaba.fastjson.JSON.parseObject(JSON.java:365)
at com.alibaba.fastjson.JSON.parseObject(JSON.java:269)
at com.alibaba.fastjson.JSON.parseObject(JSON.java:488)
at com.example.demo.StartupRunner.run(StartupRunner.kt:25)
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:813)
... 5 more
all code refer to https://github.com/forest-yang/koltinjson
I think it's a fastjson (till 1.2.54) bug.
When I change to gson, it's work.
/* it will throw exception
val state = JSON.parseObject(s, StatesMessage::class.java)
*/
val state = Gson().fromJson(s, StatesMessage::class.java)
logger.info(state.states.x)
logger.info(state.states.y)

getting user defined error messages using antlr3

numberrange returns [String value]
: numberrangesub
{
String numberRange = ($numberrangesub.text);
String [] v = numberRange.split(",");
if ( Integer.parseInt(v[0].trim()) < Integer.parseInt(v[1].trim())) $value =numberRange;
else throw new RecognitionException();
}
;
Please observe the above ANTLR code. In this I want to throw a user friendly error message like "from value should be less than to value in BETWEEN clause".
I am expecting like this RecognitionException("from value should be less than to value in BETWEEN clause"); But antlr did not accept like as above.
In java class where I am calling the generated java class by Antlr. I am handling like as follows.
try
{
parser.numberRangeCheck();
}
catch (RecognitionException e)
{
throw createException("Invalid Business logic syntax at " + parser.getErrorHeader(e) + ", " + parser.getErrorMessage(e, null), Level.INFO, logger);
}
Any help will be appriciated.
Why not simply throw a RuntimeException with your custom error message?
// ...
else throw new RuntimeException("from value should be less than to value in BETWEEN clause");
// ...
As Terrance wrote in "The Definitive ANTLR Reference" error chapter excerpt:
To avoid forcing English-only error messages and to generally make
things as flexible as possible, the recognizer does not create exception
objects with string messages. Instead, it tracks the information necessary to generate an error.
So there is no error message supplied to RecognitionError's constructor. But you can define additional field of your recognizer to hold user-friendly error message shown on RecognitionError handling:
numberrange returns [String value]
: numberrangesub
{
String numberRange = ($numberrangesub.text);
String [] v = numberRange.split(",");
if ( Integer.parseInt(v[0].trim()) < Integer.parseInt(v[1].trim()))
$value = numberRange;
else {
this.errorMessage = "from value should be less than to value in BETWEEN clause";
throw new RecognitionException(this.input);
}
}
;
And then override the getErrorMessage method:
public String getErrorMessage(RecognitionException e, String[] tokenNames) {
String msg = this.errorMessage;
// ...
}
This works similar to paraphrase mechanism explained in the same excerpt.