3
3
4
4
package software.aws.toolkits.jetbrains.services.amazonq.lsp.textdocument
5
5
6
- import com.intellij.openapi.Disposable
7
- import com.intellij.openapi.application.Application
8
- import com.intellij.openapi.application.ApplicationManager
9
- import com.intellij.openapi.components.serviceIfCreated
6
+ import com.intellij.openapi.application.writeAction
10
7
import com.intellij.openapi.editor.Document
11
8
import com.intellij.openapi.fileEditor.FileDocumentManager
12
- import com.intellij.openapi.fileEditor.FileEditorManager
13
9
import com.intellij.openapi.fileTypes.FileType
14
- import com.intellij.openapi.project.Project
15
10
import com.intellij.openapi.vfs.VirtualFile
16
11
import com.intellij.openapi.vfs.newvfs.events.VFileContentChangeEvent
17
12
import com.intellij.openapi.vfs.newvfs.events.VFileEvent
18
- import com.intellij.util.messages.MessageBus
19
- import com.intellij.util.messages.MessageBusConnection
13
+ import com.intellij.openapi.vfs.writeText
14
+ import com.intellij.testFramework.DisposableRule
15
+ import com.intellij.testFramework.fixtures.CodeInsightTestFixture
16
+ import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory
17
+ import com.intellij.testFramework.replaceService
20
18
import io.mockk.every
21
- import io.mockk.just
22
19
import io.mockk.mockk
23
20
import io.mockk.mockkObject
24
21
import io.mockk.mockkStatic
25
- import io.mockk.runs
26
22
import io.mockk.slot
27
23
import io.mockk.verify
28
24
import kotlinx.coroutines.test.runTest
25
+ import kotlinx.coroutines.withContext
29
26
import org.assertj.core.api.Assertions.assertThat
30
27
import org.eclipse.lsp4j.DidChangeTextDocumentParams
31
28
import org.eclipse.lsp4j.DidCloseTextDocumentParams
@@ -34,42 +31,51 @@ import org.eclipse.lsp4j.DidSaveTextDocumentParams
34
31
import org.eclipse.lsp4j.jsonrpc.messages.ResponseMessage
35
32
import org.eclipse.lsp4j.services.TextDocumentService
36
33
import org.junit.Before
34
+ import org.junit.Rule
37
35
import org.junit.Test
36
+ import software.aws.toolkits.jetbrains.core.coroutines.EDT
38
37
import software.aws.toolkits.jetbrains.services.amazonq.lsp.AmazonQLanguageServer
39
38
import software.aws.toolkits.jetbrains.services.amazonq.lsp.AmazonQLspService
40
39
import software.aws.toolkits.jetbrains.services.amazonq.lsp.util.FileUriUtil
40
+ import software.aws.toolkits.jetbrains.utils.rules.CodeInsightTestFixtureRule
41
+ import software.aws.toolkits.jetbrains.utils.satisfiesKt
41
42
import java.net.URI
42
43
import java.nio.file.Path
43
- import java.util.concurrent.Callable
44
44
import java.util.concurrent.CompletableFuture
45
+ import kotlin.collections.first
45
46
46
47
class TextDocumentServiceHandlerTest {
47
- private lateinit var project: Project
48
- private lateinit var mockFileEditorManager: FileEditorManager
49
48
private lateinit var mockLanguageServer: AmazonQLanguageServer
50
49
private lateinit var mockTextDocumentService: TextDocumentService
51
50
private lateinit var sut: TextDocumentServiceHandler
52
- private lateinit var mockApplication: Application
51
+
52
+ @get:Rule
53
+ val projectRule = object : CodeInsightTestFixtureRule () {
54
+ override fun createTestFixture (): CodeInsightTestFixture {
55
+ val fixtureFactory = IdeaTestFixtureFactory .getFixtureFactory()
56
+ val fixtureBuilder = fixtureFactory.createLightFixtureBuilder(testDescription, testName)
57
+ val newFixture = fixtureFactory
58
+ .createCodeInsightFixture(fixtureBuilder.fixture, fixtureFactory.createTempDirTestFixture())
59
+ newFixture.setUp()
60
+ newFixture.testDataPath = testDataPath
61
+
62
+ return newFixture
63
+ }
64
+ }
65
+
66
+ @get:Rule
67
+ val disposableRule = DisposableRule ()
53
68
54
69
@Before
55
70
fun setup () {
56
- project = mockk<Project >()
57
71
mockTextDocumentService = mockk<TextDocumentService >()
58
72
mockLanguageServer = mockk<AmazonQLanguageServer >()
59
73
60
- mockApplication = mockk<Application >()
61
- mockkStatic(ApplicationManager ::class )
62
- every { ApplicationManager .getApplication() } returns mockApplication
63
- every { mockApplication.executeOnPooledThread(any<Callable <* >>()) } answers {
64
- CompletableFuture .completedFuture(firstArg<Callable <* >>().call())
65
- }
66
-
67
74
// Mock the LSP service
68
- val mockLspService = mockk<AmazonQLspService >()
75
+ val mockLspService = mockk<AmazonQLspService >(relaxed = true )
69
76
70
77
// Mock the service methods on Project
71
- every { project.getService(AmazonQLspService ::class .java) } returns mockLspService
72
- every { project.serviceIfCreated<AmazonQLspService >() } returns mockLspService
78
+ projectRule.project.replaceService(AmazonQLspService ::class .java, mockLspService, disposableRule.disposable)
73
79
74
80
// Mock the LSP service's executeSync method as a suspend function
75
81
every {
@@ -86,19 +92,7 @@ class TextDocumentServiceHandlerTest {
86
92
every { mockTextDocumentService.didOpen(any()) } returns Unit
87
93
every { mockTextDocumentService.didClose(any()) } returns Unit
88
94
89
- // Mock message bus
90
- val messageBus = mockk<MessageBus >()
91
- every { project.messageBus } returns messageBus
92
- val mockConnection = mockk<MessageBusConnection >()
93
- every { messageBus.connect(any<Disposable >()) } returns mockConnection
94
- every { mockConnection.subscribe(any(), any()) } just runs
95
-
96
- // Mock FileEditorManager
97
- mockFileEditorManager = mockk<FileEditorManager >()
98
- every { mockFileEditorManager.openFiles } returns emptyArray()
99
- every { project.getService(FileEditorManager ::class .java) } returns mockFileEditorManager
100
-
101
- sut = TextDocumentServiceHandler (project, mockk())
95
+ sut = TextDocumentServiceHandler (projectRule.project, mockk())
102
96
}
103
97
104
98
@Test
@@ -136,41 +130,39 @@ class TextDocumentServiceHandlerTest {
136
130
137
131
@Test
138
132
fun `didOpen runs on service init` () = runTest {
139
- val uri = URI .create(" file:///test/path/file.txt" )
140
133
val content = " test content"
141
- val file = createMockVirtualFile(uri, content)
142
-
143
- every { mockFileEditorManager.openFiles } returns arrayOf(file)
134
+ val file = withContext( EDT ) {
135
+ projectRule.fixture.createFile( " name " , content). also { projectRule.fixture.openFileInEditor(it) }
136
+ }
144
137
145
- sut = TextDocumentServiceHandler (project, mockk())
138
+ sut = TextDocumentServiceHandler (projectRule. project, mockk())
146
139
147
- val paramsSlot = slot <DidOpenTextDocumentParams >()
140
+ val paramsSlot = mutableListOf <DidOpenTextDocumentParams >()
148
141
verify { mockTextDocumentService.didOpen(capture(paramsSlot)) }
149
142
150
- with (paramsSlot.captured.textDocument) {
151
- assertThat(this .uri).isEqualTo(normalizeFileUri(uri.toString()))
152
- assertThat(text).isEqualTo(content)
153
- assertThat(languageId).isEqualTo(" java" )
154
- assertThat(version).isEqualTo(1 )
143
+ assertThat(paramsSlot.first().textDocument).satisfiesKt {
144
+ assertThat(it.uri).isEqualTo(file.toNioPath().toUri().toString())
145
+ assertThat(it.text).isEqualTo(content)
146
+ assertThat(it.languageId).isEqualTo(" plain_text" )
155
147
}
156
148
}
157
149
158
150
@Test
159
151
fun `didOpen runs on fileOpened` () = runTest {
160
- val uri = URI .create(" file:///test/path/file.txt" )
161
152
val content = " test content"
162
- val file = createMockVirtualFile(uri, content)
153
+ val file = withContext(EDT ) {
154
+ projectRule.fixture.createFile(" name" , content).also { projectRule.fixture.openFileInEditor(it) }
155
+ }
163
156
164
157
sut.fileOpened(mockk(), file)
165
158
166
- val paramsSlot = slot <DidOpenTextDocumentParams >()
159
+ val paramsSlot = mutableListOf <DidOpenTextDocumentParams >()
167
160
verify { mockTextDocumentService.didOpen(capture(paramsSlot)) }
168
161
169
- with (paramsSlot.captured.textDocument) {
170
- assertThat(this .uri).isEqualTo(normalizeFileUri(uri.toString()))
171
- assertThat(text).isEqualTo(content)
172
- assertThat(languageId).isEqualTo(" java" )
173
- assertThat(version).isEqualTo(1 )
162
+ assertThat(paramsSlot.first().textDocument).satisfiesKt {
163
+ assertThat(it.uri).isEqualTo(file.toNioPath().toUri().toString())
164
+ assertThat(it.text).isEqualTo(content)
165
+ assertThat(it.languageId).isEqualTo(" plain_text" )
174
166
}
175
167
}
176
168
@@ -189,38 +181,23 @@ class TextDocumentServiceHandlerTest {
189
181
190
182
@Test
191
183
fun `didChange runs on content change events` () = runTest {
192
- val uri = URI .create(" file:///test/path/file.txt" )
193
- val document = mockk<Document > {
194
- every { text } returns " changed content"
195
- every { modificationStamp } returns 123L
196
- }
197
-
198
- val file = createMockVirtualFile(uri)
199
-
200
- val changeEvent = mockk<VFileContentChangeEvent > {
201
- every { this @mockk.file } returns file
202
- }
203
-
204
- // Mock FileDocumentManager
205
- val fileDocumentManager = mockk<FileDocumentManager > {
206
- every { getCachedDocument(file) } returns document
207
- }
184
+ val file = withContext(EDT ) {
185
+ projectRule.fixture.createFile(" name" , " " ).also {
186
+ projectRule.fixture.openFileInEditor(it)
208
187
209
- mockkStatic(FileDocumentManager ::class ) {
210
- every { FileDocumentManager .getInstance() } returns fileDocumentManager
211
-
212
- // Call the handler method
213
- sut.after(mutableListOf (changeEvent))
188
+ writeAction {
189
+ it.writeText(" changed content" )
190
+ }
191
+ }
214
192
}
215
193
216
194
// Verify the correct LSP method was called with matching parameters
217
- val paramsSlot = slot <DidChangeTextDocumentParams >()
195
+ val paramsSlot = mutableListOf <DidChangeTextDocumentParams >()
218
196
verify { mockTextDocumentService.didChange(capture(paramsSlot)) }
219
197
220
- with (paramsSlot.captured) {
221
- assertThat(textDocument.uri).isEqualTo(normalizeFileUri(uri.toString()))
222
- assertThat(textDocument.version).isEqualTo(123 )
223
- assertThat(contentChanges[0 ].text).isEqualTo(" changed content" )
198
+ assertThat(paramsSlot.first()).satisfiesKt {
199
+ assertThat(it.textDocument.uri).isEqualTo(file.toNioPath().toUri().toString())
200
+ assertThat(it.contentChanges[0 ].text).isEqualTo(" changed content" )
224
201
}
225
202
}
226
203
@@ -335,6 +312,11 @@ class TextDocumentServiceHandlerTest {
335
312
return uri
336
313
}
337
314
315
+ if (uri.startsWith(" file://C:/" )) {
316
+ val path = uri.substringAfter(" file://C:/" )
317
+ return " file:///C:/$path "
318
+ }
319
+
338
320
val path = uri.substringAfter(" file:///" )
339
321
return " file:///C:/$path "
340
322
}
0 commit comments