Skip to content

Commit 7317256

Browse files
committed
feat: Add OpenCode preset to ALL_PRESETS and implement corresponding tests #535
1 parent 32f9b9d commit 7317256

File tree

2 files changed

+112
-0
lines changed

2 files changed

+112
-0
lines changed

mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/acp/IdeaAcpAgentViewModel.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -818,6 +818,13 @@ data class IdeaAcpAgentPreset(
818818
* Known presets for common ACP agent CLIs.
819819
*/
820820
private val ALL_PRESETS = listOf(
821+
IdeaAcpAgentPreset(
822+
id = "opencode",
823+
name = "OpenCode",
824+
command = "opencode",
825+
args = "acp",
826+
description = "OpenCode AI coding agent via ACP"
827+
),
821828
IdeaAcpAgentPreset(
822829
id = "codex",
823830
name = "Codex CLI",
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package cc.unitmesh.devins.idea.toolwindow.acp
2+
3+
import org.junit.Assert.*
4+
import org.junit.Test
5+
6+
/**
7+
* Tests for OpenCode ACP agent preset detection and configuration.
8+
*/
9+
class IdeaAcpAgentPresetTest {
10+
11+
@Test
12+
fun `should include OpenCode in ALL_PRESETS`() {
13+
val allPresets = IdeaAcpAgentPreset::class.java
14+
.getDeclaredField("Companion")
15+
.get(null)!!::class.java
16+
.getDeclaredField("ALL_PRESETS")
17+
.apply { isAccessible = true }
18+
.get(IdeaAcpAgentPreset.Companion) as List<IdeaAcpAgentPreset>
19+
20+
val openCodePreset = allPresets.find { it.id == "opencode" }
21+
assertNotNull("OpenCode preset should be in ALL_PRESETS", openCodePreset)
22+
23+
assertEquals("OpenCode", openCodePreset?.name)
24+
assertEquals("opencode", openCodePreset?.command)
25+
assertEquals("acp", openCodePreset?.args)
26+
assertEquals("OpenCode AI coding agent via ACP", openCodePreset?.description)
27+
}
28+
29+
@Test
30+
fun `should detect OpenCode if installed`() {
31+
val installedPresets = IdeaAcpAgentPreset.detectInstalled()
32+
33+
// This test will pass if opencode is in PATH
34+
// If opencode is not installed, the test should still pass but skip the check
35+
val openCodePreset = installedPresets.find { it.id == "opencode" }
36+
37+
if (isCommandAvailable("opencode")) {
38+
assertNotNull("OpenCode should be detected when installed", openCodePreset)
39+
assertEquals("opencode", openCodePreset?.command)
40+
assertEquals("acp", openCodePreset?.args)
41+
} else {
42+
println("OpenCode is not installed, skipping detection test")
43+
}
44+
}
45+
46+
@Test
47+
fun `OpenCode preset should convert to config correctly`() {
48+
val preset = IdeaAcpAgentPreset(
49+
id = "opencode",
50+
name = "OpenCode",
51+
command = "opencode",
52+
args = "acp",
53+
env = "",
54+
description = "OpenCode AI coding agent via ACP"
55+
)
56+
57+
val config = preset.toConfig()
58+
59+
assertEquals("OpenCode", config.name)
60+
assertEquals("opencode", config.command)
61+
assertEquals("acp", config.args)
62+
assertEquals("", config.env)
63+
}
64+
65+
@Test
66+
fun `all presets should have unique IDs`() {
67+
val allPresets = IdeaAcpAgentPreset::class.java
68+
.getDeclaredField("Companion")
69+
.get(null)!!::class.java
70+
.getDeclaredField("ALL_PRESETS")
71+
.apply { isAccessible = true }
72+
.get(IdeaAcpAgentPreset.Companion) as List<IdeaAcpAgentPreset>
73+
74+
val ids = allPresets.map { it.id }
75+
val uniqueIds = ids.toSet()
76+
77+
assertEquals("All preset IDs should be unique", ids.size, uniqueIds.size)
78+
}
79+
80+
@Test
81+
fun `OpenCode should be first in presets list`() {
82+
val allPresets = IdeaAcpAgentPreset::class.java
83+
.getDeclaredField("Companion")
84+
.get(null)!!::class.java
85+
.getDeclaredField("ALL_PRESETS")
86+
.apply { isAccessible = true }
87+
.get(IdeaAcpAgentPreset.Companion) as List<IdeaAcpAgentPreset>
88+
89+
assertEquals("OpenCode should be the first preset", "opencode", allPresets.firstOrNull()?.id)
90+
}
91+
92+
private fun isCommandAvailable(command: String): Boolean {
93+
return try {
94+
val isWindows = System.getProperty("os.name")?.lowercase()?.contains("win") == true
95+
val checkCmd = if (isWindows) listOf("where", command) else listOf("which", command)
96+
val process = ProcessBuilder(checkCmd)
97+
.redirectErrorStream(true)
98+
.start()
99+
val result = process.waitFor()
100+
result == 0
101+
} catch (e: Exception) {
102+
false
103+
}
104+
}
105+
}

0 commit comments

Comments
 (0)