diff --git a/scripts/assets/todo_open_exemptions.textproto b/scripts/assets/todo_open_exemptions.textproto index 8dd53a51ce9..035e618e357 100644 --- a/scripts/assets/todo_open_exemptions.textproto +++ b/scripts/assets/todo_open_exemptions.textproto @@ -333,6 +333,11 @@ todo_open_exemption { line_number: 689 line_number: 737 line_number: 741 + line_number: 790 + line_number: 791 + line_number: 792 + line_number: 798 + line_number: 800 } todo_open_exemption { exempted_file_path: "scripts/static_checks.sh" diff --git a/scripts/src/java/org/oppia/android/scripts/common/GitHubClient.kt b/scripts/src/java/org/oppia/android/scripts/common/GitHubClient.kt index fd7cc105037..23348c05136 100644 --- a/scripts/src/java/org/oppia/android/scripts/common/GitHubClient.kt +++ b/scripts/src/java/org/oppia/android/scripts/common/GitHubClient.kt @@ -68,13 +68,14 @@ class GitHubClient( val call = gitHubService.fetchOpenIssues(repoOwner, repoName, authorizationBearer, pageNumber) // Deferred blocking I/O operation to the dedicated I/O dispatcher. val response = withContext(Dispatchers.IO) { call.execute() } + check(response.isSuccessful()) { "Failed to fetch issues at page $pageNumber: ${response.code()}\n${call.request()}" + "\n${response.errorBody()}." } return@async checkNotNull(response.body()) { "No issues response from GitHub for page: $pageNumber." - } + }.filter { it.pullRequest == null } } } diff --git a/scripts/src/java/org/oppia/android/scripts/common/model/GitHubIssue.kt b/scripts/src/java/org/oppia/android/scripts/common/model/GitHubIssue.kt index 7a824fc3120..67b814440fa 100644 --- a/scripts/src/java/org/oppia/android/scripts/common/model/GitHubIssue.kt +++ b/scripts/src/java/org/oppia/android/scripts/common/model/GitHubIssue.kt @@ -10,4 +10,18 @@ import com.squareup.moshi.JsonClass * 'issues/' in an issue's GitHub URL) */ @JsonClass(generateAdapter = true) -data class GitHubIssue(@Json(name = "number") val number: Int) +data class GitHubIssue( + @Json(name = "number") val number: Int, + @Json(name = "pull_request") val pullRequest: PullRequestInfo? = null +) + +/** + * Data class representing information about a pull request associated with a GitHub issue. + * + * @property url the URL of the pull request, if it exists. This provides the link to the specific + * pull request associated with the issue on GitHub. + */ +@JsonClass(generateAdapter = true) +data class PullRequestInfo( + @Json(name = "url") val url: String? = null +) diff --git a/scripts/src/javatests/org/oppia/android/scripts/todo/TodoOpenCheckTest.kt b/scripts/src/javatests/org/oppia/android/scripts/todo/TodoOpenCheckTest.kt index b614bc361d7..b5a3f783b70 100644 --- a/scripts/src/javatests/org/oppia/android/scripts/todo/TodoOpenCheckTest.kt +++ b/scripts/src/javatests/org/oppia/android/scripts/todo/TodoOpenCheckTest.kt @@ -777,11 +777,67 @@ class TodoOpenCheckTest { assertThat(outContent.toString().trim()).isEqualTo(failureMessage) } - private fun setUpGitHubService(issueNumbers: List) { - val issueJsons = issueNumbers.joinToString(separator = ",") { "{\"number\":$it}" } + @Test + fun testTodoCheck_PrPresent_checkShouldFail() { + setUpGitHubService( + issueNumbers = listOf(11004, 11003, 11002, 11001), + pullRequestNumbers = listOf(11005) + ) + val tempFile1 = tempFolder.newFile("testfiles/TempFile1.kt") + val tempFile2 = tempFolder.newFile("testfiles/TempFile2.kt") + val testContent1 = + """ + // TODO(#11002): test summary 1. + # TODO(#11004): test summary 2. + # TODO(#11001): test summary 3. + test Todo + test TODO + """.trimIndent() + val testContent2 = + """ + // TODO(#11005): test summary 3. + todo + + + """.trimIndent() + tempFile1.writeText(testContent1) + tempFile2.writeText(testContent2) + + val exception = assertThrows() { runScript() } + + assertThat(exception).hasMessageThat().contains(TODO_SYNTAX_CHECK_FAILED_OUTPUT_INDICATOR) + val failureMessage = + """ + TODOs not corresponding to open issues on GitHub: + - TempFile2.kt:1 + + $wikiReferenceNote + + $regenerateNote + """.trimIndent() + assertThat(outContent.toString().trim()).isEqualTo(failureMessage) + } + + private fun setUpGitHubService( + issueNumbers: List, + pullRequestNumbers: List = emptyList() + ) { + // Create JSON for issues with "pull_request" set to null + val issueJsons = issueNumbers + .joinToString(separator = ",") { "{\"number\":$it,\"pull_request\":null}" } + + // Create JSON for pull requests with "pull_request" as an empty object + val pullRequestJsons = pullRequestNumbers + .joinToString(separator = ",") { "{\"number\":$it,\"pull_request\":{}}" } + + // Combine issues and pull requests into one JSON array + val combinedJsons = + "[$issueJsons${if (pullRequestNumbers.isNotEmpty()) ", $pullRequestJsons" else ""}]" + val mockWebServer = MockWebServer() mockWebServer.enqueue(MockResponse().setBody("[$issueJsons]")) - mockWebServer.enqueue(MockResponse().setBody("[]")) // No more issues. + mockWebServer.enqueue(MockResponse().setBody(combinedJsons)) + mockWebServer.enqueue(MockResponse().setBody("[]")) GitHubClient.remoteApiUrl = mockWebServer.url("/").toString() }