summaryrefslogtreecommitdiff
path: root/app/src/test/java/org/terst/feedreader/data/FeedDiscoveryTest.kt
blob: eee238ac2235a6a92816ba76915ab773524db6b1 (plain)
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
83
84
85
86
87
88
89
90
91
92
93
package org.terst.feedreader.data

import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Test

/**
 * Finding the feed when what you were given is a site.
 *
 * Reported live: "Can't subscribe to https://heathercoxrichardson.substack.com/". Pasting a
 * site's home page was the only thing to hand -- a site does not advertise its feed URL
 * anywhere a reader can see, it advertises it in a <link rel="alternate"> that every other
 * reader looks for and this one did not.
 */
class FeedDiscoveryTest {

    /** Verbatim from the reported page, 2026-10-21. */
    private val substackHead =
        """<!doctype html><html><head><title>Letters from an American</title>""" +
            """<link rel="alternate" type="application/rss+xml" href="/feed" """ +
            """title="Letters from an American"/></head><body>...</body></html>"""

    @Test
    fun `finds the feed a Substack home page advertises, resolved against the site`() {
        assertEquals(
            "https://heathercoxrichardson.substack.com/feed",
            FeedDiscovery.findFeedUrl(substackHead, "https://heathercoxrichardson.substack.com/")
        )
    }

    @Test
    fun `finds an Atom feed too`() {
        val html = """<link rel="alternate" type="application/atom+xml" href="/atom.xml">"""

        assertEquals("https://example.com/atom.xml", FeedDiscovery.findFeedUrl(html, "https://example.com/"))
    }

    @Test
    fun `keeps an absolute href as it stands`() {
        val html = """<link rel="alternate" type="application/rss+xml" href="https://feeds.example.net/x.xml">"""

        assertEquals("https://feeds.example.net/x.xml", FeedDiscovery.findFeedUrl(html, "https://example.com/blog/"))
    }

    @Test
    fun `resolves a relative href against the page, not the site root`() {
        val html = """<link rel="alternate" type="application/rss+xml" href="feed.xml">"""

        assertEquals("https://example.com/blog/feed.xml", FeedDiscovery.findFeedUrl(html, "https://example.com/blog/"))
    }

    @Test
    fun `copes with the attributes in any order and single quotes`() {
        val html = """<link type='application/rss+xml' href='/rss' rel='alternate'>"""

        assertEquals("https://example.com/rss", FeedDiscovery.findFeedUrl(html, "https://example.com"))
    }

    @Test
    fun `prefers the first feed when a page advertises several`() {
        val html =
            """<link rel="alternate" type="application/rss+xml" href="/feed" title="Posts">""" +
                """<link rel="alternate" type="application/rss+xml" href="/comments/feed" title="Comments">"""

        assertEquals("https://example.com/feed", FeedDiscovery.findFeedUrl(html, "https://example.com/"))
    }

    @Test
    fun `ignores an alternate link that is not a feed`() {
        val html =
            """<link rel="alternate" hreflang="fr" href="/fr/">""" +
                """<link rel="alternate" type="application/json" href="/feed.json">"""

        assertNull(FeedDiscovery.findFeedUrl(html, "https://example.com/"))
    }

    @Test
    fun `a page with no feed at all reports nothing rather than guessing a path`() {
        assertNull(FeedDiscovery.findFeedUrl("<html><head></head><body>hi</body></html>", "https://example.com/"))
    }

    @Test
    fun `an http feed link is upgraded, since the app cannot fetch cleartext`() {
        val html = """<link rel="alternate" type="application/rss+xml" href="http://example.com/feed">"""

        assertEquals("https://example.com/feed", FeedDiscovery.findFeedUrl(html, "https://example.com/"))
    }

    @Test
    fun `xml declaration means this was already a feed, so there is nothing to discover`() {
        assertNull(FeedDiscovery.findFeedUrl("<?xml version=\"1.0\"?><rss><channel></channel></rss>", "https://x/f"))
    }
}