Skip to content

Commit

Permalink
Implement filtering in the virtual service for the get all endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
yogeshnikam671 committed Nov 20, 2024
1 parent 2e83d0e commit 70903ac
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,11 @@ class StatefulHttpStub(
}

if(method == "GET" && pathSegments.size == 1) {
val responseBody = stubCache.findAllResponsesFor(resourcePath, attributeSelectionKeys)
val responseBody = stubCache.findAllResponsesFor(
resourcePath,
attributeSelectionKeys,
httpRequest.queryParams.asMap()
)
return generatedResponse.withUpdated(responseBody, attributeSelectionKeys)
}

Expand Down
25 changes: 22 additions & 3 deletions core/src/main/kotlin/io/specmatic/stub/stateful/StubCache.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package io.specmatic.stub.stateful

import io.specmatic.core.value.JSONArrayValue
import io.specmatic.core.value.JSONObjectValue
import io.specmatic.core.value.Value
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock

Expand Down Expand Up @@ -39,9 +40,17 @@ class StubCache {
}
}

fun findAllResponsesFor(path: String, attributeSelectionKeys: Set<String>): JSONArrayValue = lock.withLock {
val responseBodies = cachedResponses.filter { it.path == path }.map {
it.responseBody.removeKeysNotPresentIn(attributeSelectionKeys)
fun findAllResponsesFor(
path: String,
attributeSelectionKeys: Set<String>,
filter: Map<String, String> = emptyMap()
): JSONArrayValue = lock.withLock {
val responseBodies = cachedResponses.filter {
it.path == path
}.map{ it.responseBody }.filter {
it.jsonObject.satisfiesFilter(filter)
}.map {
it.removeKeysNotPresentIn(attributeSelectionKeys)
}
return JSONArrayValue(responseBodies)
}
Expand All @@ -52,4 +61,14 @@ class StubCache {
cachedResponses.remove(existingResponse)
}
}

private fun Map<String, Value>.satisfiesFilter(filter: Map<String, String>): Boolean {
if(filter.isEmpty()) return true

return filter.all { (key, filterValue) ->
if(this.containsKey(key).not()) return@all true
val actualValue = this[key] ?: return@all false
actualValue.toStringLiteral() == filterValue
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,25 @@ class StatefulHttpStubTest {
)
)

val anotherResponse = httpStub.client.execute(
HttpRequest(
method = "POST",
path = "/products",
body = parsedJSONObject(
"""
{
"name": "Product B",
"description": "A detailed description of Product B.",
"price": 100,
"inStock": false
}
""".trimIndent()
)
)
)

assertThat(response.status).isEqualTo(201)
assertThat(anotherResponse.status).isEqualTo(201)
val responseBody = response.body as JSONObjectValue

resourceId = responseBody.getStringValue("id").orEmpty()
Expand All @@ -87,8 +105,11 @@ class StatefulHttpStubTest {

assertThat(response.status).isEqualTo(200)
assertThat(response.body).isInstanceOf(JSONArrayValue::class.java)
val responseBody = response.body as JSONArrayValue

val responseObjectFromResponseBody = (response.body as JSONArrayValue).list.first() as JSONObjectValue
assertThat(responseBody.list.size).isEqualTo(2)

val responseObjectFromResponseBody = responseBody.list.first() as JSONObjectValue

assertThat(responseObjectFromResponseBody.getStringValue("name")).isEqualTo("Product A")
assertThat(responseObjectFromResponseBody.getStringValue("description")).isEqualTo("A detailed description of Product A.")
Expand All @@ -98,6 +119,30 @@ class StatefulHttpStubTest {

@Test
@Order(3)
fun `should get the list of products filtered based on name and price passed in query params`() {
val response = httpStub.client.execute(
HttpRequest(
method = "GET",
path = "/products?name=Product%20A&price=19.99"
)
)

assertThat(response.status).isEqualTo(200)
assertThat(response.body).isInstanceOf(JSONArrayValue::class.java)
val responseBody = response.body as JSONArrayValue

assertThat(responseBody.list.size).isEqualTo(1)

val responseObjectFromResponseBody = responseBody.list.first() as JSONObjectValue

assertThat(responseObjectFromResponseBody.getStringValue("name")).isEqualTo("Product A")
assertThat(responseObjectFromResponseBody.getStringValue("description")).isEqualTo("A detailed description of Product A.")
assertThat(responseObjectFromResponseBody.getStringValue("price")).isEqualTo("19.99")
assertThat(responseObjectFromResponseBody.getStringValue("inStock")).isEqualTo("true")
}

@Test
@Order(4)
fun `should update an existing product with patch`() {
val response = httpStub.client.execute(
HttpRequest(
Expand Down Expand Up @@ -125,7 +170,7 @@ class StatefulHttpStubTest {
}

@Test
@Order(4)
@Order(5)
fun `should get the updated product`() {
val response = httpStub.client.execute(
HttpRequest(
Expand All @@ -145,7 +190,7 @@ class StatefulHttpStubTest {
}

@Test
@Order(5)
@Order(6)
fun `should delete a product`() {
val response = httpStub.client.execute(
HttpRequest(
Expand All @@ -167,7 +212,7 @@ class StatefulHttpStubTest {
}

@Test
@Order(6)
@Order(7)
fun `should post a product even though the request contains unknown keys`() {
val response = httpStub.client.execute(
HttpRequest(
Expand Down Expand Up @@ -200,7 +245,7 @@ class StatefulHttpStubTest {
}

@Test
@Order(7)
@Order(8)
fun `should get a 400 response in a structured manner for an invalid post request`() {
val response = httpStub.client.execute(
HttpRequest(
Expand All @@ -226,7 +271,7 @@ class StatefulHttpStubTest {
assertThat(error).contains("Contract expected boolean but request contained \"true\"")
}

@Order(8)
@Order(9)
@Test
fun `should get a 400 response as a string for an invalid get request where 400 sceham is not defined for the same in the spec`() {
val response = httpStub.client.execute(
Expand All @@ -243,7 +288,7 @@ class StatefulHttpStubTest {
}

@Test
@Order(9)
@Order(10)
fun `should get a 404 response in a structured manner for a get request where the entry with requested id is not present in the cache`() {
val response = httpStub.client.execute(
HttpRequest(
Expand All @@ -259,7 +304,7 @@ class StatefulHttpStubTest {
}

@Test
@Order(10)
@Order(11)
fun `should get a 404 response as a string for a delete request with missing id where 404 schema is not defined for the same in the spec`() {
val response = httpStub.client.execute(
HttpRequest(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ paths:
schema:
type: string
description: Specify which fields to retrieve in the response
- in: query
name: name
schema:
type: string
- in: query
name: price
schema:
type: number
format: float
responses:
'200':
description: A list of products
Expand Down

0 comments on commit 70903ac

Please sign in to comment.