apps\online\src\test\java\cn\nfarms\online\DemoTicketApiTest.kt¶
源码路径:
apps\online\src\test\java\cn\nfarms\online\DemoTicketApiTest.kt· 98 行
package cn.nfarms.online
import cn.nfarms.core.store.AppFiles
import cn.nfarms.online.data.ConflictException
import cn.nfarms.online.data.DemoTicketApi
import cn.nfarms.online.data.Merge
import cn.nfarms.online.data.MergeResult
import java.io.File
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
/**
* 演示后端 + 三路合并的联动。
*
* 这里测的是**真后端行为**:乐观锁(rev)拒绝旧版本提交、同一单被"别人"改过之后
* 必须判冲突。它们是「冲突合并」能不能正常工作的前提 ——
* 只有合并函数对、但后端接受旧版本覆盖,这套逻辑等于不存在。
*/
class DemoTicketApiTest {
private lateinit var api: DemoTicketApi
private lateinit var tmp: File
@Before
fun setUp() {
api = DemoTicketApi()
tmp = File(System.getProperty("java.io.tmpdir"), "nfarms-online-${System.nanoTime()}")
tmp.mkdirs()
}
@After
fun tearDown() { tmp.deleteRecursively() }
@Test
fun push_with_stale_rev_is_rejected() = runBlocking {
val remote = api.list().first { it.id == "T-1001" }
val stale = remote.copy(rev = 1, title = "我的旧版本")
val failed = runCatching { api.push(stale, baseRev = 1) }
assertTrue("用旧 rev 提交必须被拒(乐观锁)", failed.exceptionOrNull() is ConflictException)
// 拒绝时要把**当前**远端值抛回去,否则客户端没法做三路合并。
// 这里不写死 rev 数字:种子数据的 rev 会随演示数据调整,写死只会让测试变脆。
assertEquals(
remote.rev,
(failed.exceptionOrNull() as ConflictException).remote.rev,
)
}
@Test
fun push_with_current_rev_succeeds_and_bumps_rev() = runBlocking {
val remote = api.list().first { it.id == "T-1001" }
val ok = api.push(remote.copy(title = "改好了"), baseRev = remote.rev)
assertEquals(remote.rev + 1, ok.rev)
assertEquals("改好了", ok.title)
}
@Test
fun remote_edit_creates_a_real_conflict_against_local_edit() = runBlocking {
val base = api.list().first { it.id == "T-1001" }
// 本地改
val local = base.copy(title = "本地改的")
// 别人也改
api.simulateRemoteEdit(base.id, "远端改的")
val remote = api.list().first { it.id == base.id }
val merged = Merge.merge(local, remote, base)
assertTrue("两边都改 → 必须判冲突", merged is MergeResult.Conflict)
}
@Test
fun pushing_a_new_ticket_creates_it_with_rev_one() = runBlocking {
val draft = cn.nfarms.online.data.Ticket(
id = "D-1", title = "新工单", body = "描述", status = "open",
updatedAt = System.currentTimeMillis(), rev = 0,
)
val created = api.push(draft, baseRev = 0)
assertEquals(1, created.rev)
assertNotNull(api.revOf("D-1"))
}
/** 附件校验只依赖文件本身,不依赖 Android;这里顺带验证大小边界。 */
@Test
fun attachment_size_bounds() {
fun ok(size: Long) = cn.nfarms.online.data.Attachment("A", "f", size, "x").sizeOk
assertTrue(ok(1))
assertTrue(ok(10L * 1024 * 1024))
assertTrue(!ok(0))
assertTrue(!ok(10L * 1024 * 1024 + 1))
}
@Suppress("unused")
private fun filesForTest(): AppFiles = AppFiles.at(tmp, tmp, "online")
}