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/fallback12345not 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 == ""
+ 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