From 6ba8b228ff963d9b40c8323ce7347387bcb45d16 Mon Sep 17 00:00:00 2001 From: mojo Date: Tue, 18 Nov 2025 18:44:47 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=20okhttpMock=20?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E5=B9=B6=E8=B0=83=E6=95=B4=E4=BE=9D=E8=B5=96?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + .idea/gradle.xml | 1 + gradle/libs.versions.toml | 4 + okhttpMock/.gitignore | 1 + okhttpMock/build.gradle.kts | 45 ++++++++++ okhttpMock/consumer-rules.pro | 0 okhttpMock/proguard-rules.pro | 21 +++++ .../okhttpmock/ExampleInstrumentedTest.kt | 24 ++++++ okhttpMock/src/main/AndroidManifest.xml | 4 + .../okhttpmock/mock/DelayInterceptor.kt | 24 ++++++ .../okhttpmock/mock/MockHttpActionServer.kt | 80 ++++++++++++++++++ .../mock/MockResponseInterceptor.kt | 84 +++++++++++++++++++ .../com/example/okhttpmock/ExampleUnitTest.kt | 17 ++++ settings.gradle.kts | 1 + 14 files changed, 307 insertions(+) create mode 100644 okhttpMock/.gitignore create mode 100644 okhttpMock/build.gradle.kts create mode 100644 okhttpMock/consumer-rules.pro create mode 100644 okhttpMock/proguard-rules.pro create mode 100644 okhttpMock/src/androidTest/java/com/example/okhttpmock/ExampleInstrumentedTest.kt create mode 100644 okhttpMock/src/main/AndroidManifest.xml create mode 100644 okhttpMock/src/main/java/com/example/okhttpmock/mock/DelayInterceptor.kt create mode 100644 okhttpMock/src/main/java/com/example/okhttpmock/mock/MockHttpActionServer.kt create mode 100644 okhttpMock/src/main/java/com/example/okhttpmock/mock/MockResponseInterceptor.kt create mode 100644 okhttpMock/src/test/java/com/example/okhttpmock/ExampleUnitTest.kt diff --git a/.gitignore b/.gitignore index 96f1c17..1ceb181 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,4 @@ google-services.json # Android Profiling *.hprof +!/dict.txt diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 79ffb21..bcdf28c 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -12,6 +12,7 @@ diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2a7832c..766e5c1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -13,6 +13,8 @@ espressoCoreVersion = "3.0.2" appcompatV7 = "28.0.0" annotationJvm = "1.9.1" firebaseCrashlyticsBuildtools = "3.0.6" +appcompat = "1.6.1" +material = "1.10.0" [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } @@ -34,6 +36,8 @@ espresso-core = { group = "com.android.support.test.espresso", name = "espresso- appcompat-v7 = { group = "com.android.support", name = "appcompat-v7", version.ref = "appcompatV7" } androidx-annotation-jvm = { group = "androidx.annotation", name = "annotation-jvm", version.ref = "annotationJvm" } firebase-crashlytics-buildtools = { group = "com.google.firebase", name = "firebase-crashlytics-buildtools", version.ref = "firebaseCrashlyticsBuildtools" } +androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } +material = { group = "com.google.android.material", name = "material", version.ref = "material" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" } diff --git a/okhttpMock/.gitignore b/okhttpMock/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/okhttpMock/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/okhttpMock/build.gradle.kts b/okhttpMock/build.gradle.kts new file mode 100644 index 0000000..2aa067c --- /dev/null +++ b/okhttpMock/build.gradle.kts @@ -0,0 +1,45 @@ +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.kotlin.android) +} + +android { + namespace = "com.example.okhttpmock" + compileSdk = 34 + + defaultConfig { + minSdk = 24 + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + kotlinOptions { + jvmTarget = "11" + } +} + +dependencies { + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.appcompat) + implementation(libs.material) + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso.core) + api("com.squareup.okhttp3:okhttp:4.12.0") + api("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.0") + api(project(":lib")) +} \ No newline at end of file diff --git a/okhttpMock/consumer-rules.pro b/okhttpMock/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/okhttpMock/proguard-rules.pro b/okhttpMock/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/okhttpMock/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/okhttpMock/src/androidTest/java/com/example/okhttpmock/ExampleInstrumentedTest.kt b/okhttpMock/src/androidTest/java/com/example/okhttpmock/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..cba3b08 --- /dev/null +++ b/okhttpMock/src/androidTest/java/com/example/okhttpmock/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.example.okhttpmock + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.example.okhttpmock.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/okhttpMock/src/main/AndroidManifest.xml b/okhttpMock/src/main/AndroidManifest.xml new file mode 100644 index 0000000..a5918e6 --- /dev/null +++ b/okhttpMock/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/okhttpMock/src/main/java/com/example/okhttpmock/mock/DelayInterceptor.kt b/okhttpMock/src/main/java/com/example/okhttpmock/mock/DelayInterceptor.kt new file mode 100644 index 0000000..9ac7025 --- /dev/null +++ b/okhttpMock/src/main/java/com/example/okhttpmock/mock/DelayInterceptor.kt @@ -0,0 +1,24 @@ +package com.example.okhttpmock.mock + +import okhttp3.Interceptor +import okhttp3.Request +import okhttp3.Response + +class DelayInterceptor(val delayMillis:Long): Interceptor { + override fun intercept(chain: Interceptor.Chain): Response { + + // 获取请求 + val request: Request = chain.request() + + // 模拟网络延迟 + try { + Thread.sleep(delayMillis) + } catch (e: InterruptedException) { + e.printStackTrace() + } + + // 继续处理请求并获取响应 + val response: Response = chain.proceed(request) + return response + } +} \ No newline at end of file diff --git a/okhttpMock/src/main/java/com/example/okhttpmock/mock/MockHttpActionServer.kt b/okhttpMock/src/main/java/com/example/okhttpmock/mock/MockHttpActionServer.kt new file mode 100644 index 0000000..45b0dc4 --- /dev/null +++ b/okhttpMock/src/main/java/com/example/okhttpmock/mock/MockHttpActionServer.kt @@ -0,0 +1,80 @@ +package com.example.okhttpmock.mock + +import com.example.action.BaseAction +import com.example.action.HttpActionRequest +import com.example.action.HttpMethod +import com.example.logger.LogUtils +import com.example.service.HttpService +import com.example.task.TaskConfig +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import kotlinx.coroutines.withTimeout +import java.net.CookieHandler +import java.net.CookieManager +import java.net.CookiePolicy + +object MockHttpActionServer { + private val scope = CoroutineScope(Dispatchers.Default) + private val taskConfig: TaskConfig = TaskConfig( + taskId = 0, + taskUid = 1, + taskVer = 1, + userId = "1", + userAgent = "TextAgent", + secChUa = "", + accept = "", + acceptLanguage = "", + cookieManager = CookieManager(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER), + currentStep = 0, + reportUrl = "", + variableCache = mutableMapOf(), + notificationCache = mutableSetOf() + ) + val actions: List = listOf( + BaseAction.HttpAction( + request = HttpActionRequest( + url = "http://ng-app.com/VasDigimobility/KorrectPredict-24-No-23410220000025836-web?trfsrc=Mobidea&trxId=ffb4ced5-8664-4c7e-b14c-390f68b72fd1", + method = HttpMethod.Get + ), + delay = 0, + skipError = false, + async = false + ), + + BaseAction.HttpAction( + request = HttpActionRequest( + url = "http://ng-app.com/VasDigimobility/handle", + method = HttpMethod.Post, + autoCookie = false + ), + delay = 0, + skipError = false, + async = false, + ), + + BaseAction.HttpAction( + request = HttpActionRequest( + url = "http://ng-app.com/VasDigimobility/korrectpredict-24-no-23410220000025836-confirm?confirm=1&trfsrc=Mobidea&trxId=ffb4ced5-8664-4c7e-b14c-390f68b72fd1&permID=13b33fdb-e261-4ea9-b8fc-d68b03dc55fd_ed203603-fe78-4045-a3f5-6de71ea1f8c5", + method = HttpMethod.Get + ), + delay = 0, + skipError = false, + async = false + ), + ) + + fun start() = scope.launch { + + actions.forEach { baseAction -> + + HttpService( + taskConfig = taskConfig, + action = baseAction + ).execute { actionExecs -> + LogUtils.info(actionExecs.toString()) + } + } + } +} \ No newline at end of file diff --git a/okhttpMock/src/main/java/com/example/okhttpmock/mock/MockResponseInterceptor.kt b/okhttpMock/src/main/java/com/example/okhttpmock/mock/MockResponseInterceptor.kt new file mode 100644 index 0000000..0276bd6 --- /dev/null +++ b/okhttpMock/src/main/java/com/example/okhttpmock/mock/MockResponseInterceptor.kt @@ -0,0 +1,84 @@ +package com.example.okhttpmock.mock + +import kotlinx.coroutines.delay +import okhttp3.Headers +import okhttp3.Interceptor +import okhttp3.MediaType.Companion.toMediaType +import okhttp3.Protocol +import okhttp3.Response +import okhttp3.ResponseBody.Companion.toResponseBody +import org.json.JSONObject +import kotlin.collections.iterator + +class MockResponseInterceptor : Interceptor { + + override fun intercept(chain: Interceptor.Chain): Response { + val url = chain.request().url.toUrl() + val path = url.path + val build = Response.Builder() + .protocol(Protocol.HTTP_1_1) + .code(200) + .message("OK") + .request(chain.request()) + + when(path) { + "/VasDigimobility/KorrectPredict-24-No-23410220000025836-web" -> { +// {\"Keep-Alive\":[\"timeout=5, max=1000\"],\"Transfer-Encoding\":[\"chunked\"],\"X-Cache\":[\"MISS\"],\"Http-Cost\":[\"1023\"],\"Access-Control-Allow-Origin\":[\"*\"],\"Connection\":[\"Keep-Alive\"],\"Referer\":[\"https:\\/\\/campaignmngr.online\\/tracking\\/?target=84&src=ANGLEMEDIA&click_id=a1133o11913u723501580741246976\"],\"X-Android-Received-Millis\":[\"1763437020425\"],\"Date\":[\"Tue, 18 Nov 2025 03:37:01 GMT\"],\"Via\":[\"1.1 ngmtn1-varnish-65c8f4f546-pt4l9 (Varnish\\/7.4)\"],\"Accept-Ranges\":[\"bytes\"],\"X-Frame-Options\":[\"DENY\"],\"X-Android-Selected-Protocol\":[\"http\\/1.1\"],\"X-Varnish\":[\"61372577093\"],\"Strict-Transport-Security\":[\"max-age=0; includeSubDomains\"],\"Cache-Control\":[\"no-cache, private\"],\"X-Android-Response-Source\":[\"NETWORK 200\"],\"Set-Cookie\":[\"_ga=GA1.2.4951392835.1763437021; expires=Sun, 17 May 2026 03:37:01 GMT; Max-Age=15552000; path=\\/; HttpOnly\",\"ng_session=eyJpdiI6IjNwQ1FKeGZsanhpeFpOVVNURmhXT0E9PSIsInZhbHVlIjoiSHRTLzZGNC8zR0lXUXZXZ2tycEZFYURieTZaMXJvOFdWaWhDTWNCK284aTJZa0FpejA5ZEhKazI2MW9xWXdlamxHT2VaV2JVQzZHN3pwWkVQM2VPYldPZEoxWExwU0FxVDF3a1d1L0ExTURKOWFrL01VMWx2N3ZXdW5TTHVZd08iLCJtYWMiOiI3OTM2ZWI4ODBkMDNhOWM2MDVmZTBkZTFkODkwMGMzOTUxOGNmNzQzNzhmMjM5MTEzZjAwMTJiNGIwYjRlNGYzIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:37:01 GMT; Max-Age=21600; path=\\/; httponly; samesite=lax\",\"ctxid=eyJpdiI6ImdKVUNheFFRcSswR29xaTBBbmU0T1E9PSIsInZhbHVlIjoiRzlkOEI0aGxYUEJ6d3pHNC9tOXVxYlZMaWhqTkJzOHN1dFZxNXRkU1JvbVkxaWZpdVpkZGIyVFg4eWoyQ3JVNGN0UHVxempWc3MrUXFNZU1mNTFmMzRYQVlVc3g1SlduQVliSmpxcUVWWFk9IiwibWFjIjoiOWE3YWM2ODQyYTZiNGQ5Njk0ZmQzNWI1NTkyYzU2MWFiMGYyNzRlMTBjZDdhMDUwMDEwZWQ3ZTY2MDc0NjIwMyIsInRhZyI6IiJ9; expires=Fri, 16 Nov 2035 03:37:01 GMT; Max-Age=315360000; path=\\/; httponly; samesite=lax\",\"rd=deleted; expires=Mon, 18 Nov 2024 03:37:00 GMT; Max-Age=0; path=\\/; httponly; samesite=lax\",\"userSessionID=eyJpdiI6IkNMK1ZIOTBYSExqNlhsb01lVUd6N0E9PSIsInZhbHVlIjoia1o3QUc1QVpqNWczTE1TYy9HWkdlTW9QWVZSRHc0LytuMDlDV3ppMDdxbU1Dd2pqVmcwblQ5aGY5Y2RvclNOck10cnpUSkpQR0xLZzZZSE4vKzIzQ0t0eWQ1NHdZWWRadHBLYWFRMGFobHc9IiwibWFjIjoiODg0YjdmNjQwOWM1NzY4NWYwYWUwYzE5MmMxMGMyM2Q1NTdjOTU4NTJkMTQwODdiYzI4YmNkYjk1MDc1ZjVhNSIsInRhZyI6IiJ9; expires=Tue, 18 Nov 2025 04:07:01 GMT; Max-Age=1800; path=\\/; httponly; samesite=lax\",\"userPermID=eyJpdiI6InI1QTRoMEFXdC9CSkE4Sk5SeGtmSFE9PSIsInZhbHVlIjoiTFk2cDcyK0MxVGdkSDB2cGJ6WnBPeTJsaDFsamdmNG14cEZHL3JVUzUvRWszcGExQmhFM2FrNm1yaCtBUHloRHJ1L2c4Tk16NHF1UzBrQTlWWDJoRjBuQTZ1RmxFQWwyWXdzREorMUhUbnc9IiwibWFjIjoiMjNmNDZiY2NmNGU2ZjJjZTUzYmMzZDdmYmIwODdlOTkzM2RhOTVjMThhNGM1YzgyMDVjOWI0ZThmOTgyZWY0MiIsInRhZyI6IiJ9; expires=Fri, 16 Nov 2035 03:37:01 GMT; Max-Age=315360000; path=\\/; httponly; samesite=lax\",\"TS01e2a186=01b02e3e897d2dd37070014d269e1696f12e6a20698ebdb03130920da2e85d8d8117b386c9ad404ea8b68799a0c8dee69eb3e69498; Path=\\/; Domain=.ng-app.com;\"],\"Vary\":[\"Accept-Encoding\"],\"X-Android-Sent-Millis\":[\"1763437019808\"],\"Age\":[\"0\"],\"Content-Type\":[\"text\\/html; charset=UTF-8\"]} + val headers = parseHeader(""" + {\"accept-ranges\":[\"bytes\"],\"access-control-allow-origin\":[\"*\"],\"age\":[\"0\"],\"cache-control\":[\"no-cache, private\"],\"connection\":[\"Keep-Alive\"],\"content-type\":[\"text\\/html; charset=UTF-8\"],\"date\":[\"Tue, 18 Nov 2025 00:29:46 GMT\"],\"keep-alive\":[\"timeout=5, max=1000\"],\"set-cookie\":[\"_ga=GA1.2.1967424388.1763425786; expires=Sun, 17 May 2026 00:29:46 GMT; Max-Age=15552000; path=\\/; HttpOnly\",\"ng_session=eyJpdiI6IitQckdGUnJQbGxnaGpEcHdPL0RUMWc9PSIsInZhbHVlIjoiMDlMYWFPc2xJRUx1SmJBYWdXOUdjckE3Z0dCaUtwV3dtVjdxTGkvR0F0cUkvZ2dTbGhLUjJidGxFeDkvNVdLaEN3bEd6Vy80NGJhMWRFYllqUUtIRWhFeXJuUEdiN0RsRW9aMVA2cHhoRWZ0UkwrNUJNQ0FiNktjSGVBeWM4cnkiLCJtYWMiOiIyMTc1YjBjYjkzYjU4ZWZjMTY3MGEwNWU3YzBlNDVkNGY4NDk5ZjVlZmM1YjMzNTc4Y2ZiZjhlZmMyNWM2NTBjIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 06:29:47 GMT; Max-Age=21600; path=\\/; httponly; samesite=lax\",\"ctxid=eyJpdiI6Ik5yd0pBbm9OcjUvTEtHYWNnU1I0dGc9PSIsInZhbHVlIjoicU5uL3pjL295aE5rUFVCUVN1VjNHZjk5WmJjU2tXN2N5Q1JtVW9mMEw1SHRaMTJpa0dHZ0hzUitQTERQK0ZDdGlaeEdPbCtGNkRsR3BIcjhySStjeUVnWUNQazNIR1pXVE9aSERxcGNNUzA9IiwibWFjIjoiNjQwNDlmNGRhMTFiODMxNzkyZWUxN2FjMjNlYjY5YWRjOThjM2I2NDA4NDFmNjRhOGVlNDcxYjY5M2NjMzhkYSIsInRhZyI6IiJ9; expires=Fri, 16 Nov 2035 00:29:46 GMT; Max-Age=315359999; path=\\/; httponly; samesite=lax\",\"rd=deleted; expires=Mon, 18 Nov 2024 00:29:46 GMT; Max-Age=0; path=\\/; httponly; samesite=lax\",\"userSessionID=eyJpdiI6IllGRTduYm95TVJIeHY2SWE0WlowWFE9PSIsInZhbHVlIjoib29nNk5sdUg4L0g5dXdPUEFvclJleDlCMFJMalIxTEtsdjAzazN2eGVLM0E4cHp1dHU5bkg5bmwrcGl4Wkx0R1N5SWw4MUFmQXVGZVVhR1NtMVc0SWNtOGw0RzBQSElSd0tNb2x5TzF0dVU9IiwibWFjIjoiZWE2NmU4NzFhYjQ3YmZjZGNmNTUxM2M0Y2UwNzg0MmVjMmJmNDFhNTI0OGQ0YTk4N2U1NTc2NzcxYzc3MjdlMCIsInRhZyI6IiJ9; expires=Tue, 18 Nov 2025 00:59:46 GMT; Max-Age=1799; path=\\/; httponly; samesite=lax\",\"userPermID=eyJpdiI6Im1vQkJMUkFEUkd0WDRxbWc2VmE0QkE9PSIsInZhbHVlIjoiRWpQSWMza3F1ZlhWaFQ3NXNPVks0czNXSmdHOUsyZnd1ay9CdEErc2FQRGVlWHdFck5ObjhNT3kxYUYxU0NTZWUvRm5OZmhhNHovRFp4eEs0V0VXMWVGL0Y3aURIdXcva0dwU2J4c2hseDg9IiwibWFjIjoiNWZkNzljNGU4YmU4NjM5NzMyNTFlM2NjNTlkY2E3ZTlmYjFiM2I3Y2U0ZmViNDQwZDc0MmRlZjAwY2Y3N2Y0ZCIsInRhZyI6IiJ9; expires=Fri, 16 Nov 2035 00:29:46 GMT; Max-Age=315359999; path=\\/; httponly; samesite=lax\",\"TS01e2a186=01b02e3e8981bf74a1f512f4b0670461807efcf8a8b2f4a37655a07262210e12365df7598bbe19c34428020d816188384f8391634e; Path=\\/; Domain=.ng-app.com;\"],\"strict-transport-security\":[\"max-age=0; includeSubDomains\"],\"transfer-encoding\":[\"chunked\"],\"vary\":[\"Accept-Encoding\"],\"via\":[\"1.1 ngmtn1-varnish-65c8f4f546-xqcgq (Varnish\\/7.4)\"],\"x-cache\":[\"MISS\"],\"x-frame-options\":[\"DENY\"],\"x-varnish\":[\"61206562170\"],\"Referer\":[\"https:\\/\\/campaignmngr.online\\/tracking\\/?target=84&src=ANGLEMEDIA&click_id=a1042o11913u723454463955959808\"]} + """.trimIndent().replace("\\", "")) + build + .addHeader(headers) + .body("Success".toResponseBody("text/plain".toMediaType())) + } + "/VasDigimobility/handle" -> { +// {\"Access-Control-Allow-Origin\":[\"*\"],\"Age\":[\"0\"],\"Cache-Control\":[\"no-cache, private\"],\"Connection\":[\"Keep-Alive\"],\"Content-Type\":[\"text\\/html; charset=utf-8\"],\"Date\":[\"Tue, 18 Nov 2025 03:37:07 GMT\"],\"Keep-Alive\":[\"timeout=5, max=1000\"],\"Location\":[\"http:\\/\\/ng-app.com\\/VasDigimobility\\/korrectpredict-24-no-23410220000025836-confirm?confirm=1&trfsrc=Mobidea&trxId=ffb4ced5-8664-4c7e-b14c-390f68b72fd1&permID=13b33fdb-e261-4ea9-b8fc-d68b03dc55fd_ed203603-fe78-4045-a3f5-6de71ea1f8c5\"],\"Set-Cookie\":[\"ng_session=eyJpdiI6IjBxU2lnTXo3Q1JEeGh5S3ZNV1pOaEE9PSIsInZhbHVlIjoiWnc4OFozQnhlOEYzcmxsODkrUERvekM2WFZQTE1CTFI3NmZ3cTl6UW1saDFxVlY0YzFUMlRXdmlad3FWeGtWYVdrRG1SQzJIalc0RzN3Qnp5KzJXeW5rUXBSMGR0L3ZYcDdzZkhjZU5xY1JYNzQ5b1lkTTc3R1pKbWNFN1RrNHoiLCJtYWMiOiIzNjMxMzUzYzU3ZDQwZjM4MzYwZWQ4NGZiYzFmNThiZGM0NzMxM2ZhOGE1YTEwMzdhMjM4Yjg1YTY1NWIxMDllIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:37:07 GMT; Max-Age=21600; path=\\/; httponly; samesite=lax\",\"userPermID=eyJpdiI6IjNoclkzOW04K1dUT2p5SHpSaWZyc0E9PSIsInZhbHVlIjoiV3hXa1NyRFMzbEdzbGxPU2dhdU4zVjBkSndCYXJZeTNnallnQWNBZmNPWUVXRW13dThXb2VyaFYzaGdBTTl5YjNhcG5SRlZjSE1ZRFRRRENMQUdkbGNmZEhzaHVvTmlUV1FWRjdOS283Ymc9IiwibWFjIjoiMTVmZTRkNzI4ZDc2OWNkY2ViNzZlZmMxMTgzZDViZWMwYmZiMGNlMTQzMmYzMzQ3N2Q4MGNmZjZiNmExMzhkNCIsInRhZyI6IiJ9; expires=Fri, 16 Nov 2035 03:37:07 GMT; Max-Age=315360000; path=\\/; httponly; samesite=lax\",\"userSessionID=eyJpdiI6Ilp3TkRQTkRPUFR5bko1M09kVEVyRmc9PSIsInZhbHVlIjoic0VGSUtDdHNlZTRuNFRIZDZqbFpXZU1vaFZ6VENwU2pRYTJhWG16Z0FIWDl0Y2pRNjF0eFhGNGNZM05WeW1FNW5nK2RuQ0YrWEN0U3VDbTg3TXM1TS9HOHVVYWtVaG1qc2ptOG53eFZsLzg9IiwibWFjIjoiMjllMGJhZjFhYWQ3ZmRiYWRiZWUyNjNhMDIwOWJlOWJiNTI3MWQwN2IwNGNlOTM3MTEwNzhjZDVhZjg2YmQ0YSIsInRhZyI6IiJ9; expires=Tue, 18 Nov 2025 04:07:07 GMT; Max-Age=1800; path=\\/; httponly; samesite=lax\",\"TS01e2a186=01b02e3e897d2dd37070014d269e1696f12e6a20698ebdb03130920da2e85d8d8117b386c9ad404ea8b68799a0c8dee69eb3e69498; Path=\\/; Domain=.ng-app.com;\"],\"Transfer-Encoding\":[\"chunked\"],\"Via\":[\"1.1 ngmtn1-varnish-65c8f4f546-pt4l9 (Varnish\\/7.4)\"],\"X-Android-Received-Millis\":[\"1763437026156\"],\"X-Android-Response-Source\":[\"NETWORK 302\"],\"X-Android-Selected-Protocol\":[\"http\\/1.1\"],\"X-Android-Sent-Millis\":[\"1763437025746\"],\"X-Cache\":[\"MISS\"],\"X-Varnish\":[\"61371906349\"]} + val headers = parseHeader(""" + {\"access-control-allow-origin\":[\"*\"],\"age\":[\"0\"],\"cache-control\":[\"no-cache, private\"],\"connection\":[\"Keep-Alive\"],\"content-type\":[\"text\\/html; charset=utf-8\"],\"date\":[\"Tue, 18 Nov 2025 00:29:52 GMT\"],\"keep-alive\":[\"timeout=5, max=1000\"],\"location\":[\"http:\\/\\/ng-app.com\\/VasDigimobility\\/korrectpredict-24-no-23410220000025836-error?code=504\"],\"set-cookie\":[\"_ga=GA1.2.6808494602.1763425792; expires=Sun, 17 May 2026 00:29:52 GMT; Max-Age=15552000; path=\\/; HttpOnly\",\"TS01e2a186=01b02e3e89eb0ad1889d48b667bc9ff19cb4894294f6272ac6e4965a63a9621114361bb80b9c06559140fd2cf3e837ae2e6a2ad33e; Path=\\/; Domain=.ng-app.com;\"],\"transfer-encoding\":[\"chunked\"],\"via\":[\"1.1 ngmtn1-varnish-65c8f4f546-pt4l9 (Varnish\\/7.4)\"],\"x-cache\":[\"MISS\"],\"x-varnish\":[\"61370999051\"]} + """.trimIndent().replace("\\", "")) + build + .addHeader(headers) + .code(302) + .message("Found") + .body("redirects : http://ng-app.com/VasDigimobility/korrectpredict-24-no-23410220000025836-confirm?confirm=1&trfsrc=Mobidea&trxId=ffb4ced5-8664-4c7e-b14c-390f68b72fd1&permID=13b33fdb-e261-4ea9-b8fc-d68b03dc55fd_ed203603-fe78-4045-a3f5-6de71ea1f8c5".toResponseBody("text/plain".toMediaType()),) + } + "/VasDigimobility/korrectpredict-24-no-23410220000025836-confirm/ajax/autorenewal" -> { + val headers = parseHeader(""" + {\"Keep-Alive\":[\"timeout=5, max=998\"],\"Transfer-Encoding\":[\"chunked\"],\"X-Cache\":[\"MISS\"],\"Http-Cost\":[\"258\"],\"Access-Control-Allow-Origin\":[\"*\"],\"Connection\":[\"Keep-Alive\"],\"X-Android-Received-Millis\":[\"1763437027194\"],\"Date\":[\"Tue, 18 Nov 2025 03:37:08 GMT\"],\"Via\":[\"1.1 ngmtn1-varnish-65c8f4f546-xqcgq (Varnish\\/7.4)\"],\"X-Android-Selected-Protocol\":[\"http\\/1.1\"],\"X-Varnish\":[\"61209288972\"],\"Cache-Control\":[\"no-cache, private\"],\"X-Android-Response-Source\":[\"NETWORK 200\"],\"Set-Cookie\":[\"ng_session=eyJpdiI6IkNXZTA5T0g0eXUvTG5VREtJKzFTdFE9PSIsInZhbHVlIjoiOUZzZy91aU1ac1Z4ZkFlVjJweENMWkdzMnlvRVh1ZVp1MWd3ZG0wQkk1MTRoWklPNmRCMlQ2VTZrZCtpMHQxT0cxVm9BTlVwM3BvcitEVDVQd28waTM3NmpTeTR6VXFSSVlRVmJOd0M1cC81ajNFQ1J0dXFOd0NUL2hGVTlmbEUiLCJtYWMiOiI0NDk4NmJlMGZhNjA1Mzk4YTI2MjkyMTRlMmJiOTcwNjhiOGU5OGNjZGEyYWZjMTA2MzY5ZTcxMjNmMGVhMmJmIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:37:08 GMT; Max-Age=21600; path=\\/; httponly; samesite=lax\",\"userPermID=eyJpdiI6IllRYW51cUpMNHAwOUdsckZ5SDBoWkE9PSIsInZhbHVlIjoiNFIvK05FTm84TWdKUndGSzNFQnVXcDg5enVwVllhbHdYR3ZsK3UyS3p3cjFEWVMvMjZmL1VNVzIxMCtrRXVQL3Rla203R2wxSkplb1owUHJ2bzlXa3FnQUtFUG9LUzNCUExWWCtKNHFqQ2c9IiwibWFjIjoiYzI2NzEyMWNiZTg5NzNhYTc2OTg0YWFiZjY0YzE2MGU2NGNjOGM0YTAxNGI2MTRlNWNiNjVhNDIwZjdjYzMyZiIsInRhZyI6IiJ9; expires=Fri, 16 Nov 2035 03:37:08 GMT; Max-Age=315360000; path=\\/; httponly; samesite=lax\",\"userSessionID=eyJpdiI6IlFyczZoQ0RJcUxVNGtzQ09jNSs5blE9PSIsInZhbHVlIjoiSVhhTEwxMkdtY2hxTy9NVDJsNzFwZURWQy9mQTdyeWFlMklLV2RoNFc1NEMwYlhJLzd5QmRWc3hlSW01MEp1cS9nNmNQTlFYcjNIYjdPanNSUUx5eENwaDVwdi80SDhrWStBdlczR3RNUEk9IiwibWFjIjoiOGI1MTlhNTQ0Nzk3ZDg3ZTg3NjZkNjdhZWY0Mjg2ZTE2ZDc5ZGUxZGQ3ZGRlYzIyMDA5OTQyNWQzYTgzYjNhYiIsInRhZyI6IiJ9; expires=Tue, 18 Nov 2025 04:07:08 GMT; Max-Age=1800; path=\\/; httponly; samesite=lax\",\"TS01e2a186=01b02e3e897d2dd37070014d269e1696f12e6a20698ebdb03130920da2e85d8d8117b386c9ad404ea8b68799a0c8dee69eb3e69498; Path=\\/; Domain=.ng-app.com;\"],\"Vary\":[\"Accept-Encoding\"],\"X-Android-Sent-Millis\":[\"1763437026942\"],\"Age\":[\"0\"],\"Content-Type\":[\"application\\/json\"]} + """.trimIndent().replace("\\", "")) + build + .addHeader(headers) + .code(200) + .message("OK") + .body("OK".toResponseBody("text/plain".toMediaType())) + } + else -> + build.body("OK".toResponseBody("text/plain".toMediaType())) + } + + return build.build() + } + + private fun parseHeader(header:String): Map> { + val result: MutableMap> = mutableMapOf() + val json = JSONObject(header) + for(key in json.keys()) { + val jsonArray = json.getJSONArray(key) + val list = mutableListOf() + repeat(jsonArray.length()){ index -> + list += jsonArray.getString(index) + } + result[key] = list + } + return result + } + + private fun Response.Builder.addHeader(headers: Map>): Response.Builder { + for (keyValue in headers) { + for (value in keyValue.value) { + this.addHeader(keyValue.key, value) + } + } + return this + } +} \ No newline at end of file diff --git a/okhttpMock/src/test/java/com/example/okhttpmock/ExampleUnitTest.kt b/okhttpMock/src/test/java/com/example/okhttpmock/ExampleUnitTest.kt new file mode 100644 index 0000000..6fc0d74 --- /dev/null +++ b/okhttpMock/src/test/java/com/example/okhttpmock/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.example.okhttpmock + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 2b08a6d..4c8a8c1 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -23,3 +23,4 @@ rootProject.name = "vast" include(":app") include(":lib") include(":pin") +include(":okhttpMock")