Add frontmatter, markdown, path, xml, rss, and web packages
Sync local packages into the registry repo and update index.json and README.md to include all 9 packages.
This commit is contained in:
3
packages/rss/.gitignore
vendored
Normal file
3
packages/rss/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
_site/
|
||||
.lux_packages/
|
||||
*.bak
|
||||
101
packages/rss/lib.lux
Normal file
101
packages/rss/lib.lux
Normal file
@@ -0,0 +1,101 @@
|
||||
// rss - RSS 2.0 feed generator for Lux
|
||||
//
|
||||
// Generates valid RSS 2.0 XML feeds from structured data.
|
||||
|
||||
import xml
|
||||
|
||||
// An RSS feed item
|
||||
type Item =
|
||||
| Item(String, String, String, String, String)
|
||||
// Item(title, link, description, pubDate, guid)
|
||||
|
||||
// An RSS channel/feed
|
||||
type Feed =
|
||||
| Feed(String, String, String, String, List<Item>)
|
||||
// Feed(title, link, description, language, items)
|
||||
|
||||
// Create a feed item
|
||||
pub fn item(title: String, link: String, description: String, pubDate: String): Item =
|
||||
Item(title, link, description, pubDate, link)
|
||||
|
||||
// Create a feed item with custom GUID
|
||||
pub fn itemWithGuid(title: String, link: String, description: String, pubDate: String, guid: String): Item =
|
||||
Item(title, link, description, pubDate, guid)
|
||||
|
||||
// Create an RSS feed
|
||||
pub fn feed(title: String, link: String, description: String, items: List<Item>): Feed =
|
||||
Feed(title, link, description, "en", items)
|
||||
|
||||
// Create an RSS feed with language
|
||||
pub fn feedWithLang(title: String, link: String, description: String, language: String, items: List<Item>): Feed =
|
||||
Feed(title, link, description, language, items)
|
||||
|
||||
// Access item fields
|
||||
pub fn itemTitle(i: Item): String = match i { Item(t, _, _, _, _) => t, }
|
||||
pub fn itemLink(i: Item): String = match i { Item(_, l, _, _, _) => l, }
|
||||
pub fn itemDescription(i: Item): String = match i { Item(_, _, d, _, _) => d, }
|
||||
pub fn itemPubDate(i: Item): String = match i { Item(_, _, _, p, _) => p, }
|
||||
pub fn itemGuid(i: Item): String = match i { Item(_, _, _, _, g) => g, }
|
||||
|
||||
// Convert ISO date (2025-01-29) to RFC 822 format (29 Jan 2025 00:00:00 GMT)
|
||||
pub fn isoToRfc822(isoDate: String): String =
|
||||
if String.length(isoDate) < 10 then isoDate
|
||||
else {
|
||||
let year = String.substring(isoDate, 0, 4)
|
||||
let month = String.substring(isoDate, 5, 7)
|
||||
let day = String.substring(isoDate, 8, 10)
|
||||
let monthName = if month == "01" then "Jan"
|
||||
else if month == "02" then "Feb"
|
||||
else if month == "03" then "Mar"
|
||||
else if month == "04" then "Apr"
|
||||
else if month == "05" then "May"
|
||||
else if month == "06" then "Jun"
|
||||
else if month == "07" then "Jul"
|
||||
else if month == "08" then "Aug"
|
||||
else if month == "09" then "Sep"
|
||||
else if month == "10" then "Oct"
|
||||
else if month == "11" then "Nov"
|
||||
else "Dec"
|
||||
day + " " + monthName + " " + year + " 00:00:00 GMT"
|
||||
}
|
||||
|
||||
// Render a single item to XML string
|
||||
fn renderItemXml(i: Item): String =
|
||||
match i {
|
||||
Item(title, link, desc, pubDate, guid) =>
|
||||
xml.render(xml.el("item", [
|
||||
xml.textEl("title", title),
|
||||
xml.textEl("link", link),
|
||||
xml.textEl("description", desc),
|
||||
xml.textEl("pubDate", isoToRfc822(pubDate)),
|
||||
xml.textElAttr("guid", [xml.attr("isPermaLink", "true")], guid)
|
||||
])),
|
||||
}
|
||||
|
||||
// Render items to concatenated XML string
|
||||
fn renderItemsXml(items: List<Item>): String =
|
||||
match List.head(items) {
|
||||
None => "",
|
||||
Some(i) => renderItemXml(i) + match List.tail(items) {
|
||||
Some(rest) => renderItemsXml(rest),
|
||||
None => "",
|
||||
},
|
||||
}
|
||||
|
||||
// Render an RSS feed to XML string
|
||||
pub fn render(f: Feed): String =
|
||||
match f {
|
||||
Feed(title, link, desc, lang, items) => {
|
||||
let itemsXml = renderItemsXml(items)
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
|
||||
"<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\">" +
|
||||
"<channel>" +
|
||||
"<title>" + xml.escape(title) + "</title>" +
|
||||
"<link>" + xml.escape(link) + "</link>" +
|
||||
"<description>" + xml.escape(desc) + "</description>" +
|
||||
"<language>" + lang + "</language>" +
|
||||
"<generator>Lux RSS Package</generator>" +
|
||||
itemsXml +
|
||||
"</channel></rss>"
|
||||
},
|
||||
}
|
||||
7
packages/rss/lux.lock
Normal file
7
packages/rss/lux.lock
Normal file
@@ -0,0 +1,7 @@
|
||||
# This file is auto-generated by lux pkg. Do not edit manually.
|
||||
|
||||
[[package]]
|
||||
name = "xml"
|
||||
version = "0.1.0"
|
||||
source = "path:../xml"
|
||||
|
||||
9
packages/rss/lux.toml
Normal file
9
packages/rss/lux.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
[project]
|
||||
name = "rss"
|
||||
version = "0.1.0"
|
||||
description = "RSS 2.0 feed generator for Lux"
|
||||
authors = ["Brandon Lucas"]
|
||||
license = "MIT"
|
||||
|
||||
[dependencies]
|
||||
xml = { version = "0.1.0", path = "../xml" }
|
||||
106
packages/rss/test_integration.lux
Normal file
106
packages/rss/test_integration.lux
Normal file
@@ -0,0 +1,106 @@
|
||||
import lib
|
||||
|
||||
fn contains(haystack: String, needle: String): Bool =
|
||||
match String.indexOf(haystack, needle) {
|
||||
Some(_) => true,
|
||||
None => false,
|
||||
}
|
||||
|
||||
// Build a complete blog RSS feed with multiple posts
|
||||
fn test_full_blog_feed(): Unit with {Test} = {
|
||||
let items = [
|
||||
lib.item("Getting Started with Lux", "https://blog.example.com/getting-started", "Learn the basics of the Lux programming language", "2025-01-15"),
|
||||
lib.item("Advanced Effects in Lux", "https://blog.example.com/advanced-effects", "Deep dive into algebraic effects", "2025-02-01"),
|
||||
lib.item("Building a Static Site", "https://blog.example.com/static-site", "Create a blog with Lux", "2025-02-15")
|
||||
]
|
||||
let f = lib.feed("Lux Programming Blog", "https://blog.example.com", "Tips and tutorials about Lux", items)
|
||||
let xml = lib.render(f)
|
||||
Test.assert(contains(xml, "<?xml version=\"1.0\""), "has XML declaration")
|
||||
Test.assert(contains(xml, "<rss version=\"2.0\""), "has RSS 2.0 tag")
|
||||
Test.assert(contains(xml, "<title>Lux Programming Blog</title>"), "feed title")
|
||||
Test.assert(contains(xml, "<link>https://blog.example.com</link>"), "feed link")
|
||||
Test.assert(contains(xml, "<title>Getting Started with Lux</title>"), "first item title")
|
||||
Test.assert(contains(xml, "<title>Advanced Effects in Lux</title>"), "second item title")
|
||||
Test.assert(contains(xml, "<title>Building a Static Site</title>"), "third item title")
|
||||
Test.assert(contains(xml, "15 Jan 2025 00:00:00 GMT"), "first item date in RFC 822")
|
||||
Test.assert(contains(xml, "01 Feb 2025 00:00:00 GMT"), "second item date in RFC 822")
|
||||
Test.assert(contains(xml, "<generator>Lux RSS Package</generator>"), "has generator tag")
|
||||
}
|
||||
|
||||
// Feed with items containing custom GUIDs
|
||||
fn test_feed_with_custom_guids(): Unit with {Test} = {
|
||||
let items = [
|
||||
lib.itemWithGuid("Post 1", "https://example.com/1", "Desc 1", "2025-01-01", "urn:uuid:1234"),
|
||||
lib.itemWithGuid("Post 2", "https://example.com/2", "Desc 2", "2025-02-01", "urn:uuid:5678")
|
||||
]
|
||||
let f = lib.feed("Blog", "https://example.com", "A blog", items)
|
||||
let xml = lib.render(f)
|
||||
Test.assert(contains(xml, "urn:uuid:1234"), "first custom guid")
|
||||
Test.assert(contains(xml, "urn:uuid:5678"), "second custom guid")
|
||||
}
|
||||
|
||||
// Feed with special characters in all fields
|
||||
fn test_feed_with_special_chars(): Unit with {Test} = {
|
||||
let i = lib.item(
|
||||
"Tom & Jerry's <Adventure>",
|
||||
"https://example.com/tom&jerry",
|
||||
"A \"great\" show with <characters>",
|
||||
"2025-01-01"
|
||||
)
|
||||
let f = lib.feed(
|
||||
"Kids' TV & More",
|
||||
"https://example.com",
|
||||
"Shows for <everyone>",
|
||||
[i]
|
||||
)
|
||||
let xml = lib.render(f)
|
||||
Test.assert(contains(xml, "&"), "ampersand escaped somewhere")
|
||||
Test.assert(contains(xml, "<"), "angle bracket escaped somewhere")
|
||||
}
|
||||
|
||||
// Multilingual feed
|
||||
fn test_multilingual_feed(): Unit with {Test} = {
|
||||
let f = lib.feedWithLang(
|
||||
"Mon Blog",
|
||||
"https://exemple.fr",
|
||||
"Un blog en francais",
|
||||
"fr",
|
||||
[lib.item("Premier Article", "https://exemple.fr/premier", "Le debut", "2025-01-01")]
|
||||
)
|
||||
let xml = lib.render(f)
|
||||
Test.assert(contains(xml, "<language>fr</language>"), "french language tag")
|
||||
Test.assert(contains(xml, "<title>Premier Article</title>"), "french item title")
|
||||
}
|
||||
|
||||
// Empty feed is still valid XML
|
||||
fn test_empty_feed_structure(): Unit with {Test} = {
|
||||
let f = lib.feed("Empty Blog", "https://example.com", "Nothing here yet", [])
|
||||
let xml = lib.render(f)
|
||||
Test.assert(contains(xml, "<?xml"), "has declaration")
|
||||
Test.assert(contains(xml, "<rss"), "has rss tag")
|
||||
Test.assert(contains(xml, "<channel>"), "has channel open")
|
||||
Test.assert(contains(xml, "</channel>"), "has channel close")
|
||||
Test.assert(contains(xml, "</rss>"), "has rss close")
|
||||
Test.assert(contains(xml, "<title>Empty Blog</title>"), "has title")
|
||||
}
|
||||
|
||||
// Feed with many items (stress test)
|
||||
fn test_feed_many_items(): Unit with {Test} = {
|
||||
let items = [
|
||||
lib.item("Post 1", "https://example.com/1", "D1", "2025-01-01"),
|
||||
lib.item("Post 2", "https://example.com/2", "D2", "2025-01-02"),
|
||||
lib.item("Post 3", "https://example.com/3", "D3", "2025-01-03"),
|
||||
lib.item("Post 4", "https://example.com/4", "D4", "2025-01-04"),
|
||||
lib.item("Post 5", "https://example.com/5", "D5", "2025-01-05"),
|
||||
lib.item("Post 6", "https://example.com/6", "D6", "2025-01-06"),
|
||||
lib.item("Post 7", "https://example.com/7", "D7", "2025-01-07"),
|
||||
lib.item("Post 8", "https://example.com/8", "D8", "2025-01-08"),
|
||||
lib.item("Post 9", "https://example.com/9", "D9", "2025-01-09"),
|
||||
lib.item("Post 10", "https://example.com/10", "D10", "2025-01-10")
|
||||
]
|
||||
let f = lib.feed("Big Blog", "https://example.com", "Many posts", items)
|
||||
let xml = lib.render(f)
|
||||
Test.assert(contains(xml, "<title>Post 1</title>"), "first item")
|
||||
Test.assert(contains(xml, "<title>Post 10</title>"), "last item")
|
||||
Test.assert(contains(xml, "10 Jan 2025"), "last item date")
|
||||
}
|
||||
67
packages/rss/test_rss.lux
Normal file
67
packages/rss/test_rss.lux
Normal file
@@ -0,0 +1,67 @@
|
||||
import lib
|
||||
|
||||
fn contains(haystack: String, needle: String): Bool =
|
||||
match String.indexOf(haystack, needle) {
|
||||
Some(_) => true,
|
||||
None => false,
|
||||
}
|
||||
|
||||
fn test_create_item(): Unit with {Test} = {
|
||||
let i = lib.item("My Post", "https://example.com/post", "A description", "2025-01-29")
|
||||
Test.assertEqualMsg("My Post", lib.itemTitle(i), "item title")
|
||||
Test.assertEqualMsg("https://example.com/post", lib.itemLink(i), "item link")
|
||||
Test.assertEqualMsg("A description", lib.itemDescription(i), "item description")
|
||||
Test.assertEqualMsg("2025-01-29", lib.itemPubDate(i), "item pubDate")
|
||||
Test.assertEqualMsg("https://example.com/post", lib.itemGuid(i), "item guid defaults to link")
|
||||
}
|
||||
|
||||
fn test_item_with_guid(): Unit with {Test} = {
|
||||
let i = lib.itemWithGuid("Post", "https://example.com", "Desc", "2025-01-29", "custom-guid-123")
|
||||
Test.assertEqualMsg("custom-guid-123", lib.itemGuid(i), "item custom guid")
|
||||
}
|
||||
|
||||
fn test_date_conversion(): Unit with {Test} = {
|
||||
Test.assertEqualMsg("29 Jan 2025 00:00:00 GMT", lib.isoToRfc822("2025-01-29"), "date jan")
|
||||
Test.assertEqualMsg("31 Dec 2025 00:00:00 GMT", lib.isoToRfc822("2025-12-31"), "date dec")
|
||||
Test.assertEqualMsg("15 Jun 2025 00:00:00 GMT", lib.isoToRfc822("2025-06-15"), "date jun")
|
||||
Test.assertEqualMsg("bad", lib.isoToRfc822("bad"), "date short string passthrough")
|
||||
}
|
||||
|
||||
fn test_empty_feed(): Unit with {Test} = {
|
||||
let emptyFeed = lib.feed("My Blog", "https://example.com", "Blog about things", [])
|
||||
let xml = lib.render(emptyFeed)
|
||||
Test.assert(contains(xml, "<?xml version=\"1.0\""), "xml declaration")
|
||||
Test.assert(contains(xml, "<rss version=\"2.0\""), "rss tag")
|
||||
Test.assert(contains(xml, "<title>My Blog</title>"), "feed title")
|
||||
Test.assert(contains(xml, "<link>https://example.com</link>"), "feed link")
|
||||
Test.assert(contains(xml, "<description>Blog about things</description>"), "feed description")
|
||||
Test.assert(contains(xml, "<language>en</language>"), "feed language")
|
||||
}
|
||||
|
||||
fn test_feed_with_items(): Unit with {Test} = {
|
||||
let items = [
|
||||
lib.item("First Post", "https://example.com/first", "First!", "2025-01-29"),
|
||||
lib.item("Second Post", "https://example.com/second", "Second!", "2025-02-01")
|
||||
]
|
||||
let fullFeed = lib.feed("Blog", "https://example.com", "A blog", items)
|
||||
let xml = lib.render(fullFeed)
|
||||
Test.assert(contains(xml, "<item>"), "has item tag")
|
||||
Test.assert(contains(xml, "<title>First Post</title>"), "first item title")
|
||||
Test.assert(contains(xml, "<title>Second Post</title>"), "second item title")
|
||||
Test.assert(contains(xml, "29 Jan 2025 00:00:00 GMT"), "rfc822 date in feed")
|
||||
Test.assert(contains(xml, "<guid isPermaLink=\"true\">"), "guid with permalink")
|
||||
}
|
||||
|
||||
fn test_feed_with_language(): Unit with {Test} = {
|
||||
let frFeed = lib.feedWithLang("Mon Blog", "https://example.fr", "Un blog", "fr", [])
|
||||
let xml = lib.render(frFeed)
|
||||
Test.assert(contains(xml, "<language>fr</language>"), "custom language")
|
||||
}
|
||||
|
||||
fn test_special_characters(): Unit with {Test} = {
|
||||
let specialItem = lib.item("A & B <C>", "https://example.com", "\"quoted\" & <tagged>", "2025-01-01")
|
||||
let specialFeed = lib.feed("Test", "https://example.com", "Test", [specialItem])
|
||||
let xml = lib.render(specialFeed)
|
||||
Test.assert(contains(xml, "&"), "ampersand escaped")
|
||||
Test.assert(contains(xml, "<"), "angle bracket escaped")
|
||||
}
|
||||
103
packages/rss/test_snapshot.lux
Normal file
103
packages/rss/test_snapshot.lux
Normal file
@@ -0,0 +1,103 @@
|
||||
import lib
|
||||
|
||||
fn contains(haystack: String, needle: String): Bool =
|
||||
match String.indexOf(haystack, needle) {
|
||||
Some(_) => true,
|
||||
None => false,
|
||||
}
|
||||
|
||||
// Snapshot: complete single-item feed
|
||||
fn test_snapshot_single_item_feed(): Unit with {Test} = {
|
||||
let f = lib.feed(
|
||||
"My Blog",
|
||||
"https://example.com",
|
||||
"A personal blog",
|
||||
[lib.item("Hello World", "https://example.com/hello", "My first post", "2025-01-29")]
|
||||
)
|
||||
let xml = lib.render(f)
|
||||
// Verify exact structure of the feed
|
||||
Test.assert(String.startsWith(xml, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"), "snap: XML declaration")
|
||||
Test.assert(contains(xml, "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\">"), "snap: RSS opening tag")
|
||||
Test.assert(contains(xml, "<channel>"), "snap: channel open")
|
||||
Test.assert(contains(xml, "<title>My Blog</title>"), "snap: feed title")
|
||||
Test.assert(contains(xml, "<link>https://example.com</link>"), "snap: feed link")
|
||||
Test.assert(contains(xml, "<description>A personal blog</description>"), "snap: feed desc")
|
||||
Test.assert(contains(xml, "<language>en</language>"), "snap: language")
|
||||
Test.assert(contains(xml, "<generator>Lux RSS Package</generator>"), "snap: generator")
|
||||
Test.assert(contains(xml, "<item>"), "snap: item open")
|
||||
Test.assert(contains(xml, "<title>Hello World</title>"), "snap: item title")
|
||||
Test.assert(contains(xml, "<link>https://example.com/hello</link>"), "snap: item link")
|
||||
Test.assert(contains(xml, "<description>My first post</description>"), "snap: item desc")
|
||||
Test.assert(contains(xml, "<pubDate>29 Jan 2025 00:00:00 GMT</pubDate>"), "snap: item pubDate")
|
||||
Test.assert(contains(xml, "<guid isPermaLink=\"true\">https://example.com/hello</guid>"), "snap: item guid")
|
||||
Test.assert(contains(xml, "</item>"), "snap: item close")
|
||||
Test.assert(contains(xml, "</channel></rss>"), "snap: channel and rss close")
|
||||
}
|
||||
|
||||
// Snapshot: empty feed
|
||||
fn test_snapshot_empty_feed(): Unit with {Test} = {
|
||||
let f = lib.feed("Empty", "https://example.com", "Nothing", [])
|
||||
let xml = lib.render(f)
|
||||
Test.assert(String.startsWith(xml, "<?xml version=\"1.0\""), "snap: starts with declaration")
|
||||
// Empty feed should have no <item> tags
|
||||
Test.assertEqualMsg(false, contains(xml, "<item>"), "snap: no item tags")
|
||||
Test.assert(contains(xml, "<title>Empty</title>"), "snap: title present")
|
||||
Test.assert(String.endsWith(xml, "</channel></rss>"), "snap: ends properly")
|
||||
}
|
||||
|
||||
// Snapshot: feed with custom language
|
||||
fn test_snapshot_french_feed(): Unit with {Test} = {
|
||||
let f = lib.feedWithLang("Blog Francais", "https://exemple.fr", "Un blog", "fr", [
|
||||
lib.item("Bonjour", "https://exemple.fr/bonjour", "Premier article", "2025-06-15")
|
||||
])
|
||||
let xml = lib.render(f)
|
||||
Test.assert(contains(xml, "<language>fr</language>"), "snap: french language")
|
||||
Test.assert(contains(xml, "<title>Bonjour</title>"), "snap: french item title")
|
||||
Test.assert(contains(xml, "15 Jun 2025 00:00:00 GMT"), "snap: french item date")
|
||||
}
|
||||
|
||||
// Snapshot: feed with escaped content
|
||||
fn test_snapshot_escaped_feed(): Unit with {Test} = {
|
||||
let f = lib.feed(
|
||||
"Tom & Jerry",
|
||||
"https://example.com",
|
||||
"A <fun> show",
|
||||
[lib.item("Episode: \"The Chase\"", "https://example.com/1", "Tom & Jerry's adventure", "2025-01-01")]
|
||||
)
|
||||
let xml = lib.render(f)
|
||||
Test.assert(contains(xml, "<title>Tom & Jerry</title>"), "snap: escaped feed title")
|
||||
Test.assert(contains(xml, "<description>A <fun> show</description>"), "snap: escaped feed desc")
|
||||
}
|
||||
|
||||
// Snapshot: multi-item feed order
|
||||
fn test_snapshot_multi_item_order(): Unit with {Test} = {
|
||||
let f = lib.feed("Blog", "https://example.com", "A blog", [
|
||||
lib.item("First", "https://example.com/1", "D1", "2025-01-01"),
|
||||
lib.item("Second", "https://example.com/2", "D2", "2025-02-01"),
|
||||
lib.item("Third", "https://example.com/3", "D3", "2025-03-01")
|
||||
])
|
||||
let xml = lib.render(f)
|
||||
// Items should appear in order
|
||||
let firstIdx = match String.indexOf(xml, "<title>First</title>") { Some(i) => i, None => -1 }
|
||||
let secondIdx = match String.indexOf(xml, "<title>Second</title>") { Some(i) => i, None => -1 }
|
||||
let thirdIdx = match String.indexOf(xml, "<title>Third</title>") { Some(i) => i, None => -1 }
|
||||
Test.assert(firstIdx > 0, "snap: first item found")
|
||||
Test.assert(secondIdx > firstIdx, "snap: second after first")
|
||||
Test.assert(thirdIdx > secondIdx, "snap: third after second")
|
||||
}
|
||||
|
||||
// Snapshot: date conversion across all months
|
||||
fn test_snapshot_all_month_dates(): Unit with {Test} = {
|
||||
Test.assertEqualMsg("01 Jan 2025 00:00:00 GMT", lib.isoToRfc822("2025-01-01"), "snap: Jan")
|
||||
Test.assertEqualMsg("01 Feb 2025 00:00:00 GMT", lib.isoToRfc822("2025-02-01"), "snap: Feb")
|
||||
Test.assertEqualMsg("01 Mar 2025 00:00:00 GMT", lib.isoToRfc822("2025-03-01"), "snap: Mar")
|
||||
Test.assertEqualMsg("01 Apr 2025 00:00:00 GMT", lib.isoToRfc822("2025-04-01"), "snap: Apr")
|
||||
Test.assertEqualMsg("01 May 2025 00:00:00 GMT", lib.isoToRfc822("2025-05-01"), "snap: May")
|
||||
Test.assertEqualMsg("01 Jun 2025 00:00:00 GMT", lib.isoToRfc822("2025-06-01"), "snap: Jun")
|
||||
Test.assertEqualMsg("01 Jul 2025 00:00:00 GMT", lib.isoToRfc822("2025-07-01"), "snap: Jul")
|
||||
Test.assertEqualMsg("01 Aug 2025 00:00:00 GMT", lib.isoToRfc822("2025-08-01"), "snap: Aug")
|
||||
Test.assertEqualMsg("01 Sep 2025 00:00:00 GMT", lib.isoToRfc822("2025-09-01"), "snap: Sep")
|
||||
Test.assertEqualMsg("01 Oct 2025 00:00:00 GMT", lib.isoToRfc822("2025-10-01"), "snap: Oct")
|
||||
Test.assertEqualMsg("01 Nov 2025 00:00:00 GMT", lib.isoToRfc822("2025-11-01"), "snap: Nov")
|
||||
Test.assertEqualMsg("01 Dec 2025 00:00:00 GMT", lib.isoToRfc822("2025-12-01"), "snap: Dec")
|
||||
}
|
||||
111
packages/rss/test_unit.lux
Normal file
111
packages/rss/test_unit.lux
Normal file
@@ -0,0 +1,111 @@
|
||||
import lib
|
||||
|
||||
// --- item creation ---
|
||||
|
||||
fn test_item_defaults_guid_to_link(): Unit with {Test} = {
|
||||
let i = lib.item("Title", "https://example.com/post", "Desc", "2025-01-01")
|
||||
Test.assertEqualMsg("https://example.com/post", lib.itemGuid(i), "guid defaults to link")
|
||||
}
|
||||
|
||||
fn test_item_with_custom_guid(): Unit with {Test} = {
|
||||
let i = lib.itemWithGuid("Title", "https://example.com", "Desc", "2025-01-01", "custom-123")
|
||||
Test.assertEqualMsg("custom-123", lib.itemGuid(i), "custom guid preserved")
|
||||
}
|
||||
|
||||
fn test_item_empty_fields(): Unit with {Test} = {
|
||||
let i = lib.item("", "", "", "")
|
||||
Test.assertEqualMsg("", lib.itemTitle(i), "empty title")
|
||||
Test.assertEqualMsg("", lib.itemLink(i), "empty link")
|
||||
Test.assertEqualMsg("", lib.itemDescription(i), "empty description")
|
||||
Test.assertEqualMsg("", lib.itemPubDate(i), "empty pubDate")
|
||||
}
|
||||
|
||||
fn test_item_special_chars(): Unit with {Test} = {
|
||||
let i = lib.item("A & B <C>", "https://example.com?a=1&b=2", "\"quotes\" & <tags>", "2025-01-01")
|
||||
Test.assertEqualMsg("A & B <C>", lib.itemTitle(i), "special chars in title preserved")
|
||||
Test.assertEqualMsg("\"quotes\" & <tags>", lib.itemDescription(i), "special chars in desc preserved")
|
||||
}
|
||||
|
||||
// --- all accessor functions ---
|
||||
|
||||
fn test_item_accessors(): Unit with {Test} = {
|
||||
let i = lib.item("T", "L", "D", "P")
|
||||
Test.assertEqualMsg("T", lib.itemTitle(i), "itemTitle")
|
||||
Test.assertEqualMsg("L", lib.itemLink(i), "itemLink")
|
||||
Test.assertEqualMsg("D", lib.itemDescription(i), "itemDescription")
|
||||
Test.assertEqualMsg("P", lib.itemPubDate(i), "itemPubDate")
|
||||
Test.assertEqualMsg("L", lib.itemGuid(i), "itemGuid defaults to link")
|
||||
}
|
||||
|
||||
// --- date conversion for all months ---
|
||||
|
||||
fn test_date_january(): Unit with {Test} =
|
||||
Test.assertEqualMsg("15 Jan 2025 00:00:00 GMT", lib.isoToRfc822("2025-01-15"), "January")
|
||||
|
||||
fn test_date_february(): Unit with {Test} =
|
||||
Test.assertEqualMsg("28 Feb 2025 00:00:00 GMT", lib.isoToRfc822("2025-02-28"), "February")
|
||||
|
||||
fn test_date_march(): Unit with {Test} =
|
||||
Test.assertEqualMsg("01 Mar 2025 00:00:00 GMT", lib.isoToRfc822("2025-03-01"), "March")
|
||||
|
||||
fn test_date_april(): Unit with {Test} =
|
||||
Test.assertEqualMsg("30 Apr 2025 00:00:00 GMT", lib.isoToRfc822("2025-04-30"), "April")
|
||||
|
||||
fn test_date_may(): Unit with {Test} =
|
||||
Test.assertEqualMsg("01 May 2025 00:00:00 GMT", lib.isoToRfc822("2025-05-01"), "May")
|
||||
|
||||
fn test_date_june(): Unit with {Test} =
|
||||
Test.assertEqualMsg("15 Jun 2025 00:00:00 GMT", lib.isoToRfc822("2025-06-15"), "June")
|
||||
|
||||
fn test_date_july(): Unit with {Test} =
|
||||
Test.assertEqualMsg("04 Jul 2025 00:00:00 GMT", lib.isoToRfc822("2025-07-04"), "July")
|
||||
|
||||
fn test_date_august(): Unit with {Test} =
|
||||
Test.assertEqualMsg("20 Aug 2025 00:00:00 GMT", lib.isoToRfc822("2025-08-20"), "August")
|
||||
|
||||
fn test_date_september(): Unit with {Test} =
|
||||
Test.assertEqualMsg("10 Sep 2025 00:00:00 GMT", lib.isoToRfc822("2025-09-10"), "September")
|
||||
|
||||
fn test_date_october(): Unit with {Test} =
|
||||
Test.assertEqualMsg("31 Oct 2025 00:00:00 GMT", lib.isoToRfc822("2025-10-31"), "October")
|
||||
|
||||
fn test_date_november(): Unit with {Test} =
|
||||
Test.assertEqualMsg("11 Nov 2025 00:00:00 GMT", lib.isoToRfc822("2025-11-11"), "November")
|
||||
|
||||
fn test_date_december(): Unit with {Test} =
|
||||
Test.assertEqualMsg("25 Dec 2025 00:00:00 GMT", lib.isoToRfc822("2025-12-25"), "December")
|
||||
|
||||
fn test_date_short_string(): Unit with {Test} =
|
||||
Test.assertEqualMsg("bad", lib.isoToRfc822("bad"), "short string passthrough")
|
||||
|
||||
fn test_date_empty(): Unit with {Test} =
|
||||
Test.assertEqualMsg("", lib.isoToRfc822(""), "empty date passthrough")
|
||||
|
||||
fn test_date_partial(): Unit with {Test} =
|
||||
Test.assertEqualMsg("2025-01", lib.isoToRfc822("2025-01"), "partial date passthrough")
|
||||
|
||||
// --- feed creation ---
|
||||
|
||||
fn test_feed_default_language(): Unit with {Test} = {
|
||||
let f = lib.feed("Title", "https://example.com", "Desc", [])
|
||||
let xml = lib.render(f)
|
||||
Test.assert(String.contains(xml, "<language>en</language>"), "default language is en")
|
||||
}
|
||||
|
||||
fn test_feed_custom_language(): Unit with {Test} = {
|
||||
let f = lib.feedWithLang("Title", "https://example.com", "Desc", "fr", [])
|
||||
let xml = lib.render(f)
|
||||
Test.assert(String.contains(xml, "<language>fr</language>"), "custom language fr")
|
||||
}
|
||||
|
||||
fn test_feed_escapes_title(): Unit with {Test} = {
|
||||
let f = lib.feed("A & B", "https://example.com", "Desc", [])
|
||||
let xml = lib.render(f)
|
||||
Test.assert(String.contains(xml, "<title>A & B</title>"), "feed title is escaped")
|
||||
}
|
||||
|
||||
fn test_feed_escapes_description(): Unit with {Test} = {
|
||||
let f = lib.feed("Title", "https://example.com", "<b>Bold</b> desc", [])
|
||||
let xml = lib.render(f)
|
||||
Test.assert(String.contains(xml, "<b>Bold</b>"), "feed description is escaped")
|
||||
}
|
||||
Reference in New Issue
Block a user