Skip to content

Commit 7989107

Browse files
authored
[CodeWhisperer] Fix the logic to detect invalid references (#3243)
* Fix the logic to detect invalid references
1 parent edf661d commit 7989107

File tree

3 files changed

+104
-18
lines changed

3 files changed

+104
-18
lines changed

jetbrains-core/src/software/aws/toolkits/jetbrains/services/codewhisperer/service/CodeWhispererService.kt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -451,9 +451,12 @@ class CodeWhispererService {
451451
// If contentSpans in reference are not consistent with content(recommendations),
452452
// remove the incorrect references.
453453
val validatedRecommendations = response.recommendations().map {
454-
val validReference = it.hasReferences() && it.references().isNotEmpty() &&
455-
it.content().length == it.references().last().recommendationContentSpan().end()
456-
if (validReference) {
454+
val validReferences = it.hasReferences() && it.references().isNotEmpty() &&
455+
it.references().none { reference ->
456+
val span = reference.recommendationContentSpan()
457+
span.start() > span.end() || span.start() < 0 || span.end() > it.content().length
458+
}
459+
if (validReferences) {
457460
it
458461
} else {
459462
it.toBuilder().references(DefaultSdkAutoConstructList.getInstance()).build()
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package software.aws.toolkits.jetbrains.services.codewhisperer
5+
6+
import com.intellij.testFramework.runInEdtAndWait
7+
import org.assertj.core.api.Assertions.assertThat
8+
import org.junit.Test
9+
import org.mockito.kotlin.any
10+
import org.mockito.kotlin.argumentCaptor
11+
import org.mockito.kotlin.doAnswer
12+
import org.mockito.kotlin.stub
13+
import org.mockito.kotlin.timeout
14+
import org.mockito.kotlin.verify
15+
import software.amazon.awssdk.services.codewhisperer.model.ListRecommendationsRequest
16+
import software.amazon.awssdk.services.codewhisperer.model.ListRecommendationsResponse
17+
import software.aws.toolkits.jetbrains.services.codewhisperer.CodeWhispererTestUtil.generateMockRecommendationDetail
18+
import software.aws.toolkits.jetbrains.services.codewhisperer.CodeWhispererTestUtil.getReferenceInfo
19+
import software.aws.toolkits.jetbrains.services.codewhisperer.CodeWhispererTestUtil.metadata
20+
import software.aws.toolkits.jetbrains.services.codewhisperer.CodeWhispererTestUtil.sdkHttpResponse
21+
import software.aws.toolkits.jetbrains.services.codewhisperer.model.InvocationContext
22+
23+
class CodeWhispererReferencesTest : CodeWhispererTestBase() {
24+
25+
@Test
26+
fun `test references with invalid content span will not remove the references from the recommendation`() {
27+
testReferencesRangeValidity("test", -1, 4, invalid = true, 1)
28+
testReferencesRangeValidity("test", 0, 5, invalid = true, 2)
29+
testReferencesRangeValidity("test", 2, 1, invalid = true, 3)
30+
testReferencesRangeValidity("test", 2, 2, invalid = false, 4)
31+
testReferencesRangeValidity("test", 0, 4, invalid = false, 5)
32+
testReferencesRangeValidity("test", 0, 2, invalid = false, 6)
33+
testReferencesRangeValidity("test", 3, 4, invalid = false, 7)
34+
}
35+
36+
private fun testReferencesRangeValidity(content: String, start: Int, end: Int, invalid: Boolean, times: Int) {
37+
val referenceInfo = getReferenceInfo()
38+
val invalidReferencesResponse = ListRecommendationsResponse.builder()
39+
.recommendations(
40+
generateMockRecommendationDetail(content, referenceInfo.first, referenceInfo.second, start, end),
41+
)
42+
.nextToken("")
43+
.responseMetadata(metadata)
44+
.sdkHttpResponse(sdkHttpResponse)
45+
.build() as ListRecommendationsResponse
46+
47+
mockClient.stub {
48+
on {
49+
mockClient.listRecommendations(any<ListRecommendationsRequest>())
50+
} doAnswer {
51+
invalidReferencesResponse
52+
}
53+
}
54+
55+
val statesCaptor = argumentCaptor<InvocationContext>()
56+
withCodeWhispererServiceInvokedAndWait {
57+
verify(popupManagerSpy, timeout(5000).times(times))
58+
.render(statesCaptor.capture(), any(), any())
59+
statesCaptor.lastValue.recommendationContext.details.forEach {
60+
assertThat(it.recommendation.references().isEmpty()).isEqualTo(invalid)
61+
}
62+
runInEdtAndWait {
63+
popupManagerSpy.popupComponents.acceptButton.doClick()
64+
}
65+
}
66+
}
67+
}

jetbrains-core/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererTestUtil.kt

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@ object CodeWhispererTestUtil {
2626
Pair("Apache-2.0", "testRepo2"),
2727
Pair("BSD-4-Clause", "testRepo3")
2828
)
29-
private val metadata: DefaultAwsResponseMetadata = DefaultAwsResponseMetadata.create(
29+
val metadata: DefaultAwsResponseMetadata = DefaultAwsResponseMetadata.create(
3030
mapOf(AWS_REQUEST_ID to testRequestId)
3131
)
32-
private val sdkHttpResponse = SdkHttpResponse.builder().headers(
32+
val sdkHttpResponse = SdkHttpResponse.builder().headers(
3333
mapOf(CodeWhispererService.KET_SESSION_ID to listOf(testSessionId))
3434
).build()
3535
private val errorDetail = AwsErrorDetails.builder()
@@ -100,21 +100,37 @@ object CodeWhispererTestUtil {
100100
const val javaTestContext = "public class Test {\n public static void main\n}"
101101

102102
internal fun generateMockRecommendationDetail(content: String): Recommendation {
103-
val pair = testReferenceInfoPair[Random.nextInt(testReferenceInfoPair.size)]
104-
return Recommendation.builder()
105-
.content(content)
103+
val referenceInfo = getReferenceInfo()
104+
return Recommendation.builder().content(content)
106105
.references(
107-
Reference.builder()
108-
.licenseName(pair.first)
109-
.repository(pair.second)
110-
.recommendationContentSpan(
111-
Span.builder()
112-
.start(0)
113-
.end(content.length)
114-
.build()
115-
)
116-
.build()
106+
generateMockReferences(referenceInfo.first, referenceInfo.second, 0, content.length)
117107
)
118108
.build()
119109
}
110+
111+
internal fun getReferenceInfo() = testReferenceInfoPair[Random.nextInt(testReferenceInfoPair.size)]
112+
113+
internal fun generateMockRecommendationDetail(
114+
content: String,
115+
licenseName: String,
116+
repository: String,
117+
start: Int,
118+
end: Int
119+
): Recommendation =
120+
Recommendation.builder()
121+
.content(content)
122+
.references(generateMockReferences(licenseName, repository, start, end))
123+
.build()
124+
125+
private fun generateMockReferences(licenseName: String, repository: String, start: Int, end: Int) =
126+
Reference.builder()
127+
.licenseName(licenseName)
128+
.repository(repository)
129+
.recommendationContentSpan(
130+
Span.builder()
131+
.start(start)
132+
.end(end)
133+
.build()
134+
)
135+
.build()
120136
}

0 commit comments

Comments
 (0)