Daniel Morandini
Software Developer @KIM
mocks and tdd, 09 Nov 2021
HTTP Live Streaming (also known as HLS) is an HTTP-based adaptive bitrate streaming communications protocol developed by Apple Inc. and released in 2009.
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-STREAM-INF:BANDWIDTH=299147,AVERAGE-BANDWIDTH=290400,CODECS="avc1.66.30,mp4a.40.2",RESOLUTION=416x234,FRAME-RATE=14.985,AUDIO="PROGRAM_AUDIO"
stream_416x234_200k.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=1020588,AVERAGE-BANDWIDTH=985600,CODECS="avc1.77.30,mp4a.40.2",RESOLUTION=640x360,FRAME-RATE=29.970,AUDIO="PROGRAM_AUDIO"
stream_640x360_800k.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=1478083,AVERAGE-BANDWIDTH=1425600,CODECS="avc1.4d4029,mp4a.40.2",RESOLUTION=854x480,FRAME-RATE=29.970,AUDIO="PROGRAM_AUDIO"
stream_854x480_1200k.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=3915128,AVERAGE-BANDWIDTH=3770800,CODECS="avc1.640029,mp4a.40.2",RESOLUTION=1280x720,FRAME-RATE=29.970,AUDIO="PROGRAM_AUDIO"
stream_1280x720_3300k.m3u8
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:7
#EXT-X-MEDIA-SEQUENCE:1
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-PROGRAM-DATE-TIME:2021-06-10T06:13:16.934Z
#EXTINF:6.00600,
b/stream_1280x720_3300k/00000/stream_1280x720_3300k_00002.ts
#EXTINF:6.00600,
b/stream_1280x720_3300k/00000/stream_1280x720_3300k_00003.ts
#EXTINF:6.00600,
b/stream_1280x720_3300k/00000/stream_1280x720_3300k_00004.ts
#EXTINF:6.00600,
#EXT-X-ENDLIST
master.m3u8
child.m3u8
commit 521ccb898b55591a498154d4a68c79695446fdeb (HEAD -> master, origin/master)
Author: Philip Giuliani
Date: Mon Nov 8 15:25:05 2021 +0100
Use conn.assigns.host
commit c86ed1baf5b594b5dfe77d641f9c83d9e9631f1d
Author: Awlex
Date: Mon Nov 8 11:50:03 2021 +0100
unflakify test [closes #20]
...
commit 7a7f5e7fa6a372cf1df3558bdd8a38ba5d7b6f2e
Author: Daniel Morandini
Date: Mon Sep 27 10:20:41 2021 +0200
Test responses when a token is provided
commit eb3414441c492c421f4e02e180d6313dac9e66df
Author: Daniel Morandini
Date: Thu Sep 23 18:52:11 2021 +0200
Add mocked master playlist fetching
commit b888fd2e9e068c55cf9718a5298d47651af79f0d
Author: Daniel Morandini
Date: Thu Sep 23 17:11:58 2021 +0200
Add health_check and token generation
commit b7cbc609c95c2f268df1f91ed8d114967213eb80
Author: Daniel Morandini
Date: Thu Sep 23 11:38:26 2021 +0200
Initialize repository
CDP protects, manipulates and caches video.taxi's resources
test "does know about 404s" do
conn = conn(:get, "/this+does_not_exist")
conn = CDP.Router.call(conn, opts())
assert conn.state == :sent
assert conn.status == 404
end
describe "/health_check" do
test "is healthy" do
conn = conn(:get, "/health_check")
conn = CDP.Router.call(conn, opts())
assert conn.state == :sent
assert conn.status == 200
assert conn.resp_body == "OK"
end
end
cdp.ex
you don't want to use it while doing automated tests!
defmodule CDP.File.Store do
@type get_func :: (CDP.File.Request.t() -> {:ok, binary} | {:error, binary} | {:error, :not_found})
@spec fetch(get_func, CDP.File.Request.t()) :: {:ok, binary} | {:error, :not_found} | {:error, binary}
def fetch(driver, req) do
case driver.(req) do
{:error, reason} when is_binary(reason) -> {:error, reason}
{:error, :not_found} -> {:error, :not_found}
{:ok, data} -> {:ok, data}
end
end
end
NOTE: driver is the function resposible for actually fetching the content!
lib/cdp/file/store.ex
def fetch_playlist(conn, opts, playlist) do
func = Keyword.fetch!(opts, :storage_get_func)
playlist =
conn.path_info
|> List.insert_at(1, conn.assigns.meta.pipeline)
|> CDP.M3U8.replace_last(playlist)
request = %CDP.File.Request{
host: conn.assigns.host,
path_info: playlist
}
CDP.File.Store.fetch(func, request)
end
NOTE: storage_get_function is configurable!
def init(override \\ []) do
std = [storage_get_func: &CDP.File.S3.get/1]
Keyword.merge(std, override)
end
lib/cdp/m3u8/master.ex
defmodule Support.File.Store do
def get(%CDP.File.Request{host: host, path_info: path}) do
user = get_user(host)
[user | path]
|> Path.join()
|> get_path()
end
def get_path(path) do
resp =
(["test", "mocks"] ++ [path])
|> Path.join()
|> File.read()
case resp do
{:ok, file} -> {:ok, file}
{:error, :enoent} -> {:error, :not_found}
end
end
defp get_user(host) do
[h | _] = String.split(host, ".")
h
end
end
test/support/storage.ex
NOTE: full mock implementation!
test "returns the playlist for events w/o <meta>.json file" do
name = "master-nometa.m3u8"
{:ok, playlist} = Store.get_path("user_id/event_id/a/#{name}")
conn = conn(:get, "http://user_id.cdn.video.taxi/event_id/#{name}")
conn = CDP.Router.call(conn, [storage_get_func: &Support.File.Store.get/1])
assert conn.state == :sent
assert conn.status == 200
assert clean_master(conn.resp_body) == playlist
end
test/cdp/router_test.exs
NOTE: mocked storage function is provided!
By Daniel Morandini