Skip to content

Commit d2b5b14

Browse files
wyattwalterclaude
andauthored
test(git): de-flake GitUtilsTest.isRepoPrivate by mocking the HTTP call (#41975)
## Problem `GitUtilsTest.isRepoPrivate` intermittently fails in CI with `expected: false`. The test asserted against **real** URLs — including `github.com/appsmithorg/appsmith` — and `GitUtils.isRepoPrivate` performs a **live HTTP GET** against the repo URL (2xx ⇒ public, otherwise/error ⇒ private) with a **2-second timeout**: ```java return WebClientUtils.create(remoteHttpsUrl).get() .httpRequest(r -> r.getNativeRequest().responseTimeout(Duration.ofSeconds(2))) .exchange() .flatMap(resp -> Mono.just(!resp.statusCode().is2xxSuccessful())) .onErrorResume(t -> Mono.just(Boolean.TRUE)); // error ⇒ assume private ``` So whenever the CI runner can't reach GitHub within 2s (shared-runner rate-limits, DNS blips), the error path returns `TRUE` (private) and the `expected: false` (public) assertion fails — non-deterministically, independent of the change under test. This is almost certainly the flake behind the intermittently-red scheduled release builds too. ## Fix Make the test hermetic: mock `WebClientUtils.create` so the request hits a local `MockWebServer` instead of the internet, and assert the real branching logic deterministically — **200 ⇒ public**, **404 ⇒ private**. `WebClientUtils.create` is mocked (rather than pointing `isRepoPrivate` straight at the loopback server) because it wraps every `WebClient` in an SSRF host filter that would block a loopback address. URL-conversion coverage is untouched — `convertSshUrlToBrowserSupportedUrl` has its own dedicated test. No new dependencies (`mockwebserver` + `mockito-inline` are already test deps). Verified locally on JDK 25: `Tests run: 18, Failures: 0`. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Tests** * Updated repository privacy checks to use fully mocked HTTP responses instead of real network calls. * Added coverage for both accessible and inaccessible repository responses, improving test reliability and stability. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 0d3895d commit d2b5b14

1 file changed

Lines changed: 34 additions & 16 deletions

File tree

  • app/server/appsmith-server/src/test/java/com/appsmith/server/helpers

app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/GitUtilsTest.java

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,14 @@
55
import com.appsmith.server.domains.GitArtifactMetadata;
66
import com.appsmith.server.exceptions.AppsmithError;
77
import com.appsmith.server.exceptions.AppsmithException;
8+
import com.appsmith.util.WebClientUtils;
9+
import mockwebserver3.MockResponse;
10+
import mockwebserver3.MockWebServer;
811
import net.minidev.json.JSONObject;
912
import org.junit.jupiter.api.Test;
13+
import org.mockito.MockedStatic;
14+
import org.mockito.Mockito;
15+
import org.springframework.web.reactive.function.client.WebClient;
1016
import reactor.test.StepVerifier;
1117

1218
import java.util.UUID;
@@ -87,22 +93,34 @@ public void convertSshUrlToBrowserSupportedUrl() {
8793
}
8894

8995
@Test
90-
public void isRepoPrivate() {
91-
92-
StepVerifier.create(GitUtils.isRepoPrivate(
93-
GitUtils.convertSshUrlToBrowserSupportedUrl("git@github.com:test/testRepo.git")))
94-
.assertNext(isRepoPrivate -> assertThat(isRepoPrivate).isEqualTo(Boolean.TRUE))
95-
.verifyComplete();
96-
97-
StepVerifier.create(GitUtils.isRepoPrivate(GitUtils.convertSshUrlToBrowserSupportedUrl(
98-
"ssh://git@example.test.net/user/test/tests/testRepo.git")))
99-
.assertNext(isRepoPrivate -> assertThat(isRepoPrivate).isEqualTo(Boolean.TRUE))
100-
.verifyComplete();
101-
102-
StepVerifier.create(GitUtils.isRepoPrivate(
103-
GitUtils.convertSshUrlToBrowserSupportedUrl("git@github.com:appsmithorg/appsmith.git")))
104-
.assertNext(isRepoPrivate -> assertThat(isRepoPrivate).isEqualTo(Boolean.FALSE))
105-
.verifyComplete();
96+
public void isRepoPrivate() throws Exception {
97+
// isRepoPrivate performs a live HTTP GET against the repo URL (2xx => public, otherwise/error => private).
98+
// Mock WebClientUtils.create so the request hits a local MockWebServer instead of reaching out to
99+
// github.com. The previous version asserted against real URLs, which made this test flaky whenever CI
100+
// could not reach GitHub within the 2s timeout (rate-limits, DNS blips) and the error path defaulted to
101+
// "private". WebClientUtils.create is mocked (rather than pointing straight at the server) because it wraps
102+
// every WebClient in an SSRF host filter that would block a loopback address.
103+
MockWebServer mockServer = new MockWebServer();
104+
mockServer.start();
105+
try (MockedStatic<WebClientUtils> webClientUtilsMock = Mockito.mockStatic(WebClientUtils.class)) {
106+
webClientUtilsMock
107+
.when(() -> WebClientUtils.create(Mockito.anyString()))
108+
.thenReturn(WebClient.create(mockServer.url("/").toString()));
109+
110+
// A reachable repo that responds 2xx is treated as public.
111+
mockServer.enqueue(new MockResponse().setResponseCode(200));
112+
StepVerifier.create(GitUtils.isRepoPrivate("https://git.example.com/org/public-repo.git"))
113+
.assertNext(isRepoPrivate -> assertThat(isRepoPrivate).isEqualTo(Boolean.FALSE))
114+
.verifyComplete();
115+
116+
// A repo that responds non-2xx (e.g. 404 because it requires auth) is treated as private.
117+
mockServer.enqueue(new MockResponse().setResponseCode(404));
118+
StepVerifier.create(GitUtils.isRepoPrivate("https://git.example.com/org/private-repo.git"))
119+
.assertNext(isRepoPrivate -> assertThat(isRepoPrivate).isEqualTo(Boolean.TRUE))
120+
.verifyComplete();
121+
} finally {
122+
mockServer.shutdown();
123+
}
106124
}
107125

108126
@Test

0 commit comments

Comments
 (0)