3
3
4
4
package software.aws.toolkits.jetbrains.services.amazonq.workspace.context
5
5
6
+ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
7
+ import com.github.tomakehurst.wiremock.client.WireMock.aResponse
8
+ import com.github.tomakehurst.wiremock.client.WireMock.any
9
+ import com.github.tomakehurst.wiremock.client.WireMock.equalTo
10
+ import com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor
11
+ import com.github.tomakehurst.wiremock.client.WireMock.stubFor
12
+ import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo
13
+ import com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig
14
+ import com.github.tomakehurst.wiremock.http.Body
15
+ import com.github.tomakehurst.wiremock.junit.WireMockRule
6
16
import com.intellij.openapi.project.Project
7
17
import kotlinx.coroutines.test.TestScope
8
18
import kotlinx.coroutines.test.runTest
19
+ import org.assertj.core.api.Assertions.assertThat
9
20
import org.junit.Before
10
21
import org.junit.Rule
22
+ import org.junit.jupiter.api.assertThrows
11
23
import org.mockito.kotlin.any
12
- import org.mockito.kotlin.mock
24
+ import org.mockito.kotlin.doReturn
25
+ import org.mockito.kotlin.spy
26
+ import org.mockito.kotlin.stub
13
27
import org.mockito.kotlin.times
14
28
import org.mockito.kotlin.verify
15
29
import org.mockito.kotlin.whenever
16
30
import software.aws.toolkits.jetbrains.services.amazonq.project.EncoderServer
31
+ import software.aws.toolkits.jetbrains.services.amazonq.project.IndexRequest
32
+ import software.aws.toolkits.jetbrains.services.amazonq.project.LspMessage
17
33
import software.aws.toolkits.jetbrains.services.amazonq.project.ProjectContextProvider
34
+ import software.aws.toolkits.jetbrains.services.amazonq.project.QueryChatRequest
35
+ import software.aws.toolkits.jetbrains.services.amazonq.project.RelevantDocument
36
+ import software.aws.toolkits.jetbrains.services.amazonq.project.UpdateIndexRequest
18
37
import software.aws.toolkits.jetbrains.utils.rules.CodeInsightTestFixtureRule
19
38
import software.aws.toolkits.jetbrains.utils.rules.JavaCodeInsightTestFixtureRule
20
39
import java.net.ConnectException
@@ -25,15 +44,166 @@ class ProjectContextProviderTest {
25
44
@JvmField
26
45
val projectRule: CodeInsightTestFixtureRule = JavaCodeInsightTestFixtureRule ()
27
46
47
+ @Rule
48
+ @JvmField
49
+ val wireMock: WireMockRule = createMockServer()
50
+
28
51
private val project: Project
29
52
get() = projectRule.project
30
53
31
- private val encoderServer: EncoderServer = mock()
54
+ private lateinit var encoderServer: EncoderServer
32
55
private lateinit var sut: ProjectContextProvider
33
56
57
+ private val mapper = jacksonObjectMapper()
58
+
34
59
@Before
35
60
fun setup () {
61
+ encoderServer = spy(EncoderServer (project))
62
+ encoderServer.stub { on { port } doReturn wireMock.port() }
63
+
36
64
sut = ProjectContextProvider (project, encoderServer, TestScope ())
65
+
66
+ // initialization
67
+ stubFor(any(urlPathEqualTo(" /initialize" )).willReturn(aResponse().withStatus(200 ).withResponseBody(Body (" initialize response" ))))
68
+
69
+ // build index
70
+ stubFor(any(urlPathEqualTo(" /indexFiles" )).willReturn(aResponse().withStatus(200 ).withResponseBody(Body (" initialize response" ))))
71
+
72
+ // update index
73
+ stubFor(any(urlPathEqualTo(" /updateIndex" )).willReturn(aResponse().withStatus(200 ).withResponseBody(Body (" initialize response" ))))
74
+
75
+ // query
76
+ stubFor(
77
+ any(urlPathEqualTo(" /query" )).willReturn(
78
+ aResponse()
79
+ .withStatus(200 )
80
+ .withResponseBody(Body (validQueryChatResponse))
81
+ )
82
+ )
83
+
84
+ stubFor(
85
+ any(urlPathEqualTo(" /getUsage" ))
86
+ .willReturn(
87
+ aResponse()
88
+ .withStatus(200 )
89
+ .withResponseBody(Body (validGetUsageResponse))
90
+ )
91
+ )
92
+ }
93
+
94
+ @Test
95
+ fun `Lsp endpoint are correct` () {
96
+ assertThat(LspMessage .Initialize .endpoint).isEqualTo(" initialize" )
97
+ assertThat(LspMessage .Index .endpoint).isEqualTo(" indexFiles" )
98
+ assertThat(LspMessage .QueryChat .endpoint).isEqualTo(" query" )
99
+ assertThat(LspMessage .GetUsageMetrics .endpoint).isEqualTo(" getUsage" )
100
+ }
101
+
102
+ @Test
103
+ fun `index should send files within the project to lsp` () {
104
+ projectRule.fixture.addFileToProject(" Foo.java" , " foo" )
105
+ projectRule.fixture.addFileToProject(" Bar.java" , " bar" )
106
+ projectRule.fixture.addFileToProject(" Baz.java" , " baz" )
107
+
108
+ sut.index()
109
+
110
+ val request = IndexRequest (listOf (" /src/Foo.java" , " /src/Bar.java" , " /src/Baz.java" ), " /src" , false )
111
+ assertThat(request.filePaths).hasSize(3 )
112
+ assertThat(request.filePaths).satisfies({
113
+ it.contains(" /src/Foo.java" ) &&
114
+ it.contains(" /src/Baz.java" ) &&
115
+ it.contains(" /src/Bar.java" )
116
+ })
117
+
118
+ wireMock.verify(
119
+ 1 ,
120
+ postRequestedFor(urlPathEqualTo(" /indexFiles" ))
121
+ .withHeader(" Content-Type" , equalTo(" text/plain" ))
122
+ // comment it out because order matters and will cause json string different
123
+ // .withRequestBody(equalTo(encryptedRequest))
124
+ )
125
+ }
126
+
127
+ @Test
128
+ fun `updateIndex should send correct encrypted request to lsp` () {
129
+ sut.updateIndex(" foo.java" )
130
+ val request = UpdateIndexRequest (" foo.java" )
131
+ val requestJson = mapper.writeValueAsString(request)
132
+
133
+ assertThat(mapper.readTree(requestJson)).isEqualTo(mapper.readTree(""" { "filePath": "foo.java" }""" ))
134
+
135
+ val encryptedRequest = encoderServer.encrypt(requestJson)
136
+
137
+ wireMock.verify(
138
+ 1 ,
139
+ postRequestedFor(urlPathEqualTo(" /updateIndex" ))
140
+ .withHeader(" Content-Type" , equalTo(" text/plain" ))
141
+ .withRequestBody(equalTo(encryptedRequest))
142
+ )
143
+ }
144
+
145
+ @Test
146
+ fun `query should send correct encrypted request to lsp` () {
147
+ sut.query(" foo" )
148
+
149
+ val request = QueryChatRequest (" foo" )
150
+ val requestJson = mapper.writeValueAsString(request)
151
+
152
+ assertThat(mapper.readTree(requestJson)).isEqualTo(mapper.readTree(""" { "query": "foo" }""" ))
153
+
154
+ val encryptedRequest = encoderServer.encrypt(requestJson)
155
+
156
+ wireMock.verify(
157
+ 1 ,
158
+ postRequestedFor(urlPathEqualTo(" /query" ))
159
+ .withHeader(" Content-Type" , equalTo(" text/plain" ))
160
+ .withRequestBody(equalTo(encryptedRequest))
161
+ )
162
+ }
163
+
164
+ @Test
165
+ fun `query chat should return empty if result set non deserializable` () = runTest {
166
+ stubFor(
167
+ any(urlPathEqualTo(" /query" )).willReturn(
168
+ aResponse().withStatus(200 ).withResponseBody(
169
+ Body (
170
+ """
171
+ [
172
+ "foo", "bar"
173
+ ]
174
+ """ .trimIndent()
175
+ )
176
+ )
177
+ )
178
+ )
179
+
180
+ assertThrows<Exception > {
181
+ sut.query(" foo" )
182
+ }
183
+ }
184
+
185
+ @Test
186
+ fun `query chat should return deserialized relevantDocument` () = runTest {
187
+ val r = sut.query(" foo" )
188
+ assertThat(r).hasSize(2 )
189
+ assertThat(r[0 ]).isEqualTo(
190
+ RelevantDocument (
191
+ " relativeFilePath1" ,
192
+ " context1"
193
+ )
194
+ )
195
+ assertThat(r[1 ]).isEqualTo(
196
+ RelevantDocument (
197
+ " relativeFilePath2" ,
198
+ " context2"
199
+ )
200
+ )
201
+ }
202
+
203
+ @Test
204
+ fun `get usage should return memory, cpu usage` () = runTest {
205
+ val r = sut.getUsage()
206
+ assertThat(r).isEqualTo(ProjectContextProvider .Usage (123 , 456 ))
37
207
}
38
208
39
209
@Test
@@ -57,4 +227,52 @@ class ProjectContextProviderTest {
57
227
}
58
228
verify(encoderServer, times(1 )).encrypt(any())
59
229
}
230
+
231
+ private fun createMockServer () = WireMockRule (wireMockConfig().dynamicPort())
60
232
}
233
+
234
+ // language=JSON
235
+ val validQueryChatResponse = """
236
+ [
237
+ {
238
+ "filePath": "file1",
239
+ "content": "content1",
240
+ "id": "id1",
241
+ "index": "index1",
242
+ "vec": [
243
+ "vec_1-1",
244
+ "vec_1-2",
245
+ "vec_1-3"
246
+ ],
247
+ "context": "context1",
248
+ "prev": "prev1",
249
+ "next": "next1",
250
+ "relativePath": "relativeFilePath1",
251
+ "programmingLanguage": "language1"
252
+ },
253
+ {
254
+ "filePath": "file2",
255
+ "content": "content2",
256
+ "id": "id2",
257
+ "index": "index2",
258
+ "vec": [
259
+ "vec_2-1",
260
+ "vec_2-2",
261
+ "vec_2-3"
262
+ ],
263
+ "context": "context2",
264
+ "prev": "prev2",
265
+ "next": "next2",
266
+ "relativePath": "relativeFilePath2",
267
+ "programmingLanguage": "language2"
268
+ }
269
+ ]
270
+ """ .trimIndent()
271
+
272
+ // language=JSON
273
+ val validGetUsageResponse = """
274
+ {
275
+ "memoryUsage":123,
276
+ "cpuUsage":456
277
+ }
278
+ """ .trimIndent()
0 commit comments