feat(android): import private Shopee probe tasks
This commit is contained in:
@@ -53,9 +53,25 @@ android {
|
||||
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
||||
}
|
||||
}
|
||||
|
||||
val generatedProbeAssets = layout.buildDirectory.dir("generated/probeFixtures")
|
||||
sourceSets.getByName("debug").assets.srcDir(generatedProbeAssets)
|
||||
|
||||
val configuredProbeFixtures = providers.gradleProperty("probeFixturesDir")
|
||||
val syncProbeFixtures = tasks.register<Sync>("syncProbeFixtures") {
|
||||
if (configuredProbeFixtures.isPresent) {
|
||||
from(rootProject.file(configuredProbeFixtures.get()))
|
||||
}
|
||||
into(generatedProbeAssets.map { it.dir("probe-fixtures") })
|
||||
}
|
||||
tasks.matching { it.name == "mergeDebugAssets" }.configureEach {
|
||||
dependsOn(syncProbeFixtures)
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":task-contract"))
|
||||
|
||||
// AndroidX Core
|
||||
implementation("androidx.core:core-ktx:1.12.0")
|
||||
implementation("androidx.core:core-splashscreen:1.0.1")
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.roubao.autopilot.task
|
||||
|
||||
import android.content.Context
|
||||
import com.roubao.task.ProbeTask
|
||||
import com.roubao.task.ProbeTaskJson
|
||||
import com.roubao.task.TaskSource
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
class FixtureTaskSource(
|
||||
context: Context,
|
||||
private val taskDocumentAsset: String = DEFAULT_TASK_DOCUMENT_ASSET
|
||||
) : TaskSource {
|
||||
private val assetManager = context.applicationContext.assets
|
||||
private val nextIndex = AtomicInteger(0)
|
||||
private val tasks: List<ProbeTask> by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
|
||||
val payload = assetManager.open(taskDocumentAsset).bufferedReader(
|
||||
StandardCharsets.UTF_8
|
||||
).use { it.readText() }
|
||||
ProbeTaskJson.decode(payload)
|
||||
}
|
||||
|
||||
override suspend fun nextTask(): ProbeTask? {
|
||||
val index = nextIndex.getAndIncrement()
|
||||
return tasks.getOrNull(index)
|
||||
}
|
||||
|
||||
fun referenceImageAsset(task: ProbeTask): String =
|
||||
"$FIXTURE_ASSET_ROOT/${task.referenceImage.relativePath}"
|
||||
|
||||
companion object {
|
||||
private const val FIXTURE_ASSET_ROOT = "probe-fixtures"
|
||||
private const val DEFAULT_TASK_DOCUMENT_ASSET = "$FIXTURE_ASSET_ROOT/tasks.json"
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package com.roubao.autopilot.workflow
|
||||
|
||||
import com.roubao.task.ProbeTask
|
||||
import com.roubao.task.TaskSource
|
||||
|
||||
sealed interface ProbeWorkflowResult {
|
||||
data object NoTask : ProbeWorkflowResult
|
||||
|
||||
data class Executed(
|
||||
val probeId: String,
|
||||
val report: WorkflowReport
|
||||
) : ProbeWorkflowResult
|
||||
}
|
||||
|
||||
class ProbeWorkflowCoordinator(
|
||||
private val taskSource: TaskSource,
|
||||
private val workflowRunner: WorkflowRunner,
|
||||
private val stepsForTask: (ProbeTask) -> List<WorkflowStep>
|
||||
) {
|
||||
suspend fun runNext(): ProbeWorkflowResult {
|
||||
val task = taskSource.nextTask() ?: return ProbeWorkflowResult.NoTask
|
||||
return ProbeWorkflowResult.Executed(
|
||||
probeId = task.probeId,
|
||||
report = workflowRunner.run(stepsForTask(task))
|
||||
)
|
||||
}
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
package com.roubao.autopilot.workflow
|
||||
|
||||
import com.roubao.task.ProbeReferenceImage
|
||||
import com.roubao.task.ProbeTask
|
||||
import com.roubao.task.TaskSource
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class ProbeWorkflowCoordinatorTest {
|
||||
@Test
|
||||
fun `workflow consumes task source without knowing fixture implementation`() = runTest {
|
||||
val expectedTask = probeTask()
|
||||
val source = TaskSource { expectedTask }
|
||||
val runner = WorkflowRunner { AutomationResult.Success }
|
||||
val coordinator = ProbeWorkflowCoordinator(
|
||||
taskSource = source,
|
||||
workflowRunner = runner,
|
||||
stepsForTask = { task ->
|
||||
assertEquals(expectedTask.title, task.title)
|
||||
assertEquals(expectedTask.sku, task.sku)
|
||||
assertEquals(expectedTask.quantity, task.quantity)
|
||||
listOf(WorkflowStep("prepare_search", timeoutMillis = 1_000))
|
||||
}
|
||||
)
|
||||
|
||||
val result = coordinator.runNext() as ProbeWorkflowResult.Executed
|
||||
|
||||
assertEquals(expectedTask.probeId, result.probeId)
|
||||
assertEquals(WorkflowState.SUCCEEDED, result.report.state)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `empty task source does not start workflow`() = runTest {
|
||||
var automationCalls = 0
|
||||
val coordinator = ProbeWorkflowCoordinator(
|
||||
taskSource = TaskSource { null },
|
||||
workflowRunner = WorkflowRunner {
|
||||
automationCalls += 1
|
||||
AutomationResult.Success
|
||||
},
|
||||
stepsForTask = { listOf(WorkflowStep("unused", timeoutMillis = 1_000)) }
|
||||
)
|
||||
|
||||
assertEquals(ProbeWorkflowResult.NoTask, coordinator.runNext())
|
||||
assertEquals(0, automationCalls)
|
||||
}
|
||||
|
||||
private fun probeTask() = ProbeTask(
|
||||
probeId = "probe_0123456789abcdef",
|
||||
sourceOrderNo = "ORDER_000001",
|
||||
sourceStoreName = "Test Store",
|
||||
title = "Test Product",
|
||||
sku = "SKU-001",
|
||||
quantity = 1,
|
||||
referenceImage = ProbeReferenceImage(
|
||||
relativePath = "assets/0123456789abcdef.jpg",
|
||||
mediaType = "image/jpeg",
|
||||
sizeBytes = 123,
|
||||
sha256 = "a".repeat(64)
|
||||
)
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user