Все картинки в отчёте

This commit is contained in:
2026-05-26 23:16:40 +03:00
parent ce70c13f86
commit 3673c4aa8d
29 changed files with 121 additions and 142 deletions

View File

@@ -28,10 +28,6 @@ android {
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
testInstrumentationRunnerArguments["yandex.oauth.token"] =
localProps.getProperty("yandex.test.oauth.token").orEmpty()
testInstrumentationRunnerArguments["yandex.user.id"] =
localProps.getProperty("yandex.test.user.id").orEmpty()
testInstrumentationRunnerArguments["yandex.vault.uuid"] =
localProps.getProperty("yandex.test.vault.uuid").orEmpty()
vectorDrawables {
useSupportLibrary = true
}

View File

@@ -1,53 +0,0 @@
package com.github.nullptroma.wallenc.app.integration.yandex
import android.util.Log
import androidx.room.Room
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.github.nullptroma.wallenc.infrastructure.android.db.app.AppDb
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
/**
* Ручной helper: после входа в Yandex через приложение печатает в Logcat маскированный
* токен и подсказку для local.properties. Запускать вручную из Android Studio.
*/
@RunWith(AndroidJUnit4::class)
@Ignore("Manual: export Yandex credentials to local.properties")
class ExportYandexTestCredentialsTest {
@Test
fun printFirstAccountCredentialsToLogcat() {
val context = ApplicationProvider.getApplicationContext<android.content.Context>()
val db = Room.databaseBuilder(context, AppDb::class.java, "wallenc.db")
.build()
try {
val row = runBlocking {
db.yandexAccountDao.observeAll().first().firstOrNull()
}
if (row == null) {
Log.i(TAG, "No Yandex accounts in DB. Link a vault in the app first.")
return
}
Log.i(TAG, "Add to local.properties:")
Log.i(TAG, "yandex.test.oauth.token=<copy full token from app debug storage if needed>")
Log.i(TAG, "yandex.test.oauth.token.prefix=${maskToken(row.oauthToken)}")
Log.i(TAG, "yandex.test.user.id=${row.yandexUserId}")
Log.i(TAG, "yandex.test.vault.uuid=${row.vaultUuid}")
} finally {
db.close()
}
}
private fun maskToken(token: String): String {
if (token.length <= 8) return "***"
return token.take(4) + "" + token.takeLast(4)
}
companion object {
private const val TAG = "WallencYandexExport"
}
}

View File

@@ -12,11 +12,15 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
/**
* Live-прогон Disk API. Пути только `app:/` — как в [YandexVault]; токен из Auth SDK
* обычно имеет `cloud_api:disk.app_folder`, без полного доступа к `disk:/` (там 403).
*/
@RunWith(JUnit4::class)
class YandexDiskLiveIntegrationTest {
private lateinit var repository: YandexDiskRepository
private val testFolder = "disk:/wallenc-integration-test"
private val testFolder = "app:/wallenc-integration-test"
private val probeFileName = "wallenc-probe.txt"
private val probePath = "$testFolder/$probeFileName"
private val probePayload = "wallenc-integration-probe".encodeToByteArray()

View File

@@ -1,11 +1,9 @@
package com.github.nullptroma.wallenc.app.sync
import com.github.nullptroma.wallenc.domain.datatypes.StorageSyncPaths
import com.github.nullptroma.wallenc.domain.interfaces.IStorageSyncGroupStore
import com.github.nullptroma.wallenc.domain.interfaces.IVaultsManager
import com.github.nullptroma.wallenc.domain.tasks.StorageSyncTriggerReason
import com.github.nullptroma.wallenc.usecases.RunStorageSyncUseCase
import com.github.nullptroma.wallenc.usecases.StorageSyncReadiness
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.FlowPreview
@@ -13,13 +11,10 @@ import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.launch
import java.util.concurrent.atomic.AtomicBoolean
import javax.inject.Inject
import javax.inject.Singleton
@@ -29,15 +24,11 @@ class StorageSyncBootstrap @Inject constructor(
private val scheduler: StorageSyncScheduler,
private val vaultsManager: IVaultsManager,
private val syncRunner: RunStorageSyncUseCase,
private val syncReadiness: StorageSyncReadiness,
private val groupStore: IStorageSyncGroupStore,
) {
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
private val startupSyncScheduled = AtomicBoolean(false)
fun start() {
scheduler.ensureScheduled()
scheduleStartupSyncOnce()
scope.launch {
combine(
vaultsManager.allStorages,
@@ -86,31 +77,4 @@ class StorageSyncBootstrap @Inject constructor(
}
return System.currentTimeMillis() >= syncRunner.debounceSuppressUntilMs.value
}
/**
* Одна синхронизация после готовности хранилищ при старте процесса — не ждать только WorkManager
* (особенно если periodic work откладывался из‑за перезапусков процесса).
*/
private fun scheduleStartupSyncOnce() {
scope.launch {
combine(
vaultsManager.allStorages,
vaultsManager.unlockManager.openedStorages,
) { rootStorages, opened ->
(rootStorages + opened.values).distinctBy { it.uuid }
}
.map { it.isNotEmpty() }
.distinctUntilChanged()
.filter { it }
.first()
if (!startupSyncScheduled.compareAndSet(false, true)) {
return@launch
}
if (groupStore.getGroups().isEmpty()) {
return@launch
}
syncReadiness.awaitReady()
syncRunner.enqueue(StorageSyncTriggerReason.Background)
}
}
}