Skip to content

Commit 58424b1

Browse files
committed
Implement sentinel-based line splitter and add mock unit tests for stream retries and errors
1 parent 806365d commit 58424b1

2 files changed

Lines changed: 87 additions & 11 deletions

File tree

lib/common_crawl/index.ex

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -237,19 +237,24 @@ defmodule CommonCrawl.Index do
237237
end
238238

239239
defp split_lines(chunk_stream) do
240-
Stream.transform(
241-
chunk_stream,
240+
chunk_stream
241+
|> Stream.concat([:end_of_stream])
242+
|> Stream.transform(
242243
fn -> "" end,
243-
fn chunk, buffer ->
244-
data = buffer <> chunk
245-
lines = String.split(data, "\n")
246-
{complete_lines, [incomplete_line]} = Enum.split(lines, -1)
247-
{complete_lines, incomplete_line}
248-
end,
249244
fn
250-
"" -> {[], ""}
251-
buffer -> {[buffer], ""}
252-
end
245+
:end_of_stream, "" ->
246+
{[], ""}
247+
248+
:end_of_stream, buffer ->
249+
{[buffer], ""}
250+
251+
chunk, buffer ->
252+
data = buffer <> chunk
253+
lines = String.split(data, "\n")
254+
{complete_lines, [incomplete_line]} = Enum.split(lines, -1)
255+
{complete_lines, incomplete_line}
256+
end,
257+
fn _ -> :ok end
253258
)
254259
end
255260

test/common_crawl_mock_test.exs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,77 @@ defmodule CommonCrawlMockTest do
269269
end
270270
end
271271

272+
test "stream/2 configures Req retry options correctly" do
273+
cluster_idx_content = "key\tcdx-00000.gz\t100\n"
274+
275+
Req
276+
|> Mimic.expect(:get, 1, fn _url, opts ->
277+
assert opts[:max_retries] == 2
278+
assert is_function(opts[:retry_delay], 1)
279+
280+
retry_delay = opts[:retry_delay]
281+
282+
delay_0 = retry_delay.(0)
283+
assert is_integer(delay_0) and delay_0 >= 1 and delay_0 <= 2000
284+
285+
delay_1 = retry_delay.(1)
286+
assert is_integer(delay_1) and delay_1 >= 1 and delay_1 <= 4000
287+
288+
delay_2 = retry_delay.(2)
289+
assert is_integer(delay_2) and delay_2 >= 1 and delay_2 <= 8000
290+
291+
delay_5 = retry_delay.(5)
292+
assert is_integer(delay_5) and delay_5 >= 1 and delay_5 <= 30000
293+
294+
{:ok, %Req.Response{status: 200, body: cluster_idx_content}}
295+
end)
296+
|> Mimic.expect(:get, 1, fn _url, _opts ->
297+
{:ok, %Req.Response{status: 200, body: []}}
298+
end)
299+
300+
assert [] = Index.stream("CC-MAIN-2024-51") |> Stream.take(1) |> Enum.to_list()
301+
end
302+
303+
test "stream/2 raises on index partition stream failure" do
304+
cluster_idx_content = "key\tcdx-00000.gz\t100\n"
305+
306+
Req
307+
|> Mimic.expect(:get, 1, fn _url, _opts ->
308+
{:ok, %Req.Response{status: 200, body: cluster_idx_content}}
309+
end)
310+
|> Mimic.expect(:get, 1, fn _url, _opts ->
311+
{:error, :timeout}
312+
end)
313+
314+
assert_raise RuntimeError, ~r/Failed to stream index partition cdx-00000.gz/, fn ->
315+
Index.stream("CC-MAIN-2024-51") |> Enum.to_list()
316+
end
317+
end
318+
319+
test "stream/2 handles index partition data without trailing newline" do
320+
cluster_idx_content = "key\tcdx-00000.gz\t100\n"
321+
index_line = "com,example)/ 20240108123456 {\"url\": \"http://www.example.com\"}"
322+
gzipped_index = :zlib.gzip(index_line)
323+
324+
Req
325+
|> Mimic.expect(:get, 1, fn _url, _opts ->
326+
{:ok, %Req.Response{status: 200, body: cluster_idx_content}}
327+
end)
328+
|> Mimic.expect(:get, 1, fn _url, _opts ->
329+
{:ok, %Req.Response{status: 200, body: gzipped_index}}
330+
end)
331+
332+
tmp_dir = "test/support/tmp_stream_no_nl"
333+
File.mkdir_p!(tmp_dir)
334+
335+
try do
336+
result = Index.stream("CC-MAIN-2024-51", dir: tmp_dir) |> Enum.to_list()
337+
assert [{"com,example)/", 20240108123456, %{"url" => "http://www.example.com"}}] == result
338+
after
339+
File.rm_rf!(tmp_dir)
340+
end
341+
end
342+
272343
test "stream/2 filters out parsing errors" do
273344
cluster_idx_content = "key\tcdx-00000.gz\t100\n"
274345
invalid_index_line = "invalid line here\n"

0 commit comments

Comments
 (0)