Skip to content

Commit

Permalink
feat: Implement ip-blocks list command (#999)
Browse files Browse the repository at this point in the history
  • Loading branch information
pawelpasterz authored Aug 18, 2020
1 parent 880d468 commit f268413
Show file tree
Hide file tree
Showing 10 changed files with 293 additions and 7 deletions.
4 changes: 3 additions & 1 deletion test_runner/src/main/kotlin/ftl/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import ftl.cli.FirebaseCommand
import ftl.cli.firebase.CancelCommand
import ftl.cli.firebase.RefreshCommand
import ftl.cli.firebase.test.AndroidCommand
import ftl.cli.firebase.test.IPBlocksCommand
import ftl.cli.firebase.test.IosCommand
import ftl.cli.firebase.test.NetworkProfilesCommand
import ftl.cli.firebase.test.ProvidedSoftwareCommand
Expand All @@ -25,7 +26,8 @@ import picocli.CommandLine
CancelCommand::class,
AuthCommand::class,
ProvidedSoftwareCommand::class,
NetworkProfilesCommand::class
NetworkProfilesCommand::class,
IPBlocksCommand::class
]
)
class Main : Runnable {
Expand Down
4 changes: 3 additions & 1 deletion test_runner/src/main/kotlin/ftl/cli/firebase/TestCommand.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ftl.cli.firebase

import ftl.cli.firebase.test.AndroidCommand
import ftl.cli.firebase.test.IPBlocksCommand
import ftl.cli.firebase.test.IosCommand
import ftl.cli.firebase.test.NetworkProfilesCommand
import ftl.cli.firebase.test.ProvidedSoftwareCommand
Expand All @@ -14,7 +15,8 @@ import picocli.CommandLine.Command
AndroidCommand::class,
IosCommand::class,
NetworkProfilesCommand::class,
ProvidedSoftwareCommand::class
ProvidedSoftwareCommand::class,
IPBlocksCommand::class
],
usageHelpAutoWidth = true
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package ftl.cli.firebase.test

import ftl.cli.firebase.test.ipblocks.IPBlocksListCommand
import picocli.CommandLine

@CommandLine.Command(
name = "ip-blocks",
synopsisHeading = "",
subcommands = [IPBlocksListCommand::class],
header = ["Explore IP blocks used by Firebase Test Lab devices."],
usageHelpAutoWidth = true
)
class IPBlocksCommand : Runnable {
override fun run() {
CommandLine.usage(IPBlocksCommand(), System.out)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import picocli.CommandLine
NetworkProfilesListCommand::class,
NetworkProfilesDescribeCommand::class
],
header = ["Explore network profiles available for testing."],
usageHelpAutoWidth = true
)
class NetworkProfilesCommand : Runnable {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package ftl.cli.firebase.test.ipblocks

import ftl.environment.ipBlocksListAsTable
import picocli.CommandLine

@CommandLine.Command(
name = "list",
sortOptions = false,
headerHeading = "",
synopsisHeading = "%n",
descriptionHeading = "%n@|bold,underline Description:|@%n%n",
parameterListHeading = "%n@|bold,underline Parameters:|@%n",
optionListHeading = "%n@|bold,underline Options:|@%n",
header = ["List all IP address blocks used by Firebase Test Lab devices"],
usageHelpAutoWidth = true
)
class IPBlocksListCommand : Runnable {
override fun run() {
println(ipBlocksListAsTable())
}
}
54 changes: 54 additions & 0 deletions test_runner/src/main/kotlin/ftl/environment/ListIPBlocks.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package ftl.environment

import com.google.api.services.testing.model.Date
import com.google.api.services.testing.model.DeviceIpBlock
import ftl.gc.deviceIPBlocks
import ftl.reports.api.twoDigitString
import ftl.util.TableColumn
import ftl.util.TableStyle
import ftl.util.buildTable
import java.util.Objects.isNull

fun ipBlocksListAsTable() = deviceIPBlocks()
.toNullProof()
.createDataMap()
.collectDataPerColumn()
.buildTable()

private fun List<DeviceIpBlock>.toNullProof() =
map { IpBlocksNonNull(it.block.orUnable, it.form.orUnable, addedDate = it.addedDate.prettyDate) }

private fun List<IpBlocksNonNull>.createDataMap() = fold(mutableMapOf<String, MutableList<String>>()) { map, ipBlock ->
map.apply {
getOrCreateList(IP_BLOCK).add(ipBlock.block)
getOrCreateList(IP_FORM).add(ipBlock.form)
getOrCreateList(IP_ADDED_DATE).add(ipBlock.addedDate)
}
}

private fun Map<String, List<String>>.collectDataPerColumn() = map { (header, data) -> TableColumn(header, data) }

private fun List<TableColumn>.buildTable() =
if (isNotEmpty()) buildTable(*toTypedArray(), tableStyle = TableStyle.DEFAULT)
else "--Flank was unable to get data from TestLab--"

// yyyy-mm-dd
private val Date?.prettyDate
get() = this?.run { if (allNotNull(year, month, day)) "$year-${month.twoDigitString()}-${day.twoDigitString()}" else null }
?: UNABLE

private fun allNotNull(vararg nullable: Any?) = nullable.none { isNull(it) }

private val String?.orUnable
get() = this ?: UNABLE

private const val IP_BLOCK = "BLOCK"
private const val IP_FORM = "FORM"
private const val IP_ADDED_DATE = "ADDED_DATE"
private const val UNABLE = "[Unable to fetch]"

private data class IpBlocksNonNull(
val block: String,
val form: String,
val addedDate: String
)
8 changes: 8 additions & 0 deletions test_runner/src/main/kotlin/ftl/gc/GcTesting.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import ftl.config.FtlConstants.JSON_FACTORY
import ftl.config.FtlConstants.applicationName
import ftl.config.FtlConstants.httpCredential
import ftl.config.FtlConstants.httpTransport
import ftl.http.executeWithRetry

object GcTesting {
val get: Testing by lazy {
Expand All @@ -17,3 +18,10 @@ object GcTesting {
builder.build()
}
}

fun deviceIPBlocks() = GcTesting.get.testEnvironmentCatalog()
.get("DEVICE_IP_BLOCKS")
.executeWithRetry()
?.deviceIpBlockCatalog
?.ipBlocks
.orEmpty()
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package ftl.cli.firebase.test

import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test
import org.junit.contrib.java.lang.system.SystemOutRule

class IPBlocksCommandTest {

@get:Rule
val out = SystemOutRule().enableLog().muteForSuccessfulTests() as SystemOutRule

@Test
fun printHelp() {
IPBlocksCommand().run()

val expected = """
Explore IP blocks used by Firebase Test Lab devices.
ip-blocks [COMMAND]
Commands:
list List all IP address blocks used by Firebase Test Lab devices
""".trimIndent()

val actual = out.log.trim()

assertEquals(expected, actual)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ class NetworkProfilesCommandTest {
NetworkProfilesCommand().run()

val expected = listOf(
"network-profiles [COMMAND]",
"Commands:",
" list List all network profiles available for testing",
" describe Describe a network profile",
""
"Explore network profiles available for testing.",
"network-profiles [COMMAND]",
"Commands:",
" list List all network profiles available for testing",
" describe Describe a network profile",
""
).joinToString("\n")

val actual = systemOutRule.log.normalizeLineEnding()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package ftl.cli.firebase.test.ipblocks

import com.google.api.services.testing.model.Date
import com.google.api.services.testing.model.DeviceIpBlock
import ftl.gc.deviceIPBlocks
import ftl.test.util.runMainCommand
import io.mockk.every
import io.mockk.mockkStatic
import io.mockk.unmockkAll
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.contrib.java.lang.system.SystemOutRule

class IPBlocksListCommandTest {

@get:Rule
val out = SystemOutRule().enableLog().muteForSuccessfulTests() as SystemOutRule

@Before
fun setup() = mockkStatic("ftl.gc.GcTestingKt")

@After
fun tearDown() = unmockkAll()

@Test
fun `should print error message if no data was provided from FTL`() {
val expected = "--Flank was unable to get data from TestLab--"
every { deviceIPBlocks() } returns emptyList()

out.clearLog()
runMainCommand("ip-blocks", "list")

val result = out.log.trim()

assertEquals(expected, result)
}

@Test
fun `should print ips for devices`() {
val ips = listOf(
DeviceIpBlock().apply {
block = "1.1.1.1/1"
form = "AnyForm"
addedDate = Date().apply {
day = 1
month = 1
year = 1111
}
},
DeviceIpBlock().apply {
block = "2.2.2.2/2"
form = "OtherForm"
addedDate = Date().apply {
day = 12
month = 12
year = 1212
}
}
)

every { deviceIPBlocks() } returns ips
val expected = """
┌───────────┬───────────┬────────────┐
│ BLOCK │ FORM │ ADDED_DATE │
├───────────┼───────────┼────────────┤
│ 1.1.1.1/1 │ AnyForm │ 1111-01-01 │
│ 2.2.2.2/2 │ OtherForm │ 1212-12-12 │
└───────────┴───────────┴────────────┘
""".trimIndent()
out.clearLog()

runMainCommand("ip-blocks", "list")

val result = out.log.trim()

assertEquals(expected, result)
}

@Test
fun `should not fail when FTL returns null in any of values`() {
val ips = listOf(
DeviceIpBlock().apply {
block = "1.1.1.1/1"
form = "AnyForm"
addedDate = Date().apply {
day = null
month = 1
year = 1111
}
},
DeviceIpBlock().apply {
block = null
form = "MissingIpForm"
addedDate = Date().apply {
day = 12
month = 2
year = 1212
}
},
DeviceIpBlock().apply {
block = "2.2.2.2/2"
form = "OtherForm"
addedDate = Date().apply {
day = 12
month = null
year = 1212
}
},
DeviceIpBlock().apply {
block = "3.3.3.3/4"
form = "FunnyForm"
addedDate = Date().apply {
day = 8
month = 2
year = null
}
},
DeviceIpBlock().apply {
block = "4.4.4.4/4"
form = null
addedDate = Date().apply {
day = 8
month = 2
year = 1523
}
}
)

every { deviceIPBlocks() } returns ips
val expected = """
┌───────────────────┬───────────────────┬───────────────────┐
│ BLOCK │ FORM │ ADDED_DATE │
├───────────────────┼───────────────────┼───────────────────┤
│ 1.1.1.1/1 │ AnyForm │ [Unable to fetch] │
│ [Unable to fetch] │ MissingIpForm │ 1212-02-12 │
│ 2.2.2.2/2 │ OtherForm │ [Unable to fetch] │
│ 3.3.3.3/4 │ FunnyForm │ [Unable to fetch] │
│ 4.4.4.4/4 │ [Unable to fetch] │ 1523-02-08 │
└───────────────────┴───────────────────┴───────────────────┘
""".trimIndent()
out.clearLog()

runMainCommand("ip-blocks", "list")

val result = out.log.trim()

assertEquals(expected, result)
}
}

0 comments on commit f268413

Please sign in to comment.