diff --git a/client/src/main/kotlin/com/lowes/auditor/client/infrastructure/frameworks/mapper/JsonNodeMapper.kt b/client/src/main/kotlin/com/lowes/auditor/client/infrastructure/frameworks/mapper/JsonNodeMapper.kt index d41416c..c05f7ad 100644 --- a/client/src/main/kotlin/com/lowes/auditor/client/infrastructure/frameworks/mapper/JsonNodeMapper.kt +++ b/client/src/main/kotlin/com/lowes/auditor/client/infrastructure/frameworks/mapper/JsonNodeMapper.kt @@ -26,77 +26,67 @@ object JsonNodeMapper { fqcn: String, ): Flux { val hasFields = node.fields().hasNext() + + // Special handling for top-level arrays + if (!hasFields && node.isArray) { + return Flux.fromIterable(node) + .index() + .flatMap { tuple -> + val index = tuple.t1 + val arrayItem = tuple.t2 + val elementFqcn = "$fqcn.$index" + createElementForNode(arrayItem, index.toString(), eventType, elementFqcn) + } + } + return Flux.fromIterable(getIterables(hasFields, node)) .index() - .flatMap { indexEntryPair -> - val index = indexEntryPair.t1 - val entry = indexEntryPair.t2 - findType(entry.value) - ?.let { - if (it == NodeType.ARRAY) { + .flatMap { tuple -> + val index = tuple.t1 + val entry = tuple.t2 + val elementFqcn = getFqcnValue(hasFields, index, fqcn, entry) + + // Process only entries with valid node types + findType(entry.value)?.let { nodeType -> + when (nodeType) { + NodeType.ARRAY -> { Flux.fromIterable(entry.value) .index() - .flatMap { t -> - val fqcnValue = getFqcnValue(hasFields, t.t1, fqcn, entry).plus(".").plus(t.t1) - if (t.t2.isValueNode) { - val updatedValue = - if (eventType == EventType.CREATED) { - findType(t.t2)?.let { it1 -> getValue(it1, t.t2) } - } else { - null - } - val previousValue = - if (eventType == EventType.DELETED) { - findType(t.t2)?.let { it1 -> getValue(it1, t.t2) } - } else { - null - } - Flux.just( - Element( - name = entry.key, - updatedValue = updatedValue, - previousValue = previousValue, - metadata = - ElementMetadata( - fqdn = fqcnValue, - ), - ), - ) - } else { - toElement(t.t2, eventType, fqcnValue) - } + .flatMap { arrayTuple -> + val arrayIdx = arrayTuple.t1 + val arrayItem = arrayTuple.t2 + val arrayElementFqcn = "$elementFqcn.$arrayIdx" + createElementForNode(arrayItem, entry.key, eventType, arrayElementFqcn) } - } else if (it == NodeType.OBJECT) { - toElement(entry.value, eventType, getFqcnValue(hasFields, index, fqcn, entry)) - } else { - val updatedValue = - if (eventType == EventType.CREATED) { - getValue(it, entry.value) - } else { - null - } - val previousValue = - if (eventType == EventType.DELETED) { - getValue(it, entry.value) - } else { - null - } - Flux.just( - Element( - name = entry.key, - updatedValue = updatedValue, - previousValue = previousValue, - metadata = - ElementMetadata( - fqdn = getFqcnValue(hasFields, index, fqcn, entry), - ), - ), - ) } - } ?: Flux.empty() + NodeType.OBJECT -> toElement(entry.value, eventType, elementFqcn) + else -> createElementForNode(entry.value, entry.key, eventType, elementFqcn, nodeType) + } + } ?: Flux.empty() } } + /** + * Creates an Element for a node with the given parameters. + */ + private fun createElementForNode( + node: JsonNode, + name: String, + eventType: EventType, + fqdn: String, + nodeType: NodeType? = findType(node), + ): Flux { + if (nodeType == null) return Flux.empty() + val updatedValue = if (eventType == EventType.CREATED) getValue(nodeType, node) else null + val previousValue = if (eventType == EventType.DELETED) getValue(nodeType, node) else null + + return if (nodeType == NodeType.ARRAY || nodeType == NodeType.OBJECT) { + toElement(node, eventType, fqdn) + } else { + Flux.just(Element(name, updatedValue, previousValue, ElementMetadata(fqdn = fqdn))) + } + } + /** * Derives the fully qualified class name for a given element. */ diff --git a/client/src/test/kotlin/com/lowes/auditor/client/infrastructure/frameworks/mapper/JsonNodeMapperTest.kt b/client/src/test/kotlin/com/lowes/auditor/client/infrastructure/frameworks/mapper/JsonNodeMapperTest.kt new file mode 100644 index 0000000..5be8a28 --- /dev/null +++ b/client/src/test/kotlin/com/lowes/auditor/client/infrastructure/frameworks/mapper/JsonNodeMapperTest.kt @@ -0,0 +1,263 @@ +package com.lowes.auditor.client.infrastructure.frameworks.mapper + +import com.fasterxml.jackson.databind.ObjectMapper +import com.lowes.auditor.core.entities.domain.Element +import com.lowes.auditor.core.entities.domain.EventType +import io.kotest.core.spec.style.BehaviorSpec +import io.kotest.matchers.nulls.shouldNotBeNull +import io.kotest.matchers.shouldBe + +/** + * Unit Tests for [JsonNodeMapper] + */ +class JsonNodeMapperTest : BehaviorSpec({ + val objectMapper = ObjectMapper() + val fqcn = "com.example.Test" + + Given("a simple JSON object with primitive values") { + val simpleJson = + """ + { + "id": 1, + "name": "Test", + "active": true + } + """.trimIndent() + + val jsonNode = objectMapper.readTree(simpleJson) + + When("converting to elements with CREATED event type") { + val elements = + JsonNodeMapper.toElement(jsonNode, EventType.CREATED, fqcn) + .collectList() + .block() + + Then("should create elements with correct FQDNs and values") { + elements.shouldNotBeNull() + + val expectedElements: List = + objectMapper.readValue( + javaClass.getResource("/simpleCreated.json").readBytes(), + Array::class.java, + ).toList() + + // Sort both lists by name for consistent comparison + val sortedActual = elements.sortedBy { it.name } + val sortedExpected = expectedElements.sortedBy { it.name } + + sortedActual shouldBe sortedExpected + } + } + + When("converting to elements with DELETED event type") { + val elements = + JsonNodeMapper.toElement(jsonNode, EventType.DELETED, fqcn) + .collectList() + .block() + + Then("should set previousValue instead of updatedValue") { + elements.shouldNotBeNull() + + val expectedElements: List = + objectMapper.readValue( + javaClass.getResource("/simpleDeleted.json").readBytes(), + Array::class.java, + ).toList() + + // Sort both lists by name for consistent comparison + val sortedActual = elements.sortedBy { it.name } + val sortedExpected = expectedElements.sortedBy { it.name } + + sortedActual shouldBe sortedExpected + } + } + } + + Given("a JSON object with nested objects") { + val nestedJson = + """ + { + "id": 1, + "name": "Test", + "address": { + "street": "123 Main St", + "city": "Anytown", + "zip": "12345" + } + } + """.trimIndent() + + val jsonNode = objectMapper.readTree(nestedJson) + + When("converting to elements") { + val elements = + JsonNodeMapper.toElement(jsonNode, EventType.CREATED, fqcn) + .collectList() + .block() + + Then("should handle nested objects correctly") { + elements.shouldNotBeNull() + + val expectedElements: List = + objectMapper.readValue( + javaClass.getResource("/nestedCreated.json").readBytes(), + Array::class.java, + ).toList() + + // Sort both lists by name and fqdn for consistent comparison + val sortedActual = elements.sortedWith(compareBy({ it.name }, { it.metadata?.fqdn })) + val sortedExpected = expectedElements.sortedWith(compareBy({ it.name }, { it.metadata?.fqdn })) + + sortedActual shouldBe sortedExpected + } + } + } + + Given("a complex JSON object with arrays and nested objects") { + val complexJson = + """ + { + "id": 1, + "name": "Test User", + "addresses": [ + { + "type": "home", + "street": "123 Main St", + "city": "Anytown" + }, + { + "type": "work", + "street": "456 Business Ave", + "city": "Businesstown" + } + ], + "preferences": { + "notifications": true, + "theme": "dark", + "favoriteCategories": ["tech", "books", "music"] + } + } + """.trimIndent() + + val jsonNode = objectMapper.readTree(complexJson) + + When("converting complex object to elements") { + val elements = + JsonNodeMapper.toElement(jsonNode, EventType.CREATED, fqcn) + .collectList() + .block() + + Then("should handle all nested structures correctly") { + elements.shouldNotBeNull() + + val expectedElements: List = + objectMapper.readValue( + javaClass.getResource("/complexCreated.json").readBytes(), + Array::class.java, + ).toList() + + // Sort both lists by fqdn for consistent comparison + val sortedActual = elements.sortedBy { it.metadata?.fqdn } + val sortedExpected = expectedElements.sortedBy { it.metadata?.fqdn } + + sortedActual shouldBe sortedExpected + } + } + } + + Given("a JSON object with various array structures") { + val nestedArrayJson = + """ + { + "matrix": [ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9] + ], + "nested": { + "deepArray": [ + [ + {"id": 1, "values": ["a", "b"]}, + {"id": 2, "values": ["c", "d"]} + ], + [ + {"id": 3, "values": ["e", "f"]} + ] + ] + }, + "simpleArray": [ + {"id": 1, "name": "Item 1"}, + {"id": 2, "name": "Item 2"} + ] + } + """.trimIndent() + + val jsonNode = objectMapper.readTree(nestedArrayJson) + + When("converting nested arrays to elements") { + val elements = + JsonNodeMapper.toElement(jsonNode, EventType.CREATED, fqcn) + .collectList() + .block() + + Then("should handle all levels of nested arrays correctly") { + elements.shouldNotBeNull() + + val expectedElements: List = + objectMapper.readValue( + javaClass.getResource("/nestedArrayCreated.json").readBytes(), + Array::class.java, + ).toList() + + // Sort both lists by fqdn for consistent comparison + val sortedActual = elements.sortedBy { it.metadata?.fqdn } + val sortedExpected = expectedElements.sortedBy { it.metadata?.fqdn } + + sortedActual shouldBe sortedExpected + } + } + } + + Given("a JSON object with edge case values") { + val edgeCaseJson = + """ + { + "nullValue": null, + "emptyString": "", + "zero": 0, + "falseValue": false, + "emptyObject": {}, + "emptyArray": [] + } + """.trimIndent() + + val jsonNode = objectMapper.readTree(edgeCaseJson) + + When("converting edge case values to elements") { + val elements = + JsonNodeMapper.toElement(jsonNode, EventType.CREATED, fqcn) + .collectList() + .block() + + Then("should handle all edge cases correctly") { + elements.shouldNotBeNull() + + val expectedElements: List = + objectMapper.readValue( + javaClass.getResource("/edgeCaseCreated.json").readBytes(), + Array::class.java, + ).toList() + + // Sort both lists by name for consistent comparison + val sortedActual = elements.sortedBy { it.name } + val sortedExpected = expectedElements.sortedBy { it.name } + + sortedActual shouldBe sortedExpected + + // Explicitly verify empty objects and arrays are filtered out + elements.find { it.name == "emptyObject" } shouldBe null + elements.find { it.name == "emptyArray" } shouldBe null + } + } + } +}) diff --git a/client/src/test/kotlin/com/lowes/auditor/client/infrastructure/frameworks/model/Item.kt b/client/src/test/kotlin/com/lowes/auditor/client/infrastructure/frameworks/model/Item.kt index 4310252..bd9206c 100644 --- a/client/src/test/kotlin/com/lowes/auditor/client/infrastructure/frameworks/model/Item.kt +++ b/client/src/test/kotlin/com/lowes/auditor/client/infrastructure/frameworks/model/Item.kt @@ -15,6 +15,7 @@ data class Item( val price: BigDecimal? = null, val subList: List? = null, val subMap: Map? = null, + val nestedList: List? = null, ) data class Rand( @@ -25,6 +26,11 @@ data class Rand( val mapList: Map>? = null, ) +data class NestedItem( + val id: String, + val items: List, +) + data class SubObject( val value: String? = null, val uom: String? = null, diff --git a/client/src/test/kotlin/com/lowes/auditor/client/infrastructure/frameworks/service/ObjectDiffCheckerServiceTest.kt b/client/src/test/kotlin/com/lowes/auditor/client/infrastructure/frameworks/service/ObjectDiffCheckerServiceTest.kt index 9ce779b..f942598 100644 --- a/client/src/test/kotlin/com/lowes/auditor/client/infrastructure/frameworks/service/ObjectDiffCheckerServiceTest.kt +++ b/client/src/test/kotlin/com/lowes/auditor/client/infrastructure/frameworks/service/ObjectDiffCheckerServiceTest.kt @@ -5,6 +5,7 @@ import com.lowes.auditor.client.entities.domain.AuditorEventConfig import com.lowes.auditor.client.infrastructure.frameworks.config.FrameworkModule import com.lowes.auditor.client.infrastructure.frameworks.model.DummyClass import com.lowes.auditor.client.infrastructure.frameworks.model.Item +import com.lowes.auditor.client.infrastructure.frameworks.model.NestedItem import com.lowes.auditor.client.infrastructure.frameworks.model.Rand import com.lowes.auditor.client.infrastructure.frameworks.model.SubObject import com.lowes.auditor.core.entities.domain.Element @@ -280,4 +281,134 @@ class ObjectDiffCheckerServiceTest : BehaviorSpec({ } } } + + Given("List with different order") { + val item1 = + Item( + itemNumber = "123", + stringList = listOf("a", "b", "c"), + ) + + val item2 = + Item( + itemNumber = "123", + stringList = listOf("c", "b", "a"), + ) + + When("comparing items with lists in different orders") { + val diff = diffChecker.diff(item1, item2).collectList().block() + + Then("should detect the order change as a difference") { + diff shouldBe + obj.readValue( + javaClass.getResource("/listOrderChange.json").readBytes(), + Array::class.java, + ).toList() + } + } + } + + Given("Nested list with different order") { + val item1 = + Item( + itemNumber = "123", + rand = + Rand( + id = "1", + doubleList = + listOf( + SubObject("a", "uom1"), + SubObject("b", "uom2"), + ), + ), + ) + + val item2 = + Item( + itemNumber = "123", + rand = + Rand( + id = "1", + doubleList = + listOf( + SubObject("b", "uom2"), + SubObject("a", "uom1"), + ), + ), + ) + + When("comparing items with nested lists in different orders") { + val diff = diffChecker.diff(item1, item2).collectList().block() + + Then("should detect the order change in nested list as a difference") { + diff shouldBe + obj.readValue( + javaClass.getResource("/nestedListOrderChange.json").readBytes(), + Array::class.java, + ).toList() + } + } + } + + Given("Deeply nested list with different order") { + val item1 = + Item( + itemNumber = "123", + nestedList = + listOf( + NestedItem( + "n1", + listOf( + SubObject("a", "uom1"), + SubObject("b", "uom2"), + ), + ), + NestedItem( + "n2", + listOf( + SubObject("x", "uom3"), + SubObject("y", "uom4"), + SubObject("z", "uom5"), + ), + ), + ), + ) + + val item2 = + Item( + itemNumber = "123", + nestedList = + listOf( + NestedItem( + "n2", + listOf( + SubObject("z", "uom5"), + SubObject("y", "uom4"), + SubObject("x", "uom3"), + ), + ), + NestedItem( + "n1", + listOf( + SubObject("b", "uom2"), + SubObject("a", "uom1"), + ), + ), + ), + ) + + When("comparing items with deeply nested lists in different orders") { + val diff = diffChecker.diff(item1, item2).collectList().block() + + Then("should detect the order changes in deeply nested lists with correct FQDNs") { + val expected = + obj.readValue( + javaClass.getResource("/deeplyNestedListOrderChange.json").readBytes(), + Array::class.java, + ).toList() + + diff shouldBe expected + } + } + } }) diff --git a/client/src/test/resources/complexCreated.json b/client/src/test/resources/complexCreated.json new file mode 100644 index 0000000..0b38b96 --- /dev/null +++ b/client/src/test/resources/complexCreated.json @@ -0,0 +1,106 @@ +[ + { + "name": "id", + "updatedValue": "1", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.id" + } + }, + { + "name": "name", + "updatedValue": "Test User", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.name" + } + }, + { + "name": "type", + "updatedValue": "home", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.addresses.0.type" + } + }, + { + "name": "street", + "updatedValue": "123 Main St", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.addresses.0.street" + } + }, + { + "name": "city", + "updatedValue": "Anytown", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.addresses.0.city" + } + }, + { + "name": "type", + "updatedValue": "work", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.addresses.1.type" + } + }, + { + "name": "street", + "updatedValue": "456 Business Ave", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.addresses.1.street" + } + }, + { + "name": "city", + "updatedValue": "Businesstown", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.addresses.1.city" + } + }, + { + "name": "notifications", + "updatedValue": "true", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.preferences.notifications" + } + }, + { + "name": "theme", + "updatedValue": "dark", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.preferences.theme" + } + }, + { + "name": "favoriteCategories", + "updatedValue": "tech", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.preferences.favoriteCategories.0" + } + }, + { + "name": "favoriteCategories", + "updatedValue": "books", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.preferences.favoriteCategories.1" + } + }, + { + "name": "favoriteCategories", + "updatedValue": "music", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.preferences.favoriteCategories.2" + } + } +] diff --git a/client/src/test/resources/deeplyNestedListOrderChange.json b/client/src/test/resources/deeplyNestedListOrderChange.json new file mode 100644 index 0000000..d286a52 --- /dev/null +++ b/client/src/test/resources/deeplyNestedListOrderChange.json @@ -0,0 +1,128 @@ +[ + { + "name": "id", + "previousValue": "n2", + "updatedValue": "n1", + "metadata": { + "fqdn": "com.lowes.auditor.client.infrastructure.frameworks.model.Item.nestedList.1.id", + "identifiers": null + } + }, + { + "name": "value", + "previousValue": null, + "updatedValue": "x", + "metadata": { + "fqdn": "com.lowes.auditor.client.infrastructure.frameworks.model.Item.nestedList.0.items.2.value", + "identifiers": null + } + }, + { + "name": "id", + "previousValue": "n1", + "updatedValue": "n2", + "metadata": { + "fqdn": "com.lowes.auditor.client.infrastructure.frameworks.model.Item.nestedList.0.id", + "identifiers": null + } + }, + { + "name": "uom", + "previousValue": null, + "updatedValue": "uom3", + "metadata": { + "fqdn": "com.lowes.auditor.client.infrastructure.frameworks.model.Item.nestedList.0.items.2.uom", + "identifiers": null + } + }, + { + "name": "uom", + "previousValue": "uom2", + "updatedValue": "uom4", + "metadata": { + "fqdn": "com.lowes.auditor.client.infrastructure.frameworks.model.Item.nestedList.0.items.1.uom", + "identifiers": null + } + }, + { + "name": "value", + "previousValue": "z", + "updatedValue": null, + "metadata": { + "fqdn": "com.lowes.auditor.client.infrastructure.frameworks.model.Item.nestedList.1.items.2.value", + "identifiers": null + } + }, + { + "name": "uom", + "previousValue": "uom5", + "updatedValue": null, + "metadata": { + "fqdn": "com.lowes.auditor.client.infrastructure.frameworks.model.Item.nestedList.1.items.2.uom", + "identifiers": null + } + }, + { + "name": "value", + "previousValue": "b", + "updatedValue": "y", + "metadata": { + "fqdn": "com.lowes.auditor.client.infrastructure.frameworks.model.Item.nestedList.0.items.1.value", + "identifiers": null + } + }, + { + "name": "value", + "previousValue": "a", + "updatedValue": "z", + "metadata": { + "fqdn": "com.lowes.auditor.client.infrastructure.frameworks.model.Item.nestedList.0.items.0.value", + "identifiers": null + } + }, + { + "name": "value", + "previousValue": "y", + "updatedValue": "a", + "metadata": { + "fqdn": "com.lowes.auditor.client.infrastructure.frameworks.model.Item.nestedList.1.items.1.value", + "identifiers": null + } + }, + { + "name": "value", + "previousValue": "x", + "updatedValue": "b", + "metadata": { + "fqdn": "com.lowes.auditor.client.infrastructure.frameworks.model.Item.nestedList.1.items.0.value", + "identifiers": null + } + }, + { + "name": "uom", + "previousValue": "uom3", + "updatedValue": "uom2", + "metadata": { + "fqdn": "com.lowes.auditor.client.infrastructure.frameworks.model.Item.nestedList.1.items.0.uom", + "identifiers": null + } + }, + { + "name": "uom", + "previousValue": "uom1", + "updatedValue": "uom5", + "metadata": { + "fqdn": "com.lowes.auditor.client.infrastructure.frameworks.model.Item.nestedList.0.items.0.uom", + "identifiers": null + } + }, + { + "name": "uom", + "previousValue": "uom4", + "updatedValue": "uom1", + "metadata": { + "fqdn": "com.lowes.auditor.client.infrastructure.frameworks.model.Item.nestedList.1.items.1.uom", + "identifiers": null + } + } + ] \ No newline at end of file diff --git a/client/src/test/resources/edgeCaseCreated.json b/client/src/test/resources/edgeCaseCreated.json new file mode 100644 index 0000000..91696bd --- /dev/null +++ b/client/src/test/resources/edgeCaseCreated.json @@ -0,0 +1,34 @@ +[ + { + "name": "nullValue", + "updatedValue": "null", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.nullValue" + } + }, + { + "name": "emptyString", + "updatedValue": "", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.emptyString" + } + }, + { + "name": "zero", + "updatedValue": "0", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.zero" + } + }, + { + "name": "falseValue", + "updatedValue": "false", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.falseValue" + } + } +] diff --git a/client/src/test/resources/listOrderChange.json b/client/src/test/resources/listOrderChange.json new file mode 100644 index 0000000..48d21a4 --- /dev/null +++ b/client/src/test/resources/listOrderChange.json @@ -0,0 +1,20 @@ +[ + { + "name": "stringList", + "previousValue": "a", + "updatedValue": "c", + "metadata": { + "fqdn": "com.lowes.auditor.client.infrastructure.frameworks.model.Item.stringList.0", + "identifiers": null + } + }, + { + "name": "stringList", + "previousValue": "c", + "updatedValue": "a", + "metadata": { + "fqdn": "com.lowes.auditor.client.infrastructure.frameworks.model.Item.stringList.2", + "identifiers": null + } + } +] diff --git a/client/src/test/resources/nestedArrayCreated.json b/client/src/test/resources/nestedArrayCreated.json new file mode 100644 index 0000000..9cc32bc --- /dev/null +++ b/client/src/test/resources/nestedArrayCreated.json @@ -0,0 +1,178 @@ +[ + { + "name": "0", + "updatedValue": "1", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.matrix.0.0" + } + }, + { + "name": "1", + "updatedValue": "2", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.matrix.0.1" + } + }, + { + "name": "2", + "updatedValue": "3", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.matrix.0.2" + } + }, + { + "name": "0", + "updatedValue": "4", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.matrix.1.0" + } + }, + { + "name": "1", + "updatedValue": "5", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.matrix.1.1" + } + }, + { + "name": "2", + "updatedValue": "6", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.matrix.1.2" + } + }, + { + "name": "0", + "updatedValue": "7", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.matrix.2.0" + } + }, + { + "name": "1", + "updatedValue": "8", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.matrix.2.1" + } + }, + { + "name": "2", + "updatedValue": "9", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.matrix.2.2" + } + }, + { + "name": "id", + "updatedValue": "1", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.nested.deepArray.0.0.id" + } + }, + { + "name": "values", + "updatedValue": "a", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.nested.deepArray.0.0.values.0" + } + }, + { + "name": "values", + "updatedValue": "b", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.nested.deepArray.0.0.values.1" + } + }, + { + "name": "id", + "updatedValue": "2", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.nested.deepArray.0.1.id" + } + }, + { + "name": "values", + "updatedValue": "c", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.nested.deepArray.0.1.values.0" + } + }, + { + "name": "values", + "updatedValue": "d", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.nested.deepArray.0.1.values.1" + } + }, + { + "name": "id", + "updatedValue": "3", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.nested.deepArray.1.0.id" + } + }, + { + "name": "values", + "updatedValue": "e", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.nested.deepArray.1.0.values.0" + } + }, + { + "name": "values", + "updatedValue": "f", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.nested.deepArray.1.0.values.1" + } + }, + { + "name": "id", + "updatedValue": "1", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.simpleArray.0.id" + } + }, + { + "name": "name", + "updatedValue": "Item 1", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.simpleArray.0.name" + } + }, + { + "name": "id", + "updatedValue": "2", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.simpleArray.1.id" + } + }, + { + "name": "name", + "updatedValue": "Item 2", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.simpleArray.1.name" + } + } +] diff --git a/client/src/test/resources/nestedCreated.json b/client/src/test/resources/nestedCreated.json new file mode 100644 index 0000000..c16b1c4 --- /dev/null +++ b/client/src/test/resources/nestedCreated.json @@ -0,0 +1,42 @@ +[ + { + "name": "id", + "updatedValue": "1", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.id" + } + }, + { + "name": "name", + "updatedValue": "Test", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.name" + } + }, + { + "name": "street", + "updatedValue": "123 Main St", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.address.street" + } + }, + { + "name": "city", + "updatedValue": "Anytown", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.address.city" + } + }, + { + "name": "zip", + "updatedValue": "12345", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.address.zip" + } + } +] diff --git a/client/src/test/resources/nestedListOrderChange.json b/client/src/test/resources/nestedListOrderChange.json new file mode 100644 index 0000000..5aba5ec --- /dev/null +++ b/client/src/test/resources/nestedListOrderChange.json @@ -0,0 +1,38 @@ +[ + { + "name": "uom", + "previousValue": "uom2", + "updatedValue": "uom1", + "metadata": { + "fqdn": "com.lowes.auditor.client.infrastructure.frameworks.model.Item.rand.doubleList.1.uom", + "identifiers": null + } + }, + { + "name": "uom", + "previousValue": "uom1", + "updatedValue": "uom2", + "metadata": { + "fqdn": "com.lowes.auditor.client.infrastructure.frameworks.model.Item.rand.doubleList.0.uom", + "identifiers": null + } + }, + { + "name": "value", + "previousValue": "b", + "updatedValue": "a", + "metadata": { + "fqdn": "com.lowes.auditor.client.infrastructure.frameworks.model.Item.rand.doubleList.1.value", + "identifiers": null + } + }, + { + "name": "value", + "previousValue": "a", + "updatedValue": "b", + "metadata": { + "fqdn": "com.lowes.auditor.client.infrastructure.frameworks.model.Item.rand.doubleList.0.value", + "identifiers": null + } + } +] diff --git a/client/src/test/resources/simpleCreated.json b/client/src/test/resources/simpleCreated.json new file mode 100644 index 0000000..e6baebe --- /dev/null +++ b/client/src/test/resources/simpleCreated.json @@ -0,0 +1,26 @@ +[ + { + "name": "id", + "updatedValue": "1", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.id" + } + }, + { + "name": "name", + "updatedValue": "Test", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.name" + } + }, + { + "name": "active", + "updatedValue": "true", + "previousValue": null, + "metadata": { + "fqdn": "com.example.Test.active" + } + } +] diff --git a/client/src/test/resources/simpleDeleted.json b/client/src/test/resources/simpleDeleted.json new file mode 100644 index 0000000..63d1811 --- /dev/null +++ b/client/src/test/resources/simpleDeleted.json @@ -0,0 +1,26 @@ +[ + { + "name": "id", + "updatedValue": null, + "previousValue": "1", + "metadata": { + "fqdn": "com.example.Test.id" + } + }, + { + "name": "name", + "updatedValue": null, + "previousValue": "Test", + "metadata": { + "fqdn": "com.example.Test.name" + } + }, + { + "name": "active", + "updatedValue": null, + "previousValue": "true", + "metadata": { + "fqdn": "com.example.Test.active" + } + } +]