Skip to content

Commit 5dbe906

Browse files
committed
config tests
1 parent 77af13b commit 5dbe906

File tree

3 files changed

+189
-0
lines changed

3 files changed

+189
-0
lines changed

src/main/kotlin/mcp/code/analysis/config/AppConfig.kt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,23 @@ data class AppConfig(
3131
modelApiKey = System.getenv("MODEL_API_KEY") ?: "",
3232
modelName = System.getenv("MODEL_NAME") ?: "llama3.2",
3333
)
34+
35+
/**
36+
* Creates an instance of [AppConfig] by retrieving values from environment variables. If an environment variable is
37+
* not set, a default value is used.
38+
*
39+
* @param getEnvFunc Function to retrieve environment variables (defaults to System.getenv)
40+
* @return An instance of [AppConfig] with the retrieved or default values.
41+
*/
42+
fun fromEnv(getEnvFunc: (String) -> String? = System::getenv): AppConfig =
43+
AppConfig(
44+
serverPort = getEnvFunc("SERVER_PORT")?.toIntOrNull() ?: 3001,
45+
githubToken = getEnvFunc("GITHUB_TOKEN") ?: "",
46+
cloneDirectory = getEnvFunc("CLONE_DIRECTORY") ?: "/tmp/mcp-github-code-analyzer/clones",
47+
logDirectory = getEnvFunc("LOGS_DIRECTORY") ?: "/tmp/mcp-github-code-analyzer/logs",
48+
modelApiUrl = getEnvFunc("MODEL_API_URL") ?: "http://localhost:11434/api",
49+
modelApiKey = getEnvFunc("MODEL_API_KEY") ?: "",
50+
modelName = getEnvFunc("MODEL_NAME") ?: "llama3.2",
51+
)
3452
}
3553
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package mcp.code.analysis.config
2+
3+
import org.junit.jupiter.api.Assertions.*
4+
import org.junit.jupiter.api.Test
5+
6+
class AppConfigTest {
7+
8+
@Test
9+
fun `fromEnv uses default values when environment variables are not set`() {
10+
// Acts
11+
val config = AppConfig.fromEnv { null }
12+
13+
// Assert
14+
assertEquals(3001, config.serverPort)
15+
assertEquals("", config.githubToken)
16+
assertEquals("/tmp/mcp-github-code-analyzer/clones", config.cloneDirectory)
17+
assertEquals("/tmp/mcp-github-code-analyzer/logs", config.logDirectory)
18+
assertEquals("http://localhost:11434/api", config.modelApiUrl)
19+
assertEquals("", config.modelApiKey)
20+
assertEquals("llama3.2", config.modelName)
21+
}
22+
23+
@Test
24+
fun `fromEnv uses environment variables when available`() {
25+
// Arrange
26+
val envVars =
27+
mapOf(
28+
"SERVER_PORT" to "8080",
29+
"GITHUB_TOKEN" to "test-token",
30+
"CLONE_DIRECTORY" to "/custom/clone/dir",
31+
"LOGS_DIRECTORY" to "/custom/logs/dir",
32+
"MODEL_API_URL" to "https://api.example.com",
33+
"MODEL_API_KEY" to "test-api-key",
34+
"MODEL_NAME" to "custom-model",
35+
)
36+
37+
// Act
38+
val config = AppConfig.fromEnv { key -> envVars[key] }
39+
40+
// Assert
41+
assertEquals(8080, config.serverPort)
42+
assertEquals("test-token", config.githubToken)
43+
assertEquals("/custom/clone/dir", config.cloneDirectory)
44+
assertEquals("/custom/logs/dir", config.logDirectory)
45+
assertEquals("https://api.example.com", config.modelApiUrl)
46+
assertEquals("test-api-key", config.modelApiKey)
47+
assertEquals("custom-model", config.modelName)
48+
}
49+
50+
@Test
51+
fun `fromEnv handles invalid integer values`() {
52+
// Act
53+
val config = AppConfig.fromEnv { key -> if (key == "SERVER_PORT") "not-a-number" else null }
54+
55+
// Assert
56+
assertEquals(3001, config.serverPort, "Should use default port when env var is not a valid number")
57+
}
58+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package mcp.code.analysis.server
2+
3+
import io.mockk.*
4+
import io.modelcontextprotocol.kotlin.sdk.*
5+
import io.modelcontextprotocol.kotlin.sdk.server.Server as SdkServer
6+
import io.modelcontextprotocol.kotlin.sdk.server.StdioServerTransport
7+
import kotlin.test.assertEquals
8+
import kotlinx.coroutines.runBlocking
9+
import kotlinx.serialization.json.JsonObject
10+
import kotlinx.serialization.json.JsonPrimitive
11+
import mcp.code.analysis.service.RepositoryAnalysisService
12+
import org.junit.jupiter.api.BeforeEach
13+
import org.junit.jupiter.api.Disabled
14+
import org.junit.jupiter.api.Test
15+
import org.slf4j.Logger
16+
17+
class ServerTest {
18+
private lateinit var repositoryAnalysisService: RepositoryAnalysisService
19+
private lateinit var logger: Logger
20+
private lateinit var server: Server
21+
private lateinit var mockSdkServer: SdkServer
22+
23+
@BeforeEach
24+
fun setUp() {
25+
repositoryAnalysisService = mockk(relaxed = true)
26+
logger = mockk(relaxed = true)
27+
mockSdkServer = mockk(relaxed = true)
28+
server = Server(repositoryAnalysisService, logger)
29+
}
30+
31+
@Test
32+
fun `constructor should initialize with default values when not provided`() {
33+
// Arrange
34+
val defaultServer = Server()
35+
val implField = defaultServer.javaClass.getDeclaredField("implementation")
36+
implField.isAccessible = true
37+
38+
// Act
39+
val implementation = implField.get(defaultServer) as Implementation
40+
41+
// Assert
42+
assertEquals("MCP GitHub Code Analysis Server", implementation.name)
43+
assertEquals("0.1.0", implementation.version)
44+
}
45+
46+
@Test
47+
@Disabled("Temporarily disabled")
48+
fun `configureServer should register analyze-repository tool`() {
49+
// Arrange
50+
mockkConstructor(SdkServer::class)
51+
val mockSdkServer = mockk<SdkServer>(relaxed = true)
52+
every { anyConstructed<SdkServer>() } returns mockSdkServer
53+
every { mockSdkServer.addTool(any(), any(), any(), any()) } returns mockk()
54+
55+
val configureServerMethod = server.javaClass.getDeclaredMethod("configureServer")
56+
configureServerMethod.isAccessible = true
57+
configureServerMethod.invoke(server)
58+
59+
// Act & Assert
60+
verify { mockSdkServer.addTool(any(), any(), any(), any()) }
61+
}
62+
63+
@Test
64+
fun `analyze-repository tool should call repositoryAnalysisService`() = runBlocking {
65+
// Arrange
66+
val configureServerMethod = server.javaClass.getDeclaredMethod("configureServer")
67+
configureServerMethod.isAccessible = true
68+
69+
val handlerCaptor = slot<suspend (CallToolRequest) -> CallToolResult>()
70+
71+
mockkConstructor(SdkServer::class)
72+
every { anyConstructed<SdkServer>().addTool(any(), any(), any(), capture(handlerCaptor)) } just Runs
73+
74+
configureServerMethod.invoke(server)
75+
76+
val mockRequest = mockk<CallToolRequest>()
77+
every { mockRequest.arguments } returns
78+
JsonObject(
79+
mapOf("repoUrl" to JsonPrimitive("https://github.com/example/repo"), "branch" to JsonPrimitive("main"))
80+
)
81+
82+
coEvery { repositoryAnalysisService.analyzeRepository("https://github.com/example/repo", "main") } returns
83+
"Analysis result"
84+
85+
val result = handlerCaptor.captured(mockRequest)
86+
87+
// Assert & Verify
88+
assertEquals(false, result.isError)
89+
assertEquals("Analysis result", (result.content.first() as TextContent).text)
90+
coVerify { repositoryAnalysisService.analyzeRepository("https://github.com/example/repo", "main") }
91+
}
92+
93+
@Test
94+
@Disabled("Temporarily disabled")
95+
fun `runMcpServerUsingStdio should connect transport`() = runBlocking {
96+
// Arrange
97+
mockkConstructor(StdioServerTransport::class)
98+
val mockTransport = mockk<StdioServerTransport>(relaxed = true)
99+
every { anyConstructed<StdioServerTransport>() } returns mockTransport
100+
101+
mockkConstructor(SdkServer::class)
102+
val mockSdkServer = mockk<SdkServer>(relaxed = true)
103+
every { anyConstructed<SdkServer>() } returns mockSdkServer
104+
coEvery { mockSdkServer.connect(any()) } just Runs
105+
coEvery { mockSdkServer.onClose(any()) } answers { firstArg<() -> Unit>().invoke() }
106+
107+
// Act
108+
server.runMcpServerUsingStdio()
109+
110+
// Assert
111+
coVerify { mockSdkServer.connect(mockTransport) }
112+
}
113+
}

0 commit comments

Comments
 (0)