🚧 Add no-negative identity filter
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Tue, 6 Dec 2022 21:40:01 +0000 (22:40 +0100)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Tue, 6 Dec 2022 21:40:01 +0000 (22:40 +0100)
This mirrors exactly what the IdentityManager already does but it adds a test!

src/main/kotlin/net/pterodactylus/sone/freenet/wot/NoNegativeIdentityFilter.kt [new file with mode: 0644]
src/test/kotlin/net/pterodactylus/sone/freenet/wot/NoNegativeIdentityFilterTest.kt [new file with mode: 0644]

diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/NoNegativeIdentityFilter.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/NoNegativeIdentityFilter.kt
new file mode 100644 (file)
index 0000000..44468f2
--- /dev/null
@@ -0,0 +1,37 @@
+package net.pterodactylus.sone.freenet.wot
+
+/**
+ * Filter for identities that retains only those remote identities that do
+ * not have a single negative trust value (explicit or implicit) from any own
+ * identity.
+ */
+class NoNegativeIdentityFilter {
+
+       fun filter(identities: Map<OwnIdentity, Set<Identity>>) =
+               identities.run {
+                       val identitiesWithTrust = values.flatten()
+                               .groupBy { it.id }
+                               .mapValues { (_, identities) ->
+                                       identities.reduce { accIdentity, identity ->
+                                               identity.trust.forEach { (ownIdentity: OwnIdentity?, trust: Trust?) ->
+                                                       accIdentity.setTrust(ownIdentity, trust)
+                                               }
+                                               accIdentity
+                                       }
+                               }
+
+                       mapValues { (_, trustedIdentities) ->
+                               trustedIdentities.filter { trustedIdentity ->
+                                       identitiesWithTrust[trustedIdentity.id]!!.trust.all { it.value.hasZeroOrPositiveTrust() }
+                               }
+                       }
+               }
+
+}
+
+private fun Trust.hasZeroOrPositiveTrust() =
+               if (explicit == null) {
+                       implicit == null || implicit >= 0
+               } else {
+                       explicit >= 0
+               }
diff --git a/src/test/kotlin/net/pterodactylus/sone/freenet/wot/NoNegativeIdentityFilterTest.kt b/src/test/kotlin/net/pterodactylus/sone/freenet/wot/NoNegativeIdentityFilterTest.kt
new file mode 100644 (file)
index 0000000..05c627c
--- /dev/null
@@ -0,0 +1,87 @@
+package net.pterodactylus.sone.freenet.wot
+
+import org.hamcrest.MatcherAssert.assertThat
+import org.hamcrest.Matchers.containsInAnyOrder
+import org.hamcrest.Matchers.empty
+import org.junit.Test
+
+class NoNegativeIdentityFilterTest {
+
+       @Test
+       fun `filter retains all identities with an explicit trust larger than or equal to 0`() {
+               val allIdentities = mapOf(
+                       ownIdentity1.trust(identityA to trustExplicitely(50), identityB to trustExplicitely(0), identityC to trustExplicitely(-50))
+               )
+               val filteredIdentities = filter.filter(allIdentities)
+               assertThat(filteredIdentities[ownIdentity1]!!, containsInAnyOrder(identityA, identityB))
+       }
+
+       @Test
+       fun `filter retains all identities with an implicit trust larger than or equal to 0`() {
+               val allIdentities = mapOf(
+                       ownIdentity1.trust(identityA to trustImplicitely(50), identityB to trustImplicitely(0), identityC to trustImplicitely(-50))
+               )
+               val filteredIdentities = filter.filter(allIdentities)
+               assertThat(filteredIdentities[ownIdentity1]!!, containsInAnyOrder(identityA, identityB))
+       }
+
+       @Test
+       fun `filter retains all identities with an explicit trust larger than or equal to 0 from all local identities that know that remote identity`() {
+               val allIdentities = mapOf(
+                       ownIdentity1.trust(identityA to trustExplicitely(50), identityB to trustExplicitely(0)),
+                       ownIdentity2.trust(identityA to trustExplicitely(50), identityC to trustExplicitely(0))
+               )
+               val filteredIdentities = filter.filter(allIdentities)
+               assertThat(filteredIdentities[ownIdentity1]!!, containsInAnyOrder(identityA, identityB))
+               assertThat(filteredIdentities[ownIdentity2]!!, containsInAnyOrder(identityA, identityC))
+       }
+
+       @Test
+       fun `filter retains all identities with an implicit trust larger than or equal to 0 from all local identities that know that remote identity`() {
+               val allIdentities = mapOf(
+                       ownIdentity1.trust(identityA to trustImplicitely(50), identityB to trustImplicitely(0)),
+                       ownIdentity2.trust(identityA to trustImplicitely(50), identityC to trustImplicitely(0))
+               )
+               val filteredIdentities = filter.filter(allIdentities)
+               assertThat(filteredIdentities[ownIdentity1]!!, containsInAnyOrder(identityA, identityB))
+               assertThat(filteredIdentities[ownIdentity2]!!, containsInAnyOrder(identityA, identityC))
+       }
+
+       @Test
+       fun `strict filter removes all identities that have an explicit negative value for any of the local identities`() {
+               val allIdentities = mapOf(
+                       ownIdentity1.trust(identityA to trustExplicitely(50), identityB to trustExplicitely(50)),
+                       ownIdentity2.trust(identityB to trustExplicitely(-50))
+               )
+               val filteredIdentities = filter.filter(allIdentities)
+               assertThat(filteredIdentities[ownIdentity1]!!, containsInAnyOrder(identityA))
+               assertThat(filteredIdentities[ownIdentity2]!!, empty())
+       }
+
+       @Test
+       fun `strict filter removes all identities that have an implicit negative value for any of the local identities`() {
+               val allIdentities = mapOf(
+                       ownIdentity1.trust(identityA to trustExplicitely(50), identityB to trustExplicitely(50)),
+                       ownIdentity2.trust(identityB to trustImplicitely(-50))
+               )
+               val filteredIdentities = filter.filter(allIdentities)
+               assertThat(filteredIdentities[ownIdentity1]!!, containsInAnyOrder(identityA))
+               assertThat(filteredIdentities[ownIdentity2]!!, empty())
+       }
+
+       private val filter = NoNegativeIdentityFilter()
+       private val ownIdentity1 = createOwnIdentity("1")
+       private val ownIdentity2 = createOwnIdentity("2")
+       private val identityA = createIdentity("A")
+       private val identityB = createIdentity("B")
+       private val identityC = createIdentity("C")
+
+}
+
+private fun OwnIdentity.trust(vararg identityTrust: Pair<Identity, Trust>) =
+       this to identityTrust.map { (identity, trust) -> identity.copy().setTrust(this, trust) }.toSet()
+
+private fun Identity.copy() = DefaultIdentity(id, nickname, requestUri).apply {
+       contexts = this@copy.contexts
+       properties = this@copy.properties
+}