From f027fd5c77310b15e66126cb322259bdd288d770 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Thu, 16 Jan 2020 17:19:58 +0000 Subject: [PATCH] ENT-4652: Add unit tests for parsing attachment fix-up rules. (#5859) --- .../internal/cordapp/CordappProviderImpl.kt | 11 +- .../cordapp/CordappProviderImplTests.kt | 120 ++++++++++++++++++ 2 files changed, 126 insertions(+), 5 deletions(-) diff --git a/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappProviderImpl.kt b/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappProviderImpl.kt index 52a23e12ce..0a01cea940 100644 --- a/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappProviderImpl.kt +++ b/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappProviderImpl.kt @@ -30,6 +30,7 @@ open class CordappProviderImpl(val cordappLoader: CordappLoader, private val cordappConfigProvider: CordappConfigProvider, private val attachmentStorage: AttachmentStorage) : SingletonSerializeAsToken(), CordappProviderInternal { companion object { + const val COMMENT_MARKER = '#' private val log = contextLogger() } @@ -123,8 +124,8 @@ open class CordappProviderImpl(val cordappLoader: CordappLoader, isValidFixup(fixupConnection.jarFile) }.flatMapTo(ArrayList()) { fixupConnection -> fixupConnection.inputStream.bufferedReader().useLines { lines -> - lines.filter(String::isNotBlank).map { line -> - val tokens = line.split("=>", limit = 2) + lines.map { it.substringBefore(COMMENT_MARKER) }.map(String::trim).filterNot(String::isEmpty).map { line -> + val tokens = line.split("=>") require(tokens.size == 2) { "Invalid fix-up line '$line' in '${fixupConnection.jarFile.name}'" } @@ -148,9 +149,9 @@ open class CordappProviderImpl(val cordappLoader: CordappLoader, } private fun parseIds(ids: String): Set { - return ids.split(",").mapTo(LinkedHashSet()) { - AttachmentId.parse(it.trim()) - } + return ids.split(",").map(String::trim) + .filterNot(String::isEmpty) + .mapTo(LinkedHashSet(), SecureHash.Companion::parse) } /** diff --git a/node/src/test/kotlin/net/corda/node/internal/cordapp/CordappProviderImplTests.kt b/node/src/test/kotlin/net/corda/node/internal/cordapp/CordappProviderImplTests.kt index 005a560b71..d1594b5ecb 100644 --- a/node/src/test/kotlin/net/corda/node/internal/cordapp/CordappProviderImplTests.kt +++ b/node/src/test/kotlin/net/corda/node/internal/cordapp/CordappProviderImplTests.kt @@ -2,6 +2,7 @@ package net.corda.node.internal.cordapp import com.typesafe.config.Config import com.typesafe.config.ConfigFactory +import net.corda.core.node.services.AttachmentId import net.corda.core.node.services.AttachmentStorage import net.corda.node.VersionInfo import net.corda.testing.common.internal.testNetworkParameters @@ -11,7 +12,15 @@ import org.assertj.core.api.Assertions.assertThat import org.junit.Assert.* import org.junit.Before import org.junit.Test +import java.io.File +import java.io.FileOutputStream import java.net.URL +import java.util.jar.JarOutputStream +import java.util.zip.Deflater.NO_COMPRESSION +import java.util.zip.ZipEntry +import java.util.zip.ZipEntry.DEFLATED +import java.util.zip.ZipEntry.STORED +import kotlin.test.assertFailsWith class CordappProviderImplTests { private companion object { @@ -21,9 +30,29 @@ class CordappProviderImplTests { val emptyJAR: URL = this::class.java.getResource("empty.jar") val validConfig: Config = ConfigFactory.parseString("key=value") + @JvmField + val ID1 = AttachmentId.randomSHA256() + @JvmField + val ID2 = AttachmentId.randomSHA256() + @JvmField + val ID3 = AttachmentId.randomSHA256() + @JvmField + val ID4 = AttachmentId.randomSHA256() + val stubConfigProvider = object : CordappConfigProvider { override fun getConfigByName(name: String): Config = ConfigFactory.empty() } + + fun directoryEntry(internalName: String) = ZipEntry("$internalName/").apply { + method = STORED + compressedSize = 0 + size = 0 + crc = 0 + } + + fun fileEntry(internalName: String) = ZipEntry(internalName).apply { + method = DEFLATED + } } private lateinit var attachmentStore: AttachmentStorage @@ -84,6 +113,97 @@ class CordappProviderImplTests { assertThat(expected.getString("key")).isEqualTo("value") } + @Test + fun `test fixup rule that adds attachment`() { + val fixupJar = File.createTempFile("fixup", ".jar") + .writeFixupRules("$ID1 => $ID2, $ID3") + val fixedIDs = with(newCordappProvider(fixupJar.toURI().toURL())) { + start() + fixupAttachmentIds(listOf(ID1)) + } + assertThat(fixedIDs).containsExactly(ID2, ID3) + } + + @Test + fun `test fixup rule that deletes attachment`() { + val fixupJar = File.createTempFile("fixup", ".jar") + .writeFixupRules("$ID1 =>") + val fixedIDs = with(newCordappProvider(fixupJar.toURI().toURL())) { + start() + fixupAttachmentIds(listOf(ID1)) + } + assertThat(fixedIDs).isEmpty() + } + + @Test + fun `test fixup rule with blank LHS`() { + val fixupJar = File.createTempFile("fixup", ".jar") + .writeFixupRules(" => $ID2") + val ex = assertFailsWith { + newCordappProvider(fixupJar.toURI().toURL()).start() + } + assertThat(ex).hasMessageContaining( + "Forbidden empty list of source attachment IDs in '${fixupJar.absolutePath}'" + ) + } + + @Test + fun `test fixup rule without arrows`() { + val rule = " $ID1 " + val fixupJar = File.createTempFile("fixup", ".jar") + .writeFixupRules(rule) + val ex = assertFailsWith { + newCordappProvider(fixupJar.toURI().toURL()).start() + } + assertThat(ex).hasMessageContaining( + "Invalid fix-up line '${rule.trim()}' in '${fixupJar.absolutePath}'" + ) + } + + @Test + fun `test fixup rule with too many arrows`() { + val rule = " $ID1 => $ID2 => $ID3 " + val fixupJar = File.createTempFile("fixup", ".jar") + .writeFixupRules(rule) + val ex = assertFailsWith { + newCordappProvider(fixupJar.toURI().toURL()).start() + } + assertThat(ex).hasMessageContaining( + "Invalid fix-up line '${rule.trim()}' in '${fixupJar.absolutePath}'" + ) + } + + @Test + fun `test fixup file containing multiple rules and comments`() { + val fixupJar = File.createTempFile("fixup", ".jar").writeFixupRules( + "# Whole line comment", + "\t$ID1,$ID2 => $ID2,, $ID3 # EOl comment", + " # Empty line with comment", + "", + "$ID3 => $ID4" + ) + val fixedIDs = with(newCordappProvider(fixupJar.toURI().toURL())) { + start() + fixupAttachmentIds(listOf(ID2, ID1)) + } + assertThat(fixedIDs).containsExactlyInAnyOrder(ID2, ID4) + } + + private fun File.writeFixupRules(vararg lines: String): File { + JarOutputStream(FileOutputStream(this)).use { jar -> + jar.setMethod(DEFLATED) + jar.setLevel(NO_COMPRESSION) + jar.putNextEntry(directoryEntry("META-INF")) + jar.putNextEntry(fileEntry("META-INF/Corda-Fixups")) + for (line in lines) { + jar.write(line.toByteArray()) + jar.write('\r'.toInt()) + jar.write('\n'.toInt()) + } + } + return this + } + private fun newCordappProvider(vararg urls: URL): CordappProviderImpl { val loader = JarScanningCordappLoader.fromJarUrls(urls.toList(), VersionInfo.UNKNOWN) return CordappProviderImpl(loader, stubConfigProvider, attachmentStore).apply { start() }