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()) } } }