diff --git a/test/feed_reader/atom_test.exs b/test/feed_reader/atom_test.exs new file mode 100644 index 0000000..4ea007f --- /dev/null +++ b/test/feed_reader/atom_test.exs @@ -0,0 +1,71 @@ +defmodule FeedReader.AtomTest do + use ExUnit.Case, async: true + alias FeedReader.{Feed, Formats.Atom, Item} + + @xml ~s(NewsUpdatesStorytag:example.com,2025:1Writer2025-02-24T17:00:05Z<p>Full text</p>Brief) + @xhtml ~s(Feedhttps://example.com/fallback

Hello world

12345not a date
) + + test "normalizes feed metadata and entry fields" do + assert {:ok, _} = parse(@xml) + assert_receive {:feed, %Feed{title: "News", type: :atom, categories: ["Tech"]}} + + assert_receive {:item, %Item{content_full: "

Full text

"}} + assert %Feed{html_url: nil} = Atom.normalize_feed(%{feed: %{}}, []) + + assert {:ok, _} = parse(@xhtml) + + assert_receive {:feed, %Feed{html_url: "https://example.com/fallback"}} + assert_receive {:item, %Item{content_full: xhtml, content_summary: nil}} + assert xhtml == "

Hello world

" + assert_receive {:item, %Item{title: "12345", content_summary: "12345"}} + end + + test "accounts for parser failures and closes empty feeds" do + handler = Atom.handler() + sink = fn event -> send(self(), event) end + limited = Atom.new_parser_state(fn _ -> :ok end, max_xml_events: 0) + + assert {:stop, %{error: {:resource_limit, :max_xml_events, 0}}} = + handler.handle_event(:start_document, [], limited) + + assert {:stop, %{error: {:resource_limit, :max_xml_events, 0}}} = + handler.handle_event(:end_element, "feed", limited) + + empty = Atom.new_parser_state(fn _ -> :ok end, []) + + assert {:stop, %{error: :invalid_sax_event}} = + handler.handle_event(:characters, "text", empty) + + assert {:stop, %{error: :invalid_sax_event}} = + handler.handle_event(:end_element, "feed", %{ + empty + | limits: %{empty.limits | depth: 1} + }) + + frame = %{name: "entry", attrs: %{}, children: %{}, text: "", markup: []} + + limited_item = %{ + empty + | limits: %{empty.limits | scope: :item, max_item_bytes: 0}, + frames: [frame] + } + + assert {:stop, %{error: {:resource_limit, :max_item_bytes, 0}}} = + handler.handle_event(:characters, "x", limited_item) + + state = Atom.new_parser_state(fn _ -> :ok end, max_item_bytes: 1) + assert {:ok, state} = handler.handle_event(:start_element, {"feed", []}, state) + + assert {:stop, %{error: {:resource_limit, :max_item_bytes, 1}}} = + handler.handle_event(:start_element, {"entry", []}, state) + + assert {:ok, _} = Saxy.parse_string("", handler, Atom.new_parser_state(sink, [])) + assert_receive {:feed, %Feed{type: :atom}} + + assert {:ok, _} = + Saxy.parse_string("", handler, Atom.new_parser_state(fn _ -> :ok end, [])) + end + + defp parse(xml), + do: Saxy.parse_string(xml, Atom.handler(), Atom.new_parser_state(&send(self(), &1), [])) +end