How do I mock LocalDateTime() in Kotlin with Mockk - spring-boot

I'm writing test for a UserService that creates an anonymous user, and part of that includes saving a timestamp at time of creation. The time seems to be mocked properly within the test function itself, but when a timestamp is added in the actual UserService, the real time is being returned. How do I mock it properly?
the relevant UserService function is:
UserService.kt
fun createAnonymousUser(jwt: AuthenticationJsonWebToken): User {
val user = User()
user.anonUserId = jwt.name
user.createdAt = LocalDateTime.now(ZoneOffset.UTC)
return userRepository.save(user)
}
UserService.test.kt
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
open class UserServiceTest {
private val jwt = mockk<AuthenticationJsonWebToken>()
private val userRepository = mockk<UserRepository>()
private val authProperties = AllAuthProperties()
private val fixedClock = mockk<LocalDateTime>()
private val slotUser = slot<User>()
#BeforeTest
internal fun init() {
clearAllMocks()
}
#Test
fun getMe() {
every { jwt.name } returns "anonymous|123"
every { LocalDateTime.now() } returns LocalDateTime.MAX
val userService = UserService(userRepository, authProperties)
val user = User();
user.anonUserId = jwt.name
user.createdAt = LocalDateTime.now()
every { userRepository.save(capture(slotUser)) } returns user
val anonUser = userService.getMe(jwt)
verify { userRepository.save(anonUser) }
assertEquals(anonUser, user)
}
}
I'm just not sure how to accomplish what I'm trying.

Turns out I had it mostly right; I just needed a slight tweak.
verify { userRepository.save(anonUser.capture) }
assertEquals(anonUser, user)

Related

How to get a data from mongoDB with the _id in Kotlin

I'm new in programming and I'm had having some problems to create an API for Kotlin getting data from a existing DB from mongoDB to my API.
I'm making Tests and I want to return a data with it Id, but I can't.
I'm using the SpringBoot, JPA, and mongoDB Data. The host is my machine, the database name is ArthurLeywin, and the collection name is Capitulos.
I'm receiving the error status=404, error=Not Found, path=/ArthueLeywin/Capitulos/6306e6417d5b2e259951f292}
The code of the Test is
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#ExtendWith(SpringExtension::class)
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TbateMobileReederApplicationTests #Autowired constructor(
private val capituloRepository: CapituloRepository,
private val restTemplate: TestRestTemplate
) {
private val defaultCapituloId : ObjectId = ObjectId("6306e6417d5b2e259951f292")
//ObjectId.get()
#LocalServerPort
protected var port: Int = 27017
private fun getRootUrl(): String? = "http://localhost:$port/ArthueLeywin/Capitulos"
#Test
fun `should return single cap by id`() {
val response = restTemplate.getForObject(
getRootUrl() + "/$defaultCapituloId",
Object::class.java
)
val titleResponse = response.toString()
println(titleResponse)
}
}
My controller, if it's needed is
#RestController
#RequestMapping("/Capitulos")
class CapituloController (
private val capituloRepository : CapituloRepository
){
#GetMapping
fun getAllCapitulos() : ResponseEntity<List<Capitulo>>{
val capitulos = capituloRepository.findAll()
return ResponseEntity.ok(capitulos)
}
#GetMapping("/{id}")
fun getOneCapitulo(#PathVariable("id") id: String): ResponseEntity<Capitulo> {
val capitulo = capituloRepository.findOneById(ObjectId(id))
return ResponseEntity.ok(capitulo)
}
#PostMapping
fun createCapitulo (#RequestBody request: CapituloRequest) : ResponseEntity<Capitulo>{
val capitulo = capituloRepository.save(Capitulo(
nomeCapitulo = request.nomeCapitulo,
urlString = request.urlString,
novelTexto = request.novelTexto
))
return ResponseEntity(capitulo, HttpStatus.CREATED)
}
}
As I said I'm new in this, so please help me with explanations <3

Spring test #Transactional with #Nested junit classes

I am encountering a problem in spring transactions: even though I see in the output that a transaction is created and rolled back after each test method; when I check the database content, it still keeps the document that was saved during the transaction.
I ran mongodb instance in replica mode following instructions from here https://www.mongodb.com/docs/manual/tutorial/deploy-replica-set-for-testing/.
My test class is defined as follows:
#Transactional
#AutoConfigureMockMvc
#ActiveProfiles(profiles = ["test"])
#DisplayName("Login Functionality Test")
#SpringBootTest(classes = [TestConfiguration::class])
class LoginFunctionalityTest #Autowired constructor(
private val mockMvc: MockMvc,
private val userRepository: UserRepository,
private val openUserRepository: OpenUserRepository,
private val patientRepository: PatientRepository,
private val passwordEncoder: PasswordEncoder,
private val jwtMatcher: JWTMatcher,
#Value("\${secret:sD1fRUWtBdfA8BNcbf}") private val fastLoginSecret: String
) {
private val existingUsersPassword = "SOME_PASSWORD"
private val userFastLoginTokenSecret = "ANY_SECRET"
private lateinit var existingUser: User
private lateinit var existingOpenUser: OpenUser
private lateinit var existingPatient: Patient
#BeforeEach
fun prepareContext() {
println("Init db")
existingUser = userRepository.save(User(null, now(), "test", "test", "test#test.test", passwordEncoder.encode(existingUsersPassword), UserAuthority.Patient, true, true))
existingOpenUser = openUserRepository.save(OpenUser(null, "ANY", nextExpiryDate()))
existingPatient = patientRepository.save(Patient(null, existingUser))
}
#DisplayName("Login Controller")
#Nested
inner class LoginControllerTest{
#Test
fun `should return unauthorized 401 status code when user logins with not existing email`() {
val invalidEmail = "INVALID_EMAIL"
assertThrows<EmptyResultDataAccessException> { userRepository.findByEmail(invalidEmail) }
mockMvc.post("/auth/login") {
param("username", invalidEmail)
param("password", existingUsersPassword)
}.andExpect {
jsonPath("$.status", equalToIgnoringCase(HttpStatus.UNAUTHORIZED.name))
jsonPath("$.error",StringContains(true,"Invalid login credentials"))
jsonPath("$.data", nullValue())
status { isUnauthorized() }
}
}
...
}
#ActiveProfiles(profiles = ["open-user"])
#DisplayName("Open User Login Controller")
#Nested
inner class OpenUserControllerLoginTest(){
#Test
fun `should return bad request 400 status when any of the required login parameters are missing`(){
val validFastLoginToken = String(Base64.getEncoder().encode(passwordEncoder.encode("$userFastLoginTokenSecret${existingUser.passwordHash}$fastLoginSecret").toByteArray()))
mockMvc
.post("/auth/login/fast") {
param("userId", existingUser.id!!)
param("secret", userFastLoginTokenSecret)
param("fastLoginToken", validFastLoginToken)
}.andExpect {
jsonPath("$.data.isUserActivated", equalTo(existingUser.activated))
jsonPath("$.data.accessToken",jwtMatcher.matches(existingUser))
jsonPath("$.status", equalToIgnoringCase(HttpStatus.OK.name))
jsonPath("$.error", nullValue())
status { isOk() }
content { contentType(MediaType.APPLICATION_JSON) }
}
}
...
}
The output in console has the "Init db" printed between creation and roll back of transaction.
It works correctly only if I move #ActiveProfiles from #Nested class to the outer class. Seems like #ActiveProfiles causes context reload but I don't know why this causes problems with transactions.

How to mock repository so it doesn't instert or pull data from DB

So my issue is that in my SpringBoot REST application im testing my RestController. The problem is that i don't know how to mock the repository so it doesn't get or puts data into the DB. I'm using Kotlin and Mockk for mocking
Here is my Repository
#Repository
interface StandingOrderRepository: CrudRepository<StandingOrder, Int> {
fun findByNameAndVariableSymbol(name: String, variableSymbol: String): List<StandingOrder>
fun findByValidFromBetween(fromDate: String, toDate: String): List<StandingOrder>
fun findByValidFromAfter(fromDate: String) : List<StandingOrder>
}
And here is my Test
#SpringBootTest
#AutoConfigureMockMvc
internal class StandingOrderResourceTest {
#Autowired
lateinit var mockMvc: MockMvc
#Autowired
lateinit var objectMapper: ObjectMapper
private val standingOrderMapper = mockk<StandingOrderMapper>()
private val standingOrderRepository = mockk<StandingOrderRepository>()
private val standingOrderServiceImpl = mockk<StandingOrderServiceImpl>()
private val standingOrderResource = StandingOrderResource(standingOrderServiceImpl)
val baseUrl = "/api"
#Nested
#DisplayName("GetStandingOrders()")
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
inner class GetStandingOrders {
#Test
fun `should return all StandingOrders`() {
standingOrderResource.getStandingOrders()
mockMvc.get(baseUrl)
.andDo { print() }
.andExpect {
status { isOk() }
content { contentType(MediaType.APPLICATION_JSON)}
}
//standingOrderResource.getStandingOrders() shouldBe listOf(standingOrderDto)
}
}
}
The problem is if i Make a API call or invoke the mocked repository it still gets actual data from DB
In your test code you should try to use method whenever() from org.mockito.kotlin for stubbing StandingOrderRepository's method call.
For example your code for stubbing will looks something like this
whenever(standingOrderRepository.findByNameAndVariableSymbol(any(),any())).thenReturn(listOf(StandingOrder(...)))
UPD: So you use Mockk, then you shuold use method every instead whenever from mockito.
So this is how i made it work maybe the issue was on my side how i was trying to use it #Anton Tokmakov was correct here is how i did it
#SpringBootTest
#AutoConfigureMockMvc
#ExtendWith(SpringExtension::class)
internal class StandingOrderResourceTest #Autowired constructor(
val mockMvc: MockMvc,
val objectMapper: ObjectMapper,
) {
#MockkBean
private lateinit var standingOrderResource: StandingOrderResource
#Nested
#DisplayName("GetStandingOrders()")
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
inner class GetStandingOrders {
#Test
fun `should return all StandingOrders`() {
every { standingOrderResource.getStandingOrders() } returns
listOf(standingOrderDto1, standingOrderDto2)
mockMvc.get(baseUrl)
.andDo { print() }
.andExpect {
status { isOk() }
content { contentType(MediaType.APPLICATION_JSON)}
}
.andExpect {
jsonPath("\$..[0]", match(MockMvcResultMatchers.content().json(Gson().toJson(
listOf(
standingOrderDto1,
standingOrderDto2
)), false)))
}
}
}

Using ConnectableFlux for hot stream REST endpoint

I'm trying to create a REST endpoint to subscribe to a hot stream using Reactor.
My test provider for the stream looks like this:
#Service
class TestProvider {
fun getStream(resourceId: String): ConnectableFlux<QData> {
return Flux.create<QData> {
for (i in 1..10) {
it.next(QData(LocalDateTime.now().toString(), "next"))
Thread.sleep(500L)
}
it.complete()
}.publish()
}
}
My service for the REST endpoint looks like this:
#Service
class DataService #Autowired constructor(
private val prv: TestProvider
) {
private val streams = mutableMapOf<String, ConnectableFlux<QData>>()
fun subscribe(resourceId: String): Flux<QData> {
val stream = getStream(resourceId)
return Flux.push { flux ->
stream.subscribe{
flux.next(it)
}
flux.complete()
}
}
private fun getStream(resourceId: String): ConnectableFlux<QData> {
if(streams.containsKey(resourceId).not()) {
streams.put(resourceId, createStream(resourceId))
}
return streams.get(resourceId)!!
}
private fun createStream(resourceId: String): ConnectableFlux<QData> {
val stream = prv.getStream(resourceId)
stream.connect()
return stream
}
}
The controller looks like this:
#RestController
class DataController #Autowired constructor(
private val dataService: DataService
): DataApi {
override fun subscribe(resourceId: String): Flux<QData> {
return dataService.subscribe(resourceId)
}
}
The API interface looks like this:
interface DataApi {
#ApiResponses(value = [
ApiResponse(responseCode = "202", description = "Client is subscribed", content = [
Content(mediaType = "application/json", array = ArraySchema(schema = Schema(implementation = QData::class)))
])
])
#GetMapping(path = ["/subscription/{resourceId}"], produces = [MediaType.APPLICATION_JSON_VALUE])
fun subscribe(
#Parameter(description = "The resource id for which quality data is subscribed for", required = true, example = "example",allowEmptyValue = false)
#PathVariable("resourceId", required = true) #NotEmpty resourceId: String
): Flux<QData>
}
Unfortunately, my curl delivers an empty array.
Does anyone has an idea what the issue is? Thanks in advance!
I had to run connect() async in DataService:
CompletableFuture.runAsync {
stream.connect()
}

Neo4J repository not wired everywhere

I've defined a Neo4j repository as follow (code is in Kotlin but it's very close to Java) :
#Repository
interface UserRepository : GraphRepository<User> {
fun findByEmail(email: String): User?
#Query("match (n:User)-[:IS_AUTH]->(:Permission {name: {0}}) where id(n) = {1} return n")
fun authorizedUser(permission: String, userId: Long): User?
}
In a controller, I've written:
#Controller
#RequestMapping("/company")
open class CreateCompanyController {
#Autowired private lateinit var userRepo: UserRepository
#RequestMapping(method = arrayOf(RequestMethod.POST))
#ResponseBody
fun createCompany(#RequestParam(value = "name", required = true) name: String,
#RequestParam(value = "siret", required = true) siret: Long) : ResponseEntity<String> {
val connectedUser = SecurityContextHolder.getContext().authentication.principal as User
val testUser = userRepo.findByEmail("test#company.com")
if (!PermissionManager().hasAuthorizationFor(PermissionManager.ADMIN_ALL, connectedUser))
return ResponseEntity("You're not authorized to create a company", HttpStatus.FORBIDDEN)
return ResponseEntity(HttpStatus.OK)
}
}
In the code above, the userRepo property is set well (aka. its value is not null).
I want to have this UserRepository in a custom service. So, I've coded:
#Service
open class PermissionManager {
companion object {
val ADMIN_ALL = "ADMIN_ALL"
}
#Autowired private lateinit var userRepo: UserRepository
fun hasAuthorizationFor(permissionName: String, user: User): Boolean {
return userRepo.authorizedUser(permissionName, user.id!!) != null
}
}
But in this case, the userRepo property is not initialized.
I've written the PersistenceContext class as:
#Configuration
#ComponentScan("persistence")
#EnableNeo4jRepositories("persistence.repositories")
#EnableTransactionManagement
open class PersistenceContext : Neo4jConfiguration() {
#Bean override fun getSessionFactory(): SessionFactory {
return SessionFactory("persistence.domain")
}
#Bean
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
open override fun getSession(): Session {
return super.getSession()
}
}
What I don't understand is why is the userRepo set in my controller but not in my service ?

Resources