Skip to content

Commit 73435e4

Browse files
committed
Merge branch 'main' into bedrock
2 parents 7285150 + 755b5fc commit 73435e4

File tree

5 files changed

+165
-6
lines changed

5 files changed

+165
-6
lines changed

.github/dependabot.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: gradle
4+
directory: "/"
5+
schedule:
6+
interval: "weekly"
7+
groups:
8+
dependencies:
9+
applies-to: version-updates
10+
patterns:
11+
- "*"
12+
- package-ecosystem: github-actions
13+
directory: /
14+
schedule:
15+
interval: weekly
16+
- package-ecosystem: docker-compose
17+
directory: /
18+
schedule:
19+
interval: weekly
20+
groups:
21+
dependencies:
22+
applies-to: version-updates
23+
patterns:
24+
- "*"

.github/workflows/ci.yml renamed to .github/workflows/ci-chat-server.yml

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
1-
name: CI
1+
name: chat-server
22

33
on:
44
push:
55
branches: [ main ]
6+
paths:
7+
- .github/workflows/ci-chat-server.yml
8+
- chat-server/**
69
pull_request:
710
branches: [ main ]
11+
paths:
12+
- .github/workflows/ci-chat-server.yml
13+
- chat-server/**
814

915
jobs:
1016
build:
@@ -28,12 +34,10 @@ jobs:
2834
with:
2935
java-version: 21
3036
distribution: temurin
37+
cache: gradle
38+
cache-dependency-path: chat-server/build.gradle.kts
3139

32-
- name: Build mcp-server
33-
working-directory: mcp-server
34-
run: ./gradlew build --no-daemon
35-
36-
- name: Build chat-server
40+
- name: Build
3741
working-directory: chat-server
3842
run: ./gradlew build --no-daemon
3943

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: mcp-server
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
paths:
7+
- .github/workflows/ci-mcp-server.yml
8+
- mcp-server/**
9+
pull_request:
10+
branches: [ main ]
11+
paths:
12+
- .github/workflows/ci-mcp-server.yml
13+
- mcp-server/**
14+
15+
jobs:
16+
build:
17+
name: build
18+
runs-on: ubuntu-latest
19+
steps:
20+
21+
- name: Checkout
22+
uses: actions/checkout@v4
23+
24+
- name: Set up Java
25+
uses: actions/setup-java@v4
26+
with:
27+
java-version: 21
28+
distribution: temurin
29+
cache: gradle
30+
cache-dependency-path: mcp-server/build.gradle.kts
31+
32+
- name: Build
33+
working-directory: mcp-server
34+
run: ./gradlew build --no-daemon

mcp-server/build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ dependencies {
3232
testImplementation("org.junit.jupiter:junit-jupiter")
3333
testImplementation("org.junit.jupiter:junit-jupiter-params")
3434
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
35+
36+
testImplementation("org.mockito:mockito-core:5.17.0")
37+
testImplementation("org.mockito:mockito-junit-jupiter:5.17.0")
38+
testImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0")
3539
}
3640

3741
dependencyManagement {
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package com.rogervinas
2+
3+
import com.nhaarman.mockitokotlin2.argumentCaptor
4+
import com.nhaarman.mockitokotlin2.whenever
5+
import com.rogervinas.tools.BookingService
6+
import io.modelcontextprotocol.client.McpClient
7+
import io.modelcontextprotocol.client.McpSyncClient
8+
import io.modelcontextprotocol.client.transport.HttpClientSseClientTransport
9+
import io.modelcontextprotocol.spec.McpSchema.CallToolRequest
10+
import io.modelcontextprotocol.spec.McpSchema.TextContent
11+
import org.assertj.core.api.Assertions.assertThat
12+
import org.junit.jupiter.api.AfterAll
13+
import org.junit.jupiter.api.MethodOrderer
14+
import org.junit.jupiter.api.Order
15+
import org.junit.jupiter.api.Test
16+
import org.junit.jupiter.api.TestInstance
17+
import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS
18+
import org.junit.jupiter.api.TestMethodOrder
19+
import org.mockito.Mockito.doReturn
20+
import org.springframework.boot.test.context.SpringBootTest
21+
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT
22+
import org.springframework.boot.test.web.server.LocalServerPort
23+
import org.springframework.test.context.bean.override.mockito.MockitoBean
24+
import java.time.LocalDate
25+
import java.util.function.Consumer
26+
27+
28+
@SpringBootTest(webEnvironment = RANDOM_PORT)
29+
@TestInstance(PER_CLASS)
30+
@TestMethodOrder(MethodOrderer.OrderAnnotation::class)
31+
class McpServerApplicationTest {
32+
33+
@LocalServerPort
34+
val port: Int = 0
35+
36+
val client: McpSyncClient by lazy {
37+
McpClient.sync(HttpClientSseClientTransport("http://localhost:$port")).build().apply {
38+
initialize()
39+
ping()
40+
}
41+
}
42+
43+
@AfterAll
44+
fun closeClient() {
45+
client.closeGracefully()
46+
}
47+
48+
@MockitoBean
49+
lateinit var bookingService: BookingService
50+
51+
@Test
52+
@Order(0)
53+
fun `should list tools`() {
54+
assertThat(client.listTools().tools).singleElement().satisfies(Consumer {
55+
assertThat(it.name).isEqualTo("book")
56+
assertThat(it.description).isEqualTo("make a reservation for accommodation for a given city and date")
57+
})
58+
}
59+
60+
@Test
61+
fun `should book`() {
62+
val bookResult = "Booking is done!"
63+
val cityCaptor = argumentCaptor<String>()
64+
val checkinDateCaptor = argumentCaptor<LocalDate>()
65+
val checkoutDateCaptor = argumentCaptor<LocalDate>()
66+
doReturn(bookResult)
67+
.whenever(bookingService)
68+
.book(cityCaptor.capture(), checkinDateCaptor.capture(), checkoutDateCaptor.capture())
69+
70+
val city = "Barcelona"
71+
val checkinDate = "2025-04-15"
72+
val checkoutDate = "2025-04-18"
73+
val result = client.callTool(
74+
CallToolRequest(
75+
"book",
76+
mapOf(
77+
"city" to city,
78+
"checkinDate" to checkinDate,
79+
"checkoutDate" to checkoutDate
80+
)
81+
)
82+
)
83+
84+
assertThat(result.isError).isFalse()
85+
assertThat(result.content).singleElement().isInstanceOfSatisfying(TextContent::class.java) {
86+
// TODO why is text double quoted?
87+
assertThat(it.text).isEqualTo("\"$bookResult\"")
88+
}
89+
assertThat(cityCaptor.allValues).singleElement().isEqualTo(city)
90+
assertThat(checkinDateCaptor.allValues).singleElement().isEqualTo(LocalDate.parse(checkinDate))
91+
assertThat(checkoutDateCaptor.allValues).singleElement().isEqualTo(LocalDate.parse(checkoutDate))
92+
}
93+
}

0 commit comments

Comments
 (0)