diff --git a/adapter-sdk-kotlin/src/main/kotlin/com/github/oslokommune/oslonokkelen/adapter/thing/ThingState.kt b/adapter-sdk-kotlin/src/main/kotlin/com/github/oslokommune/oslonokkelen/adapter/thing/ThingState.kt index 88ed685..e0633c8 100644 --- a/adapter-sdk-kotlin/src/main/kotlin/com/github/oslokommune/oslonokkelen/adapter/thing/ThingState.kt +++ b/adapter-sdk-kotlin/src/main/kotlin/com/github/oslokommune/oslonokkelen/adapter/thing/ThingState.kt @@ -132,10 +132,13 @@ sealed class ThingState { val status : Status, val lastSeen: Instant?, val explanation: String? - ) : ThingState() { + ) : ThingState(), ComparableThingState { override val key = Key(thingId, "thing.${thingId.value}.online") + override fun hasSameStateAs(other: ThingState): Boolean { + return other is Online && lastSeen == other.lastSeen && status == other.status && explanation == other.explanation + } enum class Status { REPORTED_ONLINE, REPORTED_OFFLINE, ONLINE_STATUS_UNSUPPORTED diff --git a/adapter-sdk-kotlin/src/main/kotlin/com/github/oslokommune/oslonokkelen/adapter/thing/ThingsStateSnapshot.kt b/adapter-sdk-kotlin/src/main/kotlin/com/github/oslokommune/oslonokkelen/adapter/thing/ThingsStateSnapshot.kt index 6c2007b..0817326 100644 --- a/adapter-sdk-kotlin/src/main/kotlin/com/github/oslokommune/oslonokkelen/adapter/thing/ThingsStateSnapshot.kt +++ b/adapter-sdk-kotlin/src/main/kotlin/com/github/oslokommune/oslonokkelen/adapter/thing/ThingsStateSnapshot.kt @@ -11,6 +11,12 @@ data class ThingStateSnapshot( val data: PersistentMap = persistentMapOf() ) { + init { + if (data.keys.any { id -> id.thingId != thingId }) { + throw IllegalArgumentException("Tried to create snapshot of $thingId with state belonging to a different thing: $data") + } + } + constructor(first: ThingState, vararg state: ThingState) : this( thingId = first.thingId, data = (state.toList() + first).associateBy { it.key }.toPersistentHashMap() diff --git a/adapter-sdk-kotlin/src/test/kotlin/com/github/oslokommune/oslonokkelen/adapter/manifest/ManifestSnapshotTest.kt b/adapter-sdk-kotlin/src/test/kotlin/com/github/oslokommune/oslonokkelen/adapter/manifest/ManifestSnapshotTest.kt index e75669b..8ab4388 100644 --- a/adapter-sdk-kotlin/src/test/kotlin/com/github/oslokommune/oslonokkelen/adapter/manifest/ManifestSnapshotTest.kt +++ b/adapter-sdk-kotlin/src/test/kotlin/com/github/oslokommune/oslonokkelen/adapter/manifest/ManifestSnapshotTest.kt @@ -9,14 +9,15 @@ import com.github.oslokommune.oslonokkelen.adapter.thing.ThingDescription import com.github.oslokommune.oslonokkelen.adapter.thing.ThingId import com.github.oslokommune.oslonokkelen.adapter.thing.ThingState import com.github.oslokommune.oslonokkelen.adapter.thing.ThingStateSnapshot +import java.time.Instant import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentMapOf import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertSame import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows -import java.time.Instant internal class ManifestSnapshotTest { @@ -397,4 +398,69 @@ internal class ManifestSnapshotTest { } + @Nested + inner class Debug { + + @Test + fun `Add thing to empty manifest`() { + val original = ManifestSnapshot() + val door = createThingDescription("door") + val withDoor = original + door + + val expectedManifest = ManifestSnapshot( + version = 2, + things = persistentMapOf(door.id to door) + ) + + assertEquals(expectedManifest, withDoor) + } + + @Test + fun `Update thing description`() { + val original = ManifestSnapshot() + val originalDoor = createThingDescription("door", "First door description") + val updatedDoor = createThingDescription("door", "Second door description") + val withOriginalDoor = original + originalDoor + val withUpdatedDoor = withOriginalDoor + updatedDoor + + val expectedManifest = ManifestSnapshot( + version = 3, + things = persistentMapOf(originalDoor.id to updatedDoor) + ) + + assertEquals(expectedManifest, withUpdatedDoor) + } + + @Test + fun `Should keep state when updating description`() { + val original = ManifestSnapshot() + val originalDoor = createThingDescription("door", "First door description") + val updatedDoor = createThingDescription("door", "Second door description") + val withOriginalDoor = original + originalDoor + val openState = ThingState.OpenPosition(timestamp = Instant.now(), originalDoor.id, open = true) + val withOriginalDoorAndState = withOriginalDoor + openState + val withUpdatedDoorAndState = withOriginalDoorAndState + updatedDoor + + val expectedManifest = ManifestSnapshot( + version = 4, + things = persistentMapOf(originalDoor.id to updatedDoor), + thingStates = persistentMapOf(originalDoor.id to ThingStateSnapshot(openState)) + ) + + assertEquals(expectedManifest, withUpdatedDoorAndState) + } + + } + + private fun createThingDescription(id: String, description: String = "Description of $id"): ThingDescription { + return ThingDescription( + id = ThingId(id), + description = description, + adminRole = "admin_role", + tags = emptySet(), + link = null, + timeWithoutMessageBeforeAlert = null + ) + } + } \ No newline at end of file diff --git a/adapter-sdk-kotlin/src/test/kotlin/com/github/oslokommune/oslonokkelen/adapter/protobuf/ProtobufSerializerTest.kt b/adapter-sdk-kotlin/src/test/kotlin/com/github/oslokommune/oslonokkelen/adapter/protobuf/ProtobufSerializerTest.kt index 600ad56..0f8a782 100644 --- a/adapter-sdk-kotlin/src/test/kotlin/com/github/oslokommune/oslonokkelen/adapter/protobuf/ProtobufSerializerTest.kt +++ b/adapter-sdk-kotlin/src/test/kotlin/com/github/oslokommune/oslonokkelen/adapter/protobuf/ProtobufSerializerTest.kt @@ -186,6 +186,14 @@ internal class ProtobufSerializerTest { private val timestamp = Instant.now().truncatedTo(ChronoUnit.SECONDS) + private val deviceType = ThingState.DeviceType( + timestamp = timestamp, + thingId = frontDoor.id, + vendor = "Armin", + model = "Oslonøkkelboksen", + firmwareVersion = "v0.5.55" + ) + @Test fun `Serialize and parse manifest with thing`() { test(ManifestSnapshot() + frontDoor) @@ -196,6 +204,11 @@ internal class ProtobufSerializerTest { test(ManifestSnapshot() + frontDoor + unlockFrontDoor) } + @Test + fun `Serialize and parse manifest with reported firmware`() { + test(ManifestSnapshot() + frontDoor + deviceType) + } + @Test fun `Serialize and parse manifest with locked door`() { val lockedDoor = ThingState.Lock(