merge: W3 — B1 /upload(大图走上传,历史只留 URL)
This commit is contained in:
commit
664fd8dcef
11 files changed
+869
-6
No files matched your search
@@ -0,0 +1,89 @@
|
||||
package top.yeij.cyrene.ui.chat
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import top.yeij.cyrene.core.error.CyreneError
|
||||
import top.yeij.cyrene.data.api.CyreneException
|
||||
|
||||
/**
|
||||
* 上传失败的文案(纯函数)。
|
||||
*
|
||||
* 这里锚的是**行为承诺**,不是措辞本身:上传失败时这条消息**没有发出去**(文本与图一起
|
||||
* 回到输入框),而且**绝不**退回内联 base64——退回就等于把「省下历史重量」当场作废,
|
||||
* 还是在用户看不见的情况下换了一条语义不同的路。
|
||||
*
|
||||
* 两个服务端拒绝码对应的是「换一张图」而不是「重试」,必须与网络失败分开说。
|
||||
*/
|
||||
class ImageUploadFailureTest {
|
||||
|
||||
private fun server(code: String, message: String, status: Int = 400) =
|
||||
CyreneException(CyreneError.Server(code, message, status))
|
||||
|
||||
@Test
|
||||
fun `payload_too_large asks for a smaller image, not a retry`() {
|
||||
val message = uploadFailureMessage(server("payload_too_large", "file exceeds the 5MB limit", 413))
|
||||
|
||||
assertEquals("图片超过服务端 5 MB 上限,请换一张更小的", message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unsupported_media_type says the format is refused`() {
|
||||
val message = uploadFailureMessage(
|
||||
server("unsupported_media_type", "only png/jpeg/gif/webp/bmp images are accepted", 415),
|
||||
)
|
||||
|
||||
assertEquals("服务端不接受这张图片的格式,请换一张", message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a network failure names the step that failed and keeps the reason`() {
|
||||
val message = uploadFailureMessage(CyreneException(CyreneError.Local(CyreneError.LocalKind.NoNetwork)))
|
||||
|
||||
assertEquals("图片上传失败,网络不可用,请检查连接", message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a timeout keeps its own wording`() {
|
||||
val message = uploadFailureMessage(CyreneException(CyreneError.Local(CyreneError.LocalKind.Timeout)))
|
||||
|
||||
assertEquals("图片上传失败,连接超时,服务器没有响应", message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a server error keeps the server message instead of inventing one`() {
|
||||
val message = uploadFailureMessage(server("upload_failed", "failed to store upload", 500))
|
||||
|
||||
assertEquals("图片上传失败,failed to store upload", message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `an expired session is still an upload failure, never a silent success`() {
|
||||
val message = uploadFailureMessage(CyreneException(CyreneError.Unauthorized("unauthorized", "auth: unauthorized")))
|
||||
|
||||
assertTrue(message, message.startsWith("图片上传失败"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a non Cyrene throwable still produces an actionable message`() {
|
||||
assertEquals("图片上传失败,请重试", uploadFailureMessage(IllegalStateException("boom")))
|
||||
assertEquals("图片上传失败,请重试", uploadFailureMessage(RuntimeException()))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `no failure path ever falls back to inlining the image`() {
|
||||
// 文案里绝不能出现「改用内联 / 直接发送」这类暗示:回退内联正是要消灭的成本。
|
||||
val messages = listOf(
|
||||
uploadFailureMessage(server("payload_too_large", "x", 413)),
|
||||
uploadFailureMessage(server("unsupported_media_type", "x", 415)),
|
||||
uploadFailureMessage(server("upload_failed", "x", 500)),
|
||||
uploadFailureMessage(CyreneException(CyreneError.Local(CyreneError.LocalKind.ConnectFailed))),
|
||||
uploadFailureMessage(IllegalStateException()),
|
||||
)
|
||||
|
||||
assertTrue(
|
||||
messages.toString(),
|
||||
messages.all { it.contains("换一张") || it.contains("重试") || it.contains("上传失败") },
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user