From 11d868dc278053742adf2a82f82c0ba11fe376ea Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Fri, 7 Jul 2023 18:49:00 +0200 Subject: [PATCH] Diabetes levels of DSL and sugar --- .../wot/dijkstra/sq/CertSynopsis.kt | 6 - .../wot/dijkstra/sq/Certification.kt | 2 +- .../org/pgpainless/wot/dijkstra/sq/Network.kt | 2 +- .../wot/dijkstra/CertSynopsisTest.kt | 29 ++++ .../org/pgpainless/wot/dijkstra/NetworkDSL.kt | 163 ++++++++++++++++++ .../pgpainless/wot/dijkstra/NetworkTest.kt | 74 ++++++-- 6 files changed, 254 insertions(+), 22 deletions(-) create mode 100644 wot-dijkstra/src/test/kotlin/org/pgpainless/wot/dijkstra/CertSynopsisTest.kt create mode 100644 wot-dijkstra/src/test/kotlin/org/pgpainless/wot/dijkstra/NetworkDSL.kt diff --git a/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/CertSynopsis.kt b/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/CertSynopsis.kt index 9f475372..ad3a1614 100644 --- a/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/CertSynopsis.kt +++ b/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/CertSynopsis.kt @@ -20,12 +20,6 @@ data class CertSynopsis( val revocationState: RevocationState = RevocationState.notRevoked(), val userIds : Map = mapOf()) { - constructor(fingerprint: String, - expirationTime: Date? = null, - revocationState: RevocationState = RevocationState.notRevoked(), - userIds: Map = mapOf()): - this(Fingerprint(fingerprint), expirationTime, revocationState, userIds) - override fun toString(): String { return if (userIds.isEmpty()) { "$fingerprint" diff --git a/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/Certification.kt b/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/Certification.kt index c834504f..259ab703 100644 --- a/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/Certification.kt +++ b/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/Certification.kt @@ -44,7 +44,7 @@ data class Certification( constructor( issuer: CertSynopsis, target: CertSynopsis, - targetUserId: String?, + targetUserId: String? = null, creationTime: Date) : this(issuer, target, targetUserId, creationTime, null, true, 120, Depth.limited(0), RegexSet.wildcard()) diff --git a/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/Network.kt b/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/Network.kt index ca88cfcc..284b24b9 100644 --- a/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/Network.kt +++ b/wot-dijkstra/src/main/kotlin/org/pgpainless/wot/dijkstra/sq/Network.kt @@ -67,7 +67,7 @@ class Network( } class Builder internal constructor() { - private val nodes: MutableMap = mutableMapOf() + val nodes: MutableMap = mutableMapOf() private val protoEdges: MutableMap, CertificationSet> = mutableMapOf() private var referenceTime: ReferenceTime = ReferenceTime.now() diff --git a/wot-dijkstra/src/test/kotlin/org/pgpainless/wot/dijkstra/CertSynopsisTest.kt b/wot-dijkstra/src/test/kotlin/org/pgpainless/wot/dijkstra/CertSynopsisTest.kt new file mode 100644 index 00000000..ebbc6b2b --- /dev/null +++ b/wot-dijkstra/src/test/kotlin/org/pgpainless/wot/dijkstra/CertSynopsisTest.kt @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2023 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.wot.dijkstra + +import org.junit.jupiter.api.Test +import kotlin.test.assertEquals + +class CertSynopsisTest: NetworkDSL { + + @Test + fun `Fingerprint 'A' toString`() { + val node = CertSynopsis("A") + assertEquals("A", node.toString()) + } + + @Test + fun `Fingerprint 'a' toString`() { + val node = CertSynopsis("a") + assertEquals("A", node.toString()) + } + + @Test + fun `Fingerprint 'A' and UserID toString`() { + val node = CertSynopsis("A", "Alice ") + assertEquals("A (Alice )", node.toString()) + } +} \ No newline at end of file diff --git a/wot-dijkstra/src/test/kotlin/org/pgpainless/wot/dijkstra/NetworkDSL.kt b/wot-dijkstra/src/test/kotlin/org/pgpainless/wot/dijkstra/NetworkDSL.kt new file mode 100644 index 00000000..57250b1d --- /dev/null +++ b/wot-dijkstra/src/test/kotlin/org/pgpainless/wot/dijkstra/NetworkDSL.kt @@ -0,0 +1,163 @@ +// SPDX-FileCopyrightText: 2023 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.wot.dijkstra + +import org.pgpainless.wot.dijkstra.sq.* +import java.util.* + +/** + * Tons of useful DSL for [Network]-related testing. + */ +interface NetworkDSL { + + /** + * Create [CertSynopsis] from [String] fingerprint. + */ + fun CertSynopsis(fingerprint: String): CertSynopsis = + CertSynopsis(Fingerprint(fingerprint), null, RevocationState.notRevoked(), mapOf()) + + /** + * Create [CertSynopsis] from [String] fingerprint and non-revoked [userId]. + */ + fun CertSynopsis(fingerprint: String, userId: String): CertSynopsis = CertSynopsis( + Fingerprint(fingerprint), null, RevocationState.notRevoked(), mapOf(userId to RevocationState.notRevoked())) + + fun CertSynopsis(original: CertSynopsis, userId: String): CertSynopsis = CertSynopsis( + original.fingerprint, original.expirationTime, original.revocationState, original.userIds.plus(userId to RevocationState.notRevoked()) + ) + + /** + * Create [Certification] from two [CertSynopsis] nodes. + */ + fun Certification(issuer: CertSynopsis, target: CertSynopsis): Certification = + Certification(issuer, target, null, Date()) + + /** + * Create [Certification] from two [CertSynopsis] nodes and a target [userId]. + */ + fun Certification(issuer: CertSynopsis, target: CertSynopsis, userId: String): Certification = + Certification(issuer, target, userId, Date()) + + /** + * Add a single [CertSynopsis] built from a [String] fingerprint to the builder. + */ + fun Network.Builder.addNode(fingerprint: String): Network.Builder { + return addNode(CertSynopsis(fingerprint)) + } + + /** + * Add a single [CertSynopsis] built from a [String] fingerprint and [userId] to the builder. + */ + fun Network.Builder.addNode(fingerprint: String, userId: String): Network.Builder { + return addNode(CertSynopsis(fingerprint, userId)) + } + + /** + * Add multiple [CertSynopsis] nodes built from [String] fingerprints to the builder. + */ + fun Network.Builder.addNodes(vararg fingerprints: String) { + for (fingerprint in fingerprints) { + addNode(fingerprint) + } + } + + /** + * Add an edge between the [CertSynopsis] with fingerprint [issuer] and + * the [CertSynopsis] with fingerprint [target]. + * If either the issuer or target node doesn't exist, throw an [IllegalArgumentException]. + */ + fun Network.Builder.addEdge(issuer: String, target: String): Network.Builder { + val issuerNode = nodes[Fingerprint(issuer)]!! + val targetNode = nodes[Fingerprint(target)]!! + return addEdge(Certification(issuerNode, targetNode)) + } + + /** + * Add an edge for [userId] between the [CertSynopsis] with fingerprint [issuer] and + * the [CertSynopsis] with fingerprint [target]. + * If either the issuer or target node doesn't exist, throw an [IllegalArgumentException]. + */ + fun Network.Builder.addEdge(issuer: String, target: String, userId: String): Network.Builder { + val issuerNode = nodes[Fingerprint(issuer)]!! + val targetNode = nodes[Fingerprint(target)]!! + return addEdge(Certification(issuerNode, targetNode, userId)) + } + + /** + * Add an edge between the issuer and target node. If either of them doesn't exist, add + * a new node for them to the builder. + */ + fun Network.Builder.buildEdge(issuer: String, target: String): Network.Builder { + val issuerNode = nodes.getOrPut(Fingerprint(issuer)) { CertSynopsis(issuer) } + val targetNode = nodes.getOrPut(Fingerprint(target)) { CertSynopsis(target) } + return addEdge(Certification(issuerNode, targetNode)) + } + + /** + * Add an edge for [userId] between the issuer and the target node. If either of them doesn't + * exist, add a new node. + * If the target node exists, but doesn't carry the [userId], replace it with a copy with + * the [userId] inserted. + */ + fun Network.Builder.buildEdge(issuer: String, target: String, userId: String): Network.Builder { + val issuerNode = nodes.getOrPut(Fingerprint(issuer)) { CertSynopsis(issuer)} + val targetNode = CertSynopsis(nodes.getOrPut(Fingerprint(target)) { CertSynopsis(target, userId) }, userId) + return addEdge(Certification(issuerNode, targetNode, userId)) + } + + fun Network.Builder.buildEdge(issuer: String, target: String, amount: Int, depth: Int): Network.Builder { + val issuerNode = nodes.getOrPut(Fingerprint(issuer)) { CertSynopsis(issuer) } + val targetNode = nodes.getOrPut(Fingerprint(target)) { CertSynopsis(target) } + return addEdge(Certification(issuerNode, targetNode, null, Date(), null, true, amount, Depth.auto(depth), RegexSet.wildcard())) + } + + fun Network.Builder.buildEdge(issuer: String, target: String, amount: Int, depth: Int, regexSet: RegexSet): Network.Builder { + val issuerNode = nodes.getOrPut(Fingerprint(issuer)) { CertSynopsis(issuer) } + val targetNode = nodes.getOrPut(Fingerprint(target)) { CertSynopsis(target) } + return addEdge(Certification(issuerNode, targetNode, null, Date(), null, true, amount, Depth.auto(depth), regexSet)) + } + + /** + * Set the reference time of the builder to now. + */ + fun Network.Builder.now(): Network.Builder { + return setReferenceTime(ReferenceTime.now()) + } + + fun Network.getEdgesFor(issuer: Fingerprint, target: Fingerprint): CertificationSet? { + return edges[issuer]?.find { it.target.fingerprint == target } + } + + fun Network.getEdgesFor(issuer: String, target: String): CertificationSet? { + return getEdgesFor(Fingerprint(issuer), Fingerprint(target)) + } + + fun Network.getEdgeFor(issuer: Fingerprint, target: Fingerprint): List? { + return getEdgeFor(issuer, target, null) + } + + fun Network.getEdgeFor(issuer: Fingerprint, target: Fingerprint, userId: String?): List? { + return getEdgesFor(issuer, target)?.certifications?.get(userId) + } + + fun Network.getEdgeFor(issuer: String, target: String): List? { + return getEdgeFor(issuer, target, null) + } + + fun Network.getEdgeFor(issuer: String, target: String, userId: String?): List? { + return getEdgeFor(Fingerprint(issuer), Fingerprint(target), userId) + } + + /** + * Lambda with Receiver. + * + * @see + */ + fun buildNetwork(builderAction: Network.Builder.() -> Unit): Network { + val builder = Network.builder() + builder.builderAction() + return builder.build() + } +} \ No newline at end of file diff --git a/wot-dijkstra/src/test/kotlin/org/pgpainless/wot/dijkstra/NetworkTest.kt b/wot-dijkstra/src/test/kotlin/org/pgpainless/wot/dijkstra/NetworkTest.kt index 32612a1f..0e96b434 100644 --- a/wot-dijkstra/src/test/kotlin/org/pgpainless/wot/dijkstra/NetworkTest.kt +++ b/wot-dijkstra/src/test/kotlin/org/pgpainless/wot/dijkstra/NetworkTest.kt @@ -5,15 +5,14 @@ package org.pgpainless.wot.dijkstra import org.junit.jupiter.api.Test -import org.pgpainless.wot.dijkstra.sq.CertSynopsis -import org.pgpainless.wot.dijkstra.sq.Certification -import org.pgpainless.wot.dijkstra.sq.Network +import org.pgpainless.wot.dijkstra.sq.Fingerprint import org.pgpainless.wot.dijkstra.sq.Network.Companion.empty import org.pgpainless.wot.dijkstra.sq.ReferenceTime.Companion.now -import java.util.Date +import org.pgpainless.wot.dijkstra.sq.RegexSet import kotlin.test.assertEquals +import kotlin.test.assertTrue -class NetworkTest { +class NetworkTest: NetworkDSL { @Test fun testEmptyNetworkIsEmpty() { @@ -27,19 +26,66 @@ class NetworkTest { assertEquals(0, network.numberOfSignatures) } + @Test + fun `verify that setting referenceTime works`() { + val ref = now() + val network = buildNetwork { + setReferenceTime(ref) + } + + assertEquals(ref, network.referenceTime) + } + + @Test + fun `verify that adding a single node works`() { + val network = buildNetwork { addNode("A") } + + assertEquals(1, network.nodes.size) + assertTrue { network.nodes.containsKey(Fingerprint("A")) } + } + + @Test + fun `verify that adding multiple, non-connected nodes works`() { + val network = buildNetwork { addNodes("A", "B", "C", "D") } + assertEquals(4, network.nodes.size) + } + @Test fun testSimpleNetwork() { - val alice = CertSynopsis("A") - val bob = CertSynopsis("B") + val network = buildNetwork { + buildEdge("A", "B") + buildEdge("A", "C", "Charlie ") + } - val edge = Certification(alice, bob, null, Date()) + assertEquals("Network with 3 nodes, 2 edges:\n" + + "A certifies binding: null <-> B [120]\n" + + "A certifies binding: Charlie <-> C [120]\n", + network.toString()) + } - val network = Network.builder() - .addNode(alice) - .addNode(bob) - .addEdge(edge) - .build() + @Test + fun `verify that network with 2 edges between 2 nodes keeps edges`() { + val network = buildNetwork { + buildEdge("A", "B") + buildEdge("A", "B", "Bob") + } - println(network) + assertEquals(2, network.numberOfSignatures) + } + + @Test + fun `play with depths, amounts and regexes`() { + val network = buildNetwork { + buildEdge("A", "B", 120, 10) + buildEdge("B", "C", 60, 5, RegexSet.fromExpression("*")) + buildEdge("A", "C", 10, 0) + } + + assertEquals(3, network.nodes.size) + assertEquals(3, network.numberOfEdges) + assertEquals(3, network.numberOfSignatures) + + assertEquals(120, network.getEdgeFor("A", "B")!!.first().trustAmount) + assertEquals(10, network.getEdgeFor("A", "B")!!.first().trustDepth.value()) } } \ No newline at end of file