refactor(skills): extract SkillListFilter and SkillScanPresentation

Separate pure skill filtering and scan presentation helpers.
Wire into SkillsScreen with unit test coverage.
This commit is contained in:
Hy4ri committed 2026-09-11 17:58:41 +03:00
1 parent 6876db0393
commit 9eaf9663f3
5 files changed
+323 -54

No files matched your search

@@ -0,0 +1,130 @@
package com.m57.hermescontrol.ui.skills
import com.m57.hermescontrol.data.model.Skill
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
class SkillListFilterTest {
private val sampleSkills =
listOf(
Skill(
name = "code-review",
category = "dev",
description = "Automated code review assistant",
enabled = true,
source = "built-in",
),
Skill(
name = "deploy-bot",
category = "ops",
description = "Deploy to production environments",
enabled = false,
source = "hub",
),
Skill(
name = "image-gen",
category = "creative",
description = "Generate creative assets",
enabled = true,
source = "hub",
),
)
@Test
fun `filterSkills empty query and all statuses returns all`() {
val result =
SkillListFilter.filterSkills(
skills = sampleSkills,
query = "",
selectedStatus = SkillFilter.ALL_STATUSES,
selectedCategory = CATEGORY_ALL,
sourceFilter = null,
)
assertEquals(3, result.size)
}
@Test
fun `filterSkills filters by search query on name and description`() {
val nameResult =
SkillListFilter.filterSkills(
skills = sampleSkills,
query = "code",
selectedStatus = SkillFilter.ALL_STATUSES,
selectedCategory = CATEGORY_ALL,
sourceFilter = null,
)
assertEquals(1, nameResult.size)
assertEquals("code-review", nameResult[0].name)
val descResult =
SkillListFilter.filterSkills(
skills = sampleSkills,
query = "production",
selectedStatus = SkillFilter.ALL_STATUSES,
selectedCategory = CATEGORY_ALL,
sourceFilter = null,
)
assertEquals(1, descResult.size)
assertEquals("deploy-bot", descResult[0].name)
}
@Test
fun `filterSkills filters by enabled and disabled status`() {
val enabledResult =
SkillListFilter.filterSkills(
skills = sampleSkills,
query = "",
selectedStatus = SkillFilter.ENABLED,
selectedCategory = CATEGORY_ALL,
sourceFilter = null,
)
assertEquals(2, enabledResult.size)
assertTrue(enabledResult.all { it.enabled })
val disabledResult =
SkillListFilter.filterSkills(
skills = sampleSkills,
query = "",
selectedStatus = SkillFilter.DISABLED,
selectedCategory = CATEGORY_ALL,
sourceFilter = null,
)
assertEquals(1, disabledResult.size)
assertEquals("deploy-bot", disabledResult[0].name)
}
@Test
fun `filterSkills filters by category and source`() {
val catResult =
SkillListFilter.filterSkills(
skills = sampleSkills,
query = "",
selectedStatus = SkillFilter.ALL_STATUSES,
selectedCategory = "ops",
sourceFilter = null,
)
assertEquals(1, catResult.size)
assertEquals("deploy-bot", catResult[0].name)
val sourceResult =
SkillListFilter.filterSkills(
skills = sampleSkills,
query = "",
selectedStatus = SkillFilter.ALL_STATUSES,
selectedCategory = CATEGORY_ALL,
sourceFilter = "built-in",
)
assertEquals(1, sourceResult.size)
assertEquals("code-review", sourceResult[0].name)
}
@Test
fun `extractCategories and extractSources return distinct sorted values`() {
val categories = SkillListFilter.extractCategories(sampleSkills)
assertEquals(listOf("creative", "dev", "ops"), categories)
val sources = SkillListFilter.extractSources(sampleSkills)
assertEquals(listOf("built-in", "hub"), sources)
}
}
@@ -0,0 +1,64 @@
package com.m57.hermescontrol.ui.skills
import androidx.compose.ui.graphics.Color
import com.m57.hermescontrol.R
import com.m57.hermescontrol.theme.HermesStatusColors
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Test
class SkillScanPresentationTest {
private val dummyColors =
HermesStatusColors(
success = Color(0xFF00FF00),
successContainer = Color(0xFF003300),
onSuccess = Color.White,
warning = Color(0xFFFFFF00),
warningContainer = Color(0xFF333300),
onWarning = Color.Black,
error = Color(0xFFFF0000),
errorContainer = Color(0xFF330000),
onError = Color.White,
info = Color(0xFF0000FF),
infoContainer = Color(0xFF000033),
onInfo = Color.White,
neutral = Color.Gray,
)
@Test
fun `resolveVerdictKind maps safe dangerous and fallback`() {
assertEquals(ScanVerdictKind.SAFE, SkillScanPresentation.resolveVerdictKind("safe"))
assertEquals(ScanVerdictKind.DANGEROUS, SkillScanPresentation.resolveVerdictKind("dangerous"))
assertEquals(ScanVerdictKind.WARNING, SkillScanPresentation.resolveVerdictKind("unknown"))
assertEquals(ScanVerdictKind.WARNING, SkillScanPresentation.resolveVerdictKind(null))
}
@Test
fun `resolveVerdictColor returns corresponding semantic color`() {
assertEquals(dummyColors.success, SkillScanPresentation.resolveVerdictColor("safe", dummyColors))
assertEquals(dummyColors.error, SkillScanPresentation.resolveVerdictColor("dangerous", dummyColors))
assertEquals(dummyColors.warning, SkillScanPresentation.resolveVerdictColor("suspicious", dummyColors))
}
@Test
fun `resolvePolicyKind and labels map allow ask block and fallback`() {
assertEquals(ScanPolicyKind.ALLOW, SkillScanPresentation.resolvePolicyKind("allow"))
assertEquals(ScanPolicyKind.ASK, SkillScanPresentation.resolvePolicyKind("ask"))
assertEquals(ScanPolicyKind.BLOCK, SkillScanPresentation.resolvePolicyKind("block"))
assertEquals(ScanPolicyKind.OTHER, SkillScanPresentation.resolvePolicyKind("custom"))
assertEquals(R.string.skills_hub_scan_policy_allow, SkillScanPresentation.resolvePolicyLabelRes("allow"))
assertEquals(R.string.skills_hub_scan_policy_ask, SkillScanPresentation.resolvePolicyLabelRes("ask"))
assertEquals(R.string.skills_hub_scan_policy_block, SkillScanPresentation.resolvePolicyLabelRes("block"))
assertNull(SkillScanPresentation.resolvePolicyLabelRes("other"))
}
@Test
fun `resolveSeverityColor maps critical high medium low and fallback`() {
assertEquals(dummyColors.error, SkillScanPresentation.resolveSeverityColor("critical", dummyColors))
assertEquals(dummyColors.error, SkillScanPresentation.resolveSeverityColor("high", dummyColors))
assertEquals(dummyColors.warning, SkillScanPresentation.resolveSeverityColor("medium", dummyColors))
assertEquals(dummyColors.neutral, SkillScanPresentation.resolveSeverityColor("low", dummyColors))
assertEquals(dummyColors.neutral, SkillScanPresentation.resolveSeverityColor("info", dummyColors))
}
}