1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
|
package org.terst.feedreader.readability
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
/**
* Extraction failures used to be discarded entirely — ReaderScreen did
* `extracted?.content?.let { }`, so any failure left the reader silently showing
* the raw feed content. The bug report that prompted this could say only
* "extraction failed", because the app itself never knew why.
*
* These pin the two things a failure has to produce: something short enough to
* put in front of the user, and something specific enough to debug from.
*/
class ExtractionFailureTest {
@Test
fun `load error names the underlying description`() {
val f = ExtractionFailure.LoadError(-2, "net::ERR_NAME_NOT_RESOLVED")
assertEquals("Couldn't load the page (net::ERR_NAME_NOT_RESOLVED)", f.userMessage())
assertTrue(f.diagnostic().contains("code=-2"))
assertTrue(f.diagnostic().contains("ERR_NAME_NOT_RESOLVED"))
}
@Test
fun `load error without a description still reads properly`() {
assertEquals("Couldn't load the page", ExtractionFailure.LoadError(-1, null).userMessage())
assertEquals("Couldn't load the page", ExtractionFailure.LoadError(-1, " ").userMessage())
}
@Test
fun `http error names the status`() {
val f = ExtractionFailure.HttpError(403, "Forbidden")
assertEquals("Server returned HTTP 403", f.userMessage())
assertTrue(f.diagnostic().contains("status=403"))
}
@Test
fun `timeout is reported in seconds`() {
assertEquals("Page took longer than 25s to load", ExtractionFailure.TimedOut(25_000).userMessage())
assertTrue(ExtractionFailure.TimedOut(25_000).diagnostic().contains("25000ms"))
}
@Test
fun `readability error carries the thrown message`() {
val f = ExtractionFailure.ReadabilityError("TypeError: x is null")
assertTrue(f.userMessage().contains("TypeError"))
assertTrue(f.diagnostic().contains("TypeError"))
}
@Test
fun `no content is distinct from an error`() {
assertEquals("No article text found on the page", ExtractionFailure.NoContent.userMessage())
}
// The raw JS result can be enormous; a diagnostic that pastes all of it is
// unusable in a bug report.
@Test
fun `bad result truncates the raw payload`() {
val f = ExtractionFailure.BadResult("x".repeat(5_000))
assertTrue("diagnostic should be bounded", f.diagnostic().length < 400)
}
// Every case must produce a non-empty message; a blank one would put the
// user back where they started.
@Test
fun `every failure produces both messages`() {
val all = listOf(
ExtractionFailure.LoadError(-6, "ERR_CONNECTION_REFUSED"),
ExtractionFailure.HttpError(500, null),
ExtractionFailure.TimedOut(25_000),
ExtractionFailure.ReadabilityError("boom"),
ExtractionFailure.NoContent,
ExtractionFailure.BadResult("{}"),
)
for (f in all) {
assertTrue("$f has a blank user message", f.userMessage().isNotBlank())
assertTrue("$f has a blank diagnostic", f.diagnostic().isNotBlank())
}
}
}
|