add tests
This commit is contained in:
1 parent
714a2415b7
commit
2424a989bc
2 files changed
+131
No files matched your search
@@ -0,0 +1,130 @@
|
||||
defmodule FeedReader.CollectorTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
alias FeedReader.{Feed, Item, ParseResult}
|
||||
|
||||
@xml ~s(<rss version="2.0"><channel><title>Example</title><item><title>First</title><guid>one</guid></item><item><title>Second</title><guid>two</guid></item></channel></rss>)
|
||||
|
||||
test "collects binary XML through the local event stream" do
|
||||
assert {:ok, %ParseResult{feed: %Feed{title: "Example"}, items: items}} =
|
||||
FeedReader.parse_binary(@xml, max_items: 2)
|
||||
|
||||
assert [%Item{uuid: "one"}, %Item{uuid: "two"}] = items
|
||||
end
|
||||
|
||||
test "collects chunk enumerables without requiring a path" do
|
||||
chunks = String.graphemes(@xml)
|
||||
|
||||
assert {:ok, %ParseResult{items: [%Item{uuid: "one"}, %Item{uuid: "two"}]}} =
|
||||
FeedReader.parse_stream(chunks, max_items: 2)
|
||||
end
|
||||
|
||||
test "collects caller-owned files and preserves them" do
|
||||
path = write_temp(@xml)
|
||||
on_exit(fn -> File.rm(path) end)
|
||||
|
||||
assert {:ok, %ParseResult{items: items}} = FeedReader.parse_file(path, max_items: 2)
|
||||
assert length(items) == 2
|
||||
assert File.read!(path) == @xml
|
||||
end
|
||||
|
||||
test "requires a positive max_items for every collector" do
|
||||
assert {:error, {:invalid_option, :max_items}} = FeedReader.parse_binary(@xml)
|
||||
assert {:error, {:invalid_option, :max_items}} = FeedReader.parse_file("missing.xml")
|
||||
assert {:error, {:invalid_option, :max_items}} = FeedReader.parse_stream([])
|
||||
assert {:error, {:invalid_option, :max_items}} = FeedReader.parse_binary(@xml, max_items: 0)
|
||||
assert {:error, {:invalid_option, :max_items}} = FeedReader.parse_stream([], max_items: :all)
|
||||
end
|
||||
|
||||
test "propagates local source errors" do
|
||||
assert {:error, :enoent} = FeedReader.parse_file("/path/that/does/not/exist", max_items: 1)
|
||||
end
|
||||
|
||||
test "filters before counting and collecting" do
|
||||
filter = fn %Item{uuid: uuid} -> uuid == "two" end
|
||||
|
||||
assert {:ok, %ParseResult{items: [%Item{uuid: "two"}]}} =
|
||||
FeedReader.parse_binary(@xml, max_items: 1, item_filter: filter)
|
||||
end
|
||||
|
||||
test "reports an item beyond the bound instead of returning a partial result" do
|
||||
assert {:error, {:max_items_exceeded, 1}} = FeedReader.parse_binary(@xml, max_items: 1)
|
||||
end
|
||||
|
||||
test "continues to the terminal parser event before returning a malformed-tail error" do
|
||||
assert {:error, _reason} = FeedReader.parse_binary(@xml <> "<broken", max_items: 2)
|
||||
end
|
||||
|
||||
test "rejects invalid chunks and binary arguments" do
|
||||
assert {:error, :invalid_chunk} =
|
||||
FeedReader.parse_stream([@xml, 123], max_items: 2)
|
||||
|
||||
assert {:error, :invalid_binary} = FeedReader.parse_binary(123, max_items: 2)
|
||||
end
|
||||
|
||||
test "returns an error for an invalid item filter" do
|
||||
path = write_temp("<external-feed/>")
|
||||
on_exit(fn -> File.rm(path) end)
|
||||
|
||||
assert {:error, {:invalid_option, :item_filter}} =
|
||||
FeedReader.parse_file(path,
|
||||
format: :external_test,
|
||||
formats: [FeedReader.TestSupport.ExternalFormat],
|
||||
max_items: 1,
|
||||
item_filter: :not_a_function
|
||||
)
|
||||
end
|
||||
|
||||
test "rejects collection without feed metadata" do
|
||||
path = write_temp("<external-feed/>")
|
||||
on_exit(fn -> File.rm(path) end)
|
||||
|
||||
assert {:error, :feed_metadata_missing} =
|
||||
FeedReader.parse_file(path,
|
||||
format: :external_test,
|
||||
formats: [FeedReader.TestSupport.ExternalFormat],
|
||||
max_items: 1
|
||||
)
|
||||
end
|
||||
|
||||
test "handles duplicate metadata, ignored events, and parser errors" do
|
||||
feed = %Feed{title: "Custom", type: :rss}
|
||||
item = %Item{uuid: "custom"}
|
||||
path = write_temp("<external-feed/>")
|
||||
on_exit(fn -> File.rm(path) end)
|
||||
|
||||
opts = [
|
||||
format: :external_test,
|
||||
formats: [FeedReader.TestSupport.ExternalFormat],
|
||||
max_items: 1,
|
||||
collector_events: [{:feed, feed}, :ignored, {:item, item}, {:feed, feed}]
|
||||
]
|
||||
|
||||
assert {:ok, %ParseResult{feed: ^feed, items: [^item]}} = FeedReader.parse_file(path, opts)
|
||||
|
||||
assert {:error, :custom_parser_error} =
|
||||
FeedReader.parse_file(
|
||||
path,
|
||||
Keyword.put(opts, :collector_events, [{:error, :custom_parser_error}])
|
||||
)
|
||||
end
|
||||
|
||||
test "reports an enumerable failure and still cleans its temporary source" do
|
||||
chunks =
|
||||
Stream.resource(fn -> :ok end, fn _state -> raise "chunk failure" end, fn _ -> :ok end)
|
||||
|
||||
assert {:error, {:stream_write_failed, %RuntimeError{message: "chunk failure"}}} =
|
||||
FeedReader.parse_stream(chunks, max_items: 1)
|
||||
end
|
||||
|
||||
defp write_temp(content) do
|
||||
path =
|
||||
Path.join(
|
||||
System.tmp_dir!(),
|
||||
"feed_reader-collector-#{System.unique_integer([:positive])}.xml"
|
||||
)
|
||||
|
||||
File.write!(path, content)
|
||||
path
|
||||
end
|
||||
end
|
||||
@@ -7,6 +7,7 @@ defmodule FeedReader.TestSupport.ExternalFormatHandler do
|
||||
@impl true
|
||||
def handle_event(:start_document, _data, %{opts: opts} = state) do
|
||||
if Keyword.get(opts, :emit_event), do: emit_event(state)
|
||||
Enum.each(Keyword.get(opts, :collector_events, []), fn event -> state.event_sink.(event) end)
|
||||
|
||||
{:ok, state}
|
||||
end
|
||||
|
||||
Reference in new issue
Block a user