Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,9 @@ dependencies {
implementation 'org.apache.logging.log4j:log4j-api:2.24.1'
implementation 'org.apache.logging.log4j:log4j-core:2.24.1'

// Testing dependencies
testImplementation 'com.squareup.okhttp3:mockwebserver:4.12.0'
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0'
testImplementation 'org.mockito:mockito-core:5.5.0'
testImplementation 'org.mockito:mockito-junit-jupiter:5.5.0'
testImplementation 'com.squareup.okhttp3:mockwebserver:4.12.0'
testImplementation 'org.assertj:assertj-core:3.24.2'
}

Expand Down Expand Up @@ -56,6 +54,10 @@ spotless {
}
}

test {
useJUnitPlatform()
}

javadoc {
options.tags = [ "implNote:a:Implementation Note:" ]
}
42 changes: 39 additions & 3 deletions src/main/java/io/github/lambdatest/gradle/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,45 @@ private Constants() {
"This is a utility class and cannot be instantiated");
}

public static final String API_URL = "https://manual-api.lambdatest.com/app/uploadFramework";
public static final String BUILD_URL =
private static final String DEFAULT_API_URL =
"https://manual-api.lambdatest.com/app/uploadFramework";
private static final String DEFAULT_BUILD_URL =
"https://mobile-api.lambdatest.com/framework/v1/espresso/build";
public static final String FLUTTER_BUILD_URL =
private static final String DEFAULT_FLUTTER_BUILD_URL =
"https://mobile-api.lambdatest.com/framework/v1/flutter/build";

// For testing purposes - allows URL override
private static String testApiUrl = null;
private static String testBuildUrl = null;
private static String testFlutterBuildUrl = null;

public static String getApiUrl() {
return testApiUrl != null ? testApiUrl : DEFAULT_API_URL;
}

public static String getBuildUrl() {
return testBuildUrl != null ? testBuildUrl : DEFAULT_BUILD_URL;
}

public static String getFlutterBuildUrl() {
return testFlutterBuildUrl != null ? testFlutterBuildUrl : DEFAULT_FLUTTER_BUILD_URL;
}

// Public methods for testing
public static void setTestUrls(String apiUrl, String buildUrl, String flutterBuildUrl) {
testApiUrl = apiUrl;
testBuildUrl = buildUrl;
testFlutterBuildUrl = flutterBuildUrl;
}

public static void resetUrls() {
testApiUrl = null;
testBuildUrl = null;
testFlutterBuildUrl = null;
}

// Backward compatibility - deprecated
@Deprecated public static final String API_URL = DEFAULT_API_URL;
@Deprecated public static final String BUILD_URL = DEFAULT_BUILD_URL;
@Deprecated public static final String FLUTTER_BUILD_URL = DEFAULT_FLUTTER_BUILD_URL;
}
4 changes: 2 additions & 2 deletions src/main/java/io/github/lambdatest/gradle/TestExecutor.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ public void executeTests(Map<String, String> params) throws IOException {

String url =
(isFlutter == null || !isFlutter)
? Constants.BUILD_URL
: Constants.FLUTTER_BUILD_URL;
? Constants.getBuildUrl()
: Constants.getFlutterBuildUrl();
RequestBody body = RequestBody.create(gson.toJson(capabilities), mediaType);

Request request =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ public static String uploadAndGetId(
}
Request request =
new Request.Builder()
.url(Constants.API_URL)
.url(Constants.getApiUrl())
.addHeader("Authorization", Credentials.basic(username, accessKey))
.post(body)
.build();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package io.github.lambdatest.gradle.integration;

import static org.junit.jupiter.api.Assertions.*;

import io.github.lambdatest.gradle.TestExecutor;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import okhttp3.mockwebserver.RecordedRequest;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class ExecutionIntegrationTest {
private MockLambdaTestServer mockServer;

@BeforeEach
void setUp() throws IOException {
mockServer = new MockLambdaTestServer();
mockServer.start();
}

@AfterEach
void tearDown() throws IOException {
mockServer.stop();
}

@Test
void testEspressoTestExecution() throws Exception {
// Arrange
String buildId = "BUILD123456789";
mockServer.enqueueBuildResponse(buildId);

TestExecutor executor =
new TestExecutor(
"testuser",
"testkey",
"lt://APP123",
"lt://TEST123",
Arrays.asList("Pixel 3-9"),
false);

Map<String, String> params = new HashMap<>();
params.put("build", "Test Build");
params.put("video", "true");

// Act
executor.executeTests(params);

// Assert
RecordedRequest request = mockServer.takeRequest();
assertEquals("POST", request.getMethod());
assertTrue(request.getPath().contains("espresso/build"));
assertNotNull(request.getHeader("Authorization"));
assertTrue(request.getBody().readUtf8().contains("Test Build"));
}

@Test
void testFlutterTestExecution() throws Exception {
// Arrange
String buildId = "FLUTTER_BUILD123456789";
mockServer.enqueueBuildResponse(buildId);

TestExecutor executor =
new TestExecutor(
"testuser",
"testkey",
"lt://APP123",
"lt://TEST123",
Arrays.asList("Pixel 3-9"),
true);

// Act
executor.executeTests(new HashMap<>());

// Assert
RecordedRequest request = mockServer.takeRequest();
assertEquals("POST", request.getMethod());
assertTrue(request.getPath().contains("flutter/build"));
}

@Test
void testExecutionWithInvalidCredentials() throws Exception {
// Arrange
mockServer.enqueueErrorResponse(401, "Invalid credentials");

TestExecutor executor =
new TestExecutor(
"baduser",
"badkey",
"lt://APP123",
"lt://TEST123",
Arrays.asList("Pixel 3-9"),
false);

// Act
executor.executeTests(new HashMap<>());

// Assert - Verify request was made even with invalid credentials
RecordedRequest request = mockServer.takeRequest();
assertEquals("POST", request.getMethod());
assertTrue(request.getPath().contains("espresso/build"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package io.github.lambdatest.gradle.integration;

import static org.junit.jupiter.api.Assertions.*;

import io.github.lambdatest.gradle.AppUploader;
import io.github.lambdatest.gradle.TestExecutor;
import io.github.lambdatest.gradle.TestSuiteUploader;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.concurrent.CompletableFuture;
import okhttp3.mockwebserver.RecordedRequest;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class FullWorkflowTest {
private MockLambdaTestServer mockServer;
private String testAppPath;
private String testSuitePath;

@BeforeEach
void setUp() throws IOException {
mockServer = new MockLambdaTestServer();
mockServer.start();

testAppPath = getClass().getClassLoader().getResource("test-app.apk").getPath();
testSuitePath = getClass().getClassLoader().getResource("test-suite.apk").getPath();
}

@AfterEach
void tearDown() throws IOException {
mockServer.stop();
}

@Test
void testFullWorkflow() throws Exception {
// Arrange - Mock responses for upload and execution
mockServer.enqueueUploadResponse("lt://APP123456789");
mockServer.enqueueUploadResponse("lt://TEST123456789");
mockServer.enqueueBuildResponse("BUILD123456789");

// Act - Upload app and test suite
AppUploader appUploader = new AppUploader("testuser", "testkey", testAppPath);
TestSuiteUploader testUploader =
new TestSuiteUploader("testuser", "testkey", testSuitePath);

CompletableFuture<String> appIdFuture = appUploader.uploadAppAsync();
CompletableFuture<String> testIdFuture = testUploader.uploadTestSuiteAsync();

String appId = appIdFuture.get();
String testId = testIdFuture.get();

// Execute tests
TestExecutor executor =
new TestExecutor(
"testuser", "testkey", appId, testId, Arrays.asList("Pixel 3-9"), false);
executor.executeTests(new HashMap<>());

// Assert - Verify all three requests were made
RecordedRequest appUploadRequest = mockServer.takeRequest();
assertEquals("POST", appUploadRequest.getMethod());
assertTrue(appUploadRequest.getPath().contains("uploadFramework"));

RecordedRequest testUploadRequest = mockServer.takeRequest();
assertEquals("POST", testUploadRequest.getMethod());
assertTrue(testUploadRequest.getPath().contains("uploadFramework"));

RecordedRequest executionRequest = mockServer.takeRequest();
assertEquals("POST", executionRequest.getMethod());
assertTrue(executionRequest.getPath().contains("espresso/build"));
}

@Test
void testUploadOnlyWorkflow() throws Exception {
// Arrange
mockServer.enqueueUploadResponse("lt://APP123456789");
mockServer.enqueueUploadResponse("lt://TEST123456789");

// Act
AppUploader appUploader = new AppUploader("testuser", "testkey", testAppPath);
TestSuiteUploader testUploader =
new TestSuiteUploader("testuser", "testkey", testSuitePath);

CompletableFuture<String> appIdFuture = appUploader.uploadAppAsync();
CompletableFuture<String> testIdFuture = testUploader.uploadTestSuiteAsync();

String appId = appIdFuture.get();
String testId = testIdFuture.get();

// Assert - Since uploads are async, either could get either ID
assertTrue(appId.equals("lt://APP123456789") || appId.equals("lt://TEST123456789"));
assertTrue(testId.equals("lt://APP123456789") || testId.equals("lt://TEST123456789"));
assertNotEquals(appId, testId); // They should be different

RecordedRequest appUploadRequest = mockServer.takeRequest();
assertEquals("POST", appUploadRequest.getMethod());
assertTrue(appUploadRequest.getPath().contains("uploadFramework"));

RecordedRequest testUploadRequest = mockServer.takeRequest();
assertEquals("POST", testUploadRequest.getMethod());
assertTrue(testUploadRequest.getPath().contains("uploadFramework"));
}

@Test
void testExecutionWithExistingIds() throws Exception {
// Arrange
mockServer.enqueueBuildResponse("BUILD123456789");

// Act
TestExecutor executor =
new TestExecutor(
"testuser",
"testkey",
"lt://EXISTING_APP",
"lt://EXISTING_TEST",
Arrays.asList("Pixel 3-9"),
false);
executor.executeTests(new HashMap<>());

// Assert
RecordedRequest executionRequest = mockServer.takeRequest();
assertEquals("POST", executionRequest.getMethod());
assertTrue(executionRequest.getPath().contains("espresso/build"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package io.github.lambdatest.gradle.integration;

import io.github.lambdatest.gradle.Constants;
import java.io.IOException;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;

/** Utility class for setting up MockWebServer to simulate LambdaTest API responses. */
public class MockLambdaTestServer {
private MockWebServer server;
private String baseUrl;

public void start() throws IOException {
server = new MockWebServer();
server.start();
baseUrl = server.url("/").toString();

// Configure Constants to use mock URLs
Constants.setTestUrls(
baseUrl + "app/uploadFramework",
baseUrl + "framework/v1/espresso/build",
baseUrl + "framework/v1/flutter/build");
}

public void stop() throws IOException {
if (server != null) {
server.shutdown();
Constants.resetUrls();
}
}

public void enqueueUploadResponse(String appId) {
server.enqueue(
new MockResponse().setResponseCode(200).setBody("{\"app_id\":\"" + appId + "\"}"));
}

public void enqueueBuildResponse(String buildId) {
server.enqueue(
new MockResponse()
.setResponseCode(200)
.setBody("{\"build_id\":\"" + buildId + "\",\"status\":\"success\"}"));
}

public void enqueueErrorResponse(int code, String message) {
server.enqueue(
new MockResponse()
.setResponseCode(code)
.setBody("{\"error\":\"" + message + "\"}"));
}

public RecordedRequest takeRequest() throws InterruptedException {
return server.takeRequest();
}

public String getBaseUrl() {
return baseUrl;
}
}
Loading