Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,10 @@ Add at least one:
- Unit test for payload parsing/serialization
- Test for command tracking and response correlation (if applicable)

Tests for extension functions (commands/queries) use `util/MockedXesarConnect` (no MQTT broker)
and live in `command/` and `query/`. Only a few representative integration tests with
testcontainers remain in `it/` — don't add new ones for plain commands/queries.

---

## Code Style & Formatting
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package com.open200.xesar.connect.command

import com.open200.xesar.connect.Topics
import com.open200.xesar.connect.extension.addEntityMetadataDefinitionAsync
import com.open200.xesar.connect.messages.EntityMetadata
import com.open200.xesar.connect.messages.EntityType
import com.open200.xesar.connect.messages.event.ApiEvent
import com.open200.xesar.connect.messages.event.PartitionChanged
import com.open200.xesar.connect.messages.event.encodeEvent
import com.open200.xesar.connect.util.MockedXesarConnect
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.equals.shouldBeEqual
import java.util.*
import kotlinx.coroutines.test.runTest

class AddEntityMetadataDefinitionTest :
FunSpec({
test("add entity metadata definition") {
runTest {
val harness = MockedXesarConnect(this)
harness.respondTo(Topics.Command.ADD_ENTITY_METADATA_DEFINITION) {
val apiEvent =
ApiEvent(
UUID.fromString("00000000-1281-40ae-89d7-5c541d77a757"),
PartitionChanged(
id = UUID.fromString("43edc7cf-80ab-4486-86db-41cda2c7a2cd"),
entityMetadataDefinitions =
PartitionChanged.EntityMetadataDefinitions(
authorizationProfiles = emptyList(),
identificationMedia = emptyList(),
installationPoints = emptyList(),
persons =
listOf(
EntityMetadata(
id =
UUID.fromString(
"aaaaaaaa-0000-0000-0000-000000000001"
),
name = "nickname",
),
EntityMetadata(
id =
UUID.fromString(
"bbbbbbbb-0000-0000-0000-000000000002"
),
name = "department",
),
),
zones = emptyList(),
),
),
)

emitMessage(Topics.Event.PARTITION_CHANGED, encodeEvent(apiEvent))
}

val api = harness.api
val result =
api.addEntityMetadataDefinitionAsync(
EntityType.PERSON,
"nickname",
"department",
)
.await()

result.id.shouldBeEqual(UUID.fromString("43edc7cf-80ab-4486-86db-41cda2c7a2cd"))
result.entityMetadataDefinitions!!
.persons
.map { it.name }
.shouldBeEqual(listOf("nickname", "department"))

harness
.publishedPayload(Topics.Command.ADD_ENTITY_METADATA_DEFINITION)
.shouldBeEqual(
"{\"commandId\":\"00000000-1281-40ae-89d7-5c541d77a757\",\"entityType\":\"PERSON\",\"names\":[\"nickname\",\"department\"],\"token\":\"JDJhJDEwJDFSNEljZ2FaRUNXUXBTQ25XN05KbE9qRzFHQ1VjMzkvWTBVcFpZb1M4Vmt0dnJYZ0tJVFBx\"}"
)
}
}
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.open200.xesar.connect.command

import com.open200.xesar.connect.*
import com.open200.xesar.connect.extension.addEvvaComponentAsync
import com.open200.xesar.connect.messages.ComponentType
import com.open200.xesar.connect.messages.event.*
import com.open200.xesar.connect.messages.query.ComponentStatus
import com.open200.xesar.connect.util.MockedXesarConnect
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.equals.shouldBeEqual
import io.kotest.matchers.shouldBe
import java.time.LocalDateTime
import java.util.*
import kotlinx.coroutines.test.runTest

class AddEvvaComponentTest :
FunSpec({
test("add evva component returning both events") {
runTest {
val harness = MockedXesarConnect(this)
harness.respondTo(Topics.Command.ADD_EVVA_COMPONENT) {
val apiEvent =
ApiEvent(
UUID.fromString("00000000-1281-40ae-89d7-5c541d77a757"),
InstallationPointChanged(
aggregateId =
UUID.fromString("43edc7cf-80ab-4486-86db-41cda2c7a2cd")
),
)

val apiEvent2 =
ApiEvent(
UUID.fromString("00000000-1281-40ae-89d7-5c541d77a757"),
EvvaComponentAdded(
id = UUID.fromString("2d52bd95-18ba-4e46-8f00-0fc4c1e3f9be"),
evvaComponentId =
UUID.fromString("3a33c05b-133d-4b9d-a496-5d30dfd2d2c3"),
type = ComponentType.Cylinder,
stateChangedAt = LocalDateTime.MIN,
status = ComponentStatus.AssembledPrepared,
),
)

emitMessage(Topics.Event.INSTALLATION_POINT_CHANGED, encodeEvent(apiEvent))

emitMessage(Topics.Event.EVVA_COMPONENT_ADDED, encodeEvent(apiEvent2))
}

val api = harness.api
val addEvvaComponentEventPair =
api.addEvvaComponentAsync(
UUID.fromString("2d52bd95-18ba-4e46-8f00-0fc4c1e3f9be"),
ComponentType.Cylinder,
)
val test = addEvvaComponentEventPair.installationPointChangedDeferred.await()
test.aggregateId.shouldBeEqual(
UUID.fromString("43edc7cf-80ab-4486-86db-41cda2c7a2cd")
)
val evvaComponentAdded =
addEvvaComponentEventPair.evvaComponentAddedDeferred.await()
evvaComponentAdded.type.shouldBe(ComponentType.Cylinder)

harness
.publishedPayload(Topics.Command.ADD_EVVA_COMPONENT)
.shouldBeEqual(
"{\"commandId\":\"00000000-1281-40ae-89d7-5c541d77a757\",\"id\":\"2d52bd95-18ba-4e46-8f00-0fc4c1e3f9be\",\"type\":\"Cylinder\",\"token\":\"JDJhJDEwJDFSNEljZ2FaRUNXUXBTQ25XN05KbE9qRzFHQ1VjMzkvWTBVcFpZb1M4Vmt0dnJYZ0tJVFBx\"}"
)
}
}
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.open200.xesar.connect.command

import com.open200.xesar.connect.Topics
import com.open200.xesar.connect.extension.addInstallationPointAuthorizationToMediumAsync
import com.open200.xesar.connect.messages.command.AuthorizationData
import com.open200.xesar.connect.messages.event.ApiEvent
import com.open200.xesar.connect.messages.event.IndividualAuthorizationsAddedToMedium
import com.open200.xesar.connect.messages.event.encodeEvent
import com.open200.xesar.connect.util.MockedXesarConnect
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.equals.shouldBeEqual
import java.util.*
import kotlinx.coroutines.test.runTest

class AddInstallationPointAuthorizationToMediumTest :
FunSpec({
test("add installation point authorization to medium") {
runTest {
val harness = MockedXesarConnect(this)
harness.respondTo(Topics.Command.ADD_INSTALLATION_POINT_AUTHORIZATION_TO_MEDIUM) {
val apiEvent =
ApiEvent(
UUID.fromString("00000000-1281-40ae-89d7-5c541d77a757"),
IndividualAuthorizationsAddedToMedium(
UUID.fromString("43edc7cf-80ab-4486-86db-41cda2c7a2cd")
),
)

emitMessage(
Topics.Event.INDIVIDUAL_AUTHORIZATIONS_ADDED_TO_MEDIUM,
encodeEvent(apiEvent),
)
}

val api = harness.api
val authorizationData =
AuthorizationData(
"authorizationName",
UUID.fromString("2d52bd95-18ba-4e46-8f00-0fc4c1e3f9be"),
"timeProfileName",
UUID.fromString("8c124504-8263-4201-8b17-f49a6c2f8671"),
true,
UUID.fromString("e9b31e62-8969-4794-a219-8c81ff10c91d"),
)
val result =
api.addInstallationPointAuthorizationToMediumAsync(
UUID.fromString("43edc7cf-80ab-4486-86db-41cda2c7a2cd"),
authorizationData,
)
result
.await()
.id
.shouldBeEqual(UUID.fromString("43edc7cf-80ab-4486-86db-41cda2c7a2cd"))

harness
.publishedPayload(Topics.Command.ADD_INSTALLATION_POINT_AUTHORIZATION_TO_MEDIUM)
.shouldBeEqual(
"{\"commandId\":\"00000000-1281-40ae-89d7-5c541d77a757\",\"id\":\"43edc7cf-80ab-4486-86db-41cda2c7a2cd\",\"authorization\":{\"authorizationName\":\"authorizationName\",\"authorizationProfileId\":\"2d52bd95-18ba-4e46-8f00-0fc4c1e3f9be\",\"timeProfileName\":\"timeProfileName\",\"authorizationId\":\"8c124504-8263-4201-8b17-f49a6c2f8671\",\"installationPoint\":true,\"timeProfileId\":\"e9b31e62-8969-4794-a219-8c81ff10c91d\"},\"token\":\"JDJhJDEwJDFSNEljZ2FaRUNXUXBTQ25XN05KbE9qRzFHQ1VjMzkvWTBVcFpZb1M4Vmt0dnJYZ0tJVFBx\"}"
)
}
}
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.open200.xesar.connect.command

import com.open200.xesar.connect.Topics
import com.open200.xesar.connect.extension.addInstallationPointToZoneAsync
import com.open200.xesar.connect.messages.event.ApiEvent
import com.open200.xesar.connect.messages.event.InstallationPointsInZoneChanged
import com.open200.xesar.connect.messages.event.encodeEvent
import com.open200.xesar.connect.util.MockedXesarConnect
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.equals.shouldBeEqual
import java.util.*
import kotlinx.coroutines.test.runTest

class AddInstallationPointToZoneTest :
FunSpec({
test("add installation point to zone") {
runTest {
val harness = MockedXesarConnect(this)
harness.respondTo(Topics.Command.ADD_INSTALLATION_POINT_TO_ZONE) {
val apiEvent =
ApiEvent(
UUID.fromString("00000000-1281-40ae-89d7-5c541d77a757"),
InstallationPointsInZoneChanged(
123,
listOf(UUID.fromString("43edc7cf-80ab-4486-86db-41cda2c7a2cd")),
UUID.fromString("2d52bd95-18ba-4e46-8f00-0fc4c1e3f9be"),
),
)

emitMessage(
Topics.Event.INSTALLATION_POINTS_IN_ZONE_CHANGED,
encodeEvent(apiEvent),
)
}

val api = harness.api
val result =
api.addInstallationPointToZoneAsync(
UUID.fromString("43edc7cf-80ab-4486-86db-41cda2c7a2cd"),
UUID.fromString("2d52bd95-18ba-4e46-8f00-0fc4c1e3f9be"),
)
.await()
result.addedInstallationPoints.size.shouldBeEqual(1)
result.aggregateId.shouldBeEqual(
UUID.fromString("2d52bd95-18ba-4e46-8f00-0fc4c1e3f9be")
)
result.accessId.shouldBeEqual(123)

harness
.publishedPayload(Topics.Command.ADD_INSTALLATION_POINT_TO_ZONE)
.shouldBeEqual(
"{\"commandId\":\"00000000-1281-40ae-89d7-5c541d77a757\",\"installationPointId\":\"43edc7cf-80ab-4486-86db-41cda2c7a2cd\",\"id\":\"2d52bd95-18ba-4e46-8f00-0fc4c1e3f9be\",\"token\":\"JDJhJDEwJDFSNEljZ2FaRUNXUXBTQ25XN05KbE9qRzFHQ1VjMzkvWTBVcFpZb1M4Vmt0dnJYZ0tJVFBx\"}"
)
}
}
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.open200.xesar.connect.command

import com.open200.xesar.connect.Topics
import com.open200.xesar.connect.extension.addSmartphoneToInstallationAsync
import com.open200.xesar.connect.messages.DisengagePeriod
import com.open200.xesar.connect.messages.event.ApiEvent
import com.open200.xesar.connect.messages.event.SmartphoneAddedToInstallation
import com.open200.xesar.connect.messages.event.encodeEvent
import com.open200.xesar.connect.util.MockedXesarConnect
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.equals.shouldBeEqual
import io.kotest.matchers.shouldBe
import java.time.LocalDateTime
import java.util.*
import kotlinx.coroutines.test.runTest

class AddSmartphoneToInstallationTest :
FunSpec({
test("add smartphone to installation") {
runTest {
val harness = MockedXesarConnect(this)
harness.respondTo(Topics.Command.ADD_SMARTPHONE_TO_INSTALLATION) {
val apiEvent =
ApiEvent(
UUID.fromString("00000000-1281-40ae-89d7-5c541d77a757"),
SmartphoneAddedToInstallation(
accessBeginAt = LocalDateTime.parse("2024-08-08T12:25"),
partitionId =
UUID.fromString("5679d684-41c6-484c-8292-204feb085ef0"),
disengagePeriod = DisengagePeriod.SHORT,
mediumIdentifier = 8,
authorizationProfileId =
UUID.fromString("00000000-0000-0000-0000-000000000001"),
individualAuthorizations = emptyList(),
sagaId = UUID.fromString("a14d08c0-6f7a-4fc6-8469-be15a3e7b575"),
messageLanguage = "en",
label = "new f228c89d-c2e1-450d-8b79-89072b7b24c3",
validityBeginAt = LocalDateTime.parse("2024-08-08T12:20"),
accessEndAt = null,
validityEndAt = LocalDateTime.parse("2024-12-09T14:20"),
aggregateId =
UUID.fromString("43edc7cf-80ab-4486-86db-41cda2c7a2cd"),
issuedAt = LocalDateTime.parse("2024-08-08T12:29:55.708239786"),
),
)

emitMessage(
Topics.Event.SMARTPHONE_ADDED_TO_INSTALLATION,
encodeEvent(apiEvent),
)
}

val api = harness.api
val result =
api.addSmartphoneToInstallationAsync(
id = UUID.fromString("43edc7cf-80ab-4486-86db-41cda2c7a2cd")
)
.await()

result.aggregateId.shouldBe(UUID.fromString("43edc7cf-80ab-4486-86db-41cda2c7a2cd"))

harness
.publishedPayload(Topics.Command.ADD_SMARTPHONE_TO_INSTALLATION)
.shouldBeEqual(
"{\"commandId\":\"00000000-1281-40ae-89d7-5c541d77a757\",\"accessBeginAt\":null,\"partitionId\":null,\"disengagePeriod\":null,\"authorizationProfileId\":null,\"individualAuthorizations\":[],\"messageLanguage\":null,\"label\":null,\"accessEndAt\":null,\"phoneNumber\":null,\"validityDuration\":null,\"personId\":null,\"id\":\"43edc7cf-80ab-4486-86db-41cda2c7a2cd\",\"token\":\"JDJhJDEwJDFSNEljZ2FaRUNXUXBTQ25XN05KbE9qRzFHQ1VjMzkvWTBVcFpZb1M4Vmt0dnJYZ0tJVFBx\"}"
)
}
}
})
Loading
Loading