Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

feat: Implement ip-blocks list command #999

Merged
merged 7 commits into from
Aug 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
}
}
4 changes: 2 additions & 2 deletions test_runner/src/test/kotlin/ftl/run/DumpShardsKtTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ class DumpShardsKtTest {
]
},
"junit-ignored": [
"class com.example.test_app.InstrumentedTest#ignoredTest1",
"class com.example.test_app.InstrumentedTest#ignoredTest2",
"class com.example.test_app.InstrumentedTest#ignoredTestWitSuppress",
"class com.example.test_app.InstrumentedTest#ignoredTestWithIgnore",
"class com.example.test_app.bar.BarInstrumentedTest#ignoredTestBar",
"class com.example.test_app.foo.FooInstrumentedTest#ignoredTestFoo"
]
Expand Down