Skip to content

Commit cb24caf

Browse files
authored
Move envelope decoding to test helpers (#672)
* Move from_binary/1 to test helpers, all tests pass * Finish up * Dialyzer
1 parent a8e67d8 commit cb24caf

File tree

5 files changed

+48
-244
lines changed

5 files changed

+48
-244
lines changed

lib/sentry/envelope.ex

Lines changed: 0 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -56,87 +56,4 @@ defmodule Sentry.Envelope do
5656
error
5757
end
5858
end
59-
60-
@doc """
61-
Decodes the envelope from its binary representation.
62-
"""
63-
@spec from_binary(String.t()) :: {:ok, t()} | {:error, :invalid_envelope}
64-
def from_binary(binary) when is_binary(binary) do
65-
json_library = Config.json_library()
66-
67-
[raw_headers | raw_items] = String.split(binary, "\n")
68-
69-
with {:ok, headers} <- json_library.decode(raw_headers),
70-
{:ok, items} <- decode_items(raw_items, json_library) do
71-
{:ok,
72-
%__MODULE__{
73-
event_id: headers["event_id"] || nil,
74-
items: items
75-
}}
76-
else
77-
{:error, _json_error} -> {:error, :invalid_envelope}
78-
end
79-
end
80-
81-
#
82-
# Decoding
83-
#
84-
85-
# Steps over the item pairs in the envelope body. The item header is decoded
86-
# first so it can be used to decode the item following it.
87-
defp decode_items(raw_items, json_library) do
88-
items =
89-
raw_items
90-
|> Enum.chunk_every(2, 2, :discard)
91-
|> Enum.map(fn [k, v] ->
92-
with {:ok, item_header} <- json_library.decode(k),
93-
{:ok, item} <- decode_item(item_header, v, json_library) do
94-
item
95-
else
96-
{:error, _reason} = error -> throw(error)
97-
end
98-
end)
99-
100-
{:ok, items}
101-
catch
102-
{:error, reason} -> {:error, reason}
103-
end
104-
105-
defp decode_item(%{"type" => "event"}, data, json_library) do
106-
result = json_library.decode(data)
107-
108-
case result do
109-
{:ok, fields} ->
110-
{:ok,
111-
%Event{
112-
breadcrumbs: fields["breadcrumbs"],
113-
culprit: fields["culprit"],
114-
environment: fields["environment"],
115-
event_id: fields["event_id"],
116-
source: fields["event_source"],
117-
exception: List.wrap(fields["exception"]),
118-
extra: fields["extra"],
119-
fingerprint: fields["fingerprint"],
120-
level: fields["level"],
121-
message: fields["message"],
122-
modules: fields["modules"],
123-
original_exception: fields["original_exception"],
124-
platform: fields["platform"],
125-
release: fields["release"],
126-
request: fields["request"],
127-
server_name: fields["server_name"],
128-
tags: fields["tags"],
129-
timestamp: fields["timestamp"],
130-
user: fields["user"]
131-
}}
132-
133-
{:error, e} ->
134-
{:error, "Failed to decode event item: #{e}"}
135-
end
136-
end
137-
138-
defp decode_item(%{"type" => type}, _data, _json_library),
139-
do: {:error, "unexpected item type '#{type}'"}
140-
141-
defp decode_item(_, _data, _json_library), do: {:error, "Missing item type header"}
14259
end

test/envelope_test.exs

Lines changed: 1 addition & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -3,134 +3,7 @@ defmodule Sentry.EnvelopeTest do
33

44
import Sentry.TestHelpers
55

6-
alias Sentry.{Envelope, Event, Interfaces}
7-
8-
describe "from_binary/1" do
9-
test "parses envelope with empty headers" do
10-
raw = "{}\n"
11-
12-
{:ok, envelope} = Envelope.from_binary(raw)
13-
14-
assert envelope.event_id == nil
15-
assert envelope.items == []
16-
end
17-
18-
test "parses envelope with only headers" do
19-
raw = "{\"event_id\":\"12c2d058d58442709aa2eca08bf20986\"}\n"
20-
21-
{:ok, envelope} = Envelope.from_binary(raw)
22-
23-
assert envelope.event_id == "12c2d058d58442709aa2eca08bf20986"
24-
assert envelope.items == []
25-
end
26-
27-
test "parses envelope containing an event" do
28-
event = %Event{
29-
breadcrumbs: [],
30-
environment: "test",
31-
event_id: "1d208b37d9904203918a9c2125ea91fa",
32-
source: nil,
33-
exception: [],
34-
extra: %{},
35-
fingerprint: ["{{ default }}"],
36-
level: "error",
37-
message: "hello",
38-
modules: %{
39-
bypass: "2.1.0",
40-
certifi: "2.6.1",
41-
cowboy: "2.8.0",
42-
cowboy_telemetry: "0.3.1",
43-
cowlib: "2.9.1",
44-
dialyxir: "1.1.0",
45-
erlex: "0.2.6",
46-
hackney: "1.17.4",
47-
idna: "6.1.1",
48-
jason: "1.2.2",
49-
metrics: "1.0.1",
50-
mime: "1.5.0",
51-
mimerl: "1.2.0",
52-
parse_trans: "3.3.1",
53-
phoenix: "1.5.8",
54-
phoenix_html: "2.14.3",
55-
phoenix_pubsub: "2.0.0",
56-
plug: "1.11.1",
57-
plug_cowboy: "2.4.1",
58-
plug_crypto: "1.2.2",
59-
ranch: "1.7.1",
60-
ssl_verify_fun: "1.1.6",
61-
telemetry: "0.4.2",
62-
unicode_util_compat: "0.7.0"
63-
},
64-
original_exception: nil,
65-
platform: :elixir,
66-
release: nil,
67-
request: %Interfaces.Request{},
68-
server_name: "john-linux",
69-
tags: %{},
70-
timestamp: "2021-10-09T03:53:22",
71-
user: %{}
72-
}
73-
74-
{:ok, raw_envelope} =
75-
[event]
76-
|> Envelope.new()
77-
|> Envelope.to_binary()
78-
79-
{:ok, envelope} = Envelope.from_binary(raw_envelope)
80-
81-
assert envelope.event_id == event.event_id
82-
83-
assert envelope.items == [
84-
%Event{
85-
breadcrumbs: [],
86-
environment: "test",
87-
event_id: "1d208b37d9904203918a9c2125ea91fa",
88-
exception: [],
89-
extra: %{},
90-
fingerprint: ["{{ default }}"],
91-
level: "error",
92-
message: "hello",
93-
modules: %{
94-
"bypass" => "2.1.0",
95-
"certifi" => "2.6.1",
96-
"cowboy" => "2.8.0",
97-
"cowboy_telemetry" => "0.3.1",
98-
"cowlib" => "2.9.1",
99-
"dialyxir" => "1.1.0",
100-
"erlex" => "0.2.6",
101-
"hackney" => "1.17.4",
102-
"idna" => "6.1.1",
103-
"jason" => "1.2.2",
104-
"metrics" => "1.0.1",
105-
"mime" => "1.5.0",
106-
"mimerl" => "1.2.0",
107-
"parse_trans" => "3.3.1",
108-
"phoenix" => "1.5.8",
109-
"phoenix_html" => "2.14.3",
110-
"phoenix_pubsub" => "2.0.0",
111-
"plug" => "1.11.1",
112-
"plug_cowboy" => "2.4.1",
113-
"plug_crypto" => "1.2.2",
114-
"ranch" => "1.7.1",
115-
"ssl_verify_fun" => "1.1.6",
116-
"telemetry" => "0.4.2",
117-
"unicode_util_compat" => "0.7.0"
118-
},
119-
platform: "elixir",
120-
release: nil,
121-
request: %{},
122-
server_name: "john-linux",
123-
tags: %{},
124-
timestamp: "2021-10-09T03:53:22",
125-
user: %{}
126-
}
127-
]
128-
end
129-
130-
test "returns an error if headers are missing" do
131-
assert {:error, :invalid_envelope} = Envelope.from_binary("no newlines here")
132-
end
133-
end
6+
alias Sentry.{Envelope, Event}
1347

1358
describe "to_binary/1" do
1369
test "encodes an envelope" do

test/plug_capture_test.exs

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,10 @@ defmodule Sentry.PlugCaptureTest do
4747

4848
event = decode_event_from_envelope!(body)
4949

50-
assert event.request["url"] == "http://www.example.com/error_route"
51-
assert event.request["method"] == "GET"
52-
assert event.request["query_string"] == ""
53-
assert event.request["data"] == %{}
50+
assert event["request"]["url"] == "http://www.example.com/error_route"
51+
assert event["request"]["method"] == "GET"
52+
assert event["request"]["query_string"] == ""
53+
assert event["request"]["data"] == %{}
5454
Plug.Conn.resp(conn, 200, ~s<{"id": "340"}>)
5555
end)
5656

@@ -63,9 +63,7 @@ defmodule Sentry.PlugCaptureTest do
6363
test "sends throws to Sentry", %{bypass: bypass} do
6464
Bypass.expect(bypass, fn conn ->
6565
{:ok, body, conn} = Plug.Conn.read_body(conn)
66-
6766
_event = decode_event_from_envelope!(body)
68-
6967
Plug.Conn.resp(conn, 200, ~s<{"id": "340"}>)
7068
end)
7169

@@ -75,9 +73,7 @@ defmodule Sentry.PlugCaptureTest do
7573
test "sends exits to Sentry", %{bypass: bypass} do
7674
Bypass.expect(bypass, fn conn ->
7775
{:ok, body, conn} = Plug.Conn.read_body(conn)
78-
7976
_event = decode_event_from_envelope!(body)
80-
8177
Plug.Conn.resp(conn, 200, ~s<{"id": "340"}>)
8278
end)
8379

@@ -94,9 +90,7 @@ defmodule Sentry.PlugCaptureTest do
9490
test "can render feedback form", %{bypass: bypass} do
9591
Bypass.expect(bypass, fn conn ->
9692
{:ok, body, conn} = Plug.Conn.read_body(conn)
97-
9893
_event = decode_event_from_envelope!(body)
99-
10094
Plug.Conn.resp(conn, 200, ~s<{"id": "340"}>)
10195
end)
10296

@@ -135,10 +129,10 @@ defmodule Sentry.PlugCaptureTest do
135129

136130
event = decode_event_from_envelope!(body)
137131

138-
assert event.culprit == "Sentry.PlugCaptureTest.PhoenixController.error/2"
132+
assert event["culprit"] == "Sentry.PlugCaptureTest.PhoenixController.error/2"
139133

140-
assert List.first(event.exception)["type"] == "RuntimeError"
141-
assert List.first(event.exception)["value"] == "PhoenixError"
134+
assert List.first(event["exception"])["type"] == "RuntimeError"
135+
assert List.first(event["exception"])["value"] == "PhoenixError"
142136

143137
Plug.Conn.resp(conn, 200, ~s<{"id": "340"}>)
144138
end)
@@ -155,8 +149,8 @@ defmodule Sentry.PlugCaptureTest do
155149

156150
event = decode_event_from_envelope!(body)
157151

158-
assert event.culprit == "Sentry.PlugCaptureTest.PhoenixController.exit/2"
159-
assert event.message == "Uncaught exit - :test"
152+
assert event["culprit"] == "Sentry.PlugCaptureTest.PhoenixController.exit/2"
153+
assert event["message"] == "Uncaught exit - :test"
160154
Plug.Conn.resp(conn, 200, ~s<{"id": "340"}>)
161155
end)
162156

@@ -169,8 +163,8 @@ defmodule Sentry.PlugCaptureTest do
169163

170164
event = decode_event_from_envelope!(body)
171165

172-
assert event.culprit == "Sentry.PlugCaptureTest.PhoenixController.throw/2"
173-
assert event.message == "Uncaught throw - :test"
166+
assert event["culprit"] == "Sentry.PlugCaptureTest.PhoenixController.throw/2"
167+
assert event["message"] == "Uncaught throw - :test"
174168
Plug.Conn.resp(conn, 200, ~s<{"id": "340"}>)
175169
end)
176170

@@ -203,8 +197,8 @@ defmodule Sentry.PlugCaptureTest do
203197
assert_receive {^ref, sentry_body}
204198
event = decode_event_from_envelope!(sentry_body)
205199

206-
assert event.culprit == "Sentry.PlugCaptureTest.PhoenixController.action_clause_error/2"
207-
assert [exception] = event.exception
200+
assert event["culprit"] == "Sentry.PlugCaptureTest.PhoenixController.action_clause_error/2"
201+
assert [exception] = event["exception"]
208202
assert exception["type"] == "Phoenix.ActionClauseError"
209203
assert exception["value"] =~ ~s(params: %{"password" => "*********"})
210204
end
@@ -235,7 +229,7 @@ defmodule Sentry.PlugCaptureTest do
235229
Bypass.expect(bypass, fn conn ->
236230
{:ok, body, conn} = Plug.Conn.read_body(conn)
237231
event = decode_event_from_envelope!(body)
238-
assert event.culprit == "Sentry.PlugCaptureTest.PhoenixController.assigns/2"
232+
assert event["culprit"] == "Sentry.PlugCaptureTest.PhoenixController.assigns/2"
239233
Plug.Conn.resp(conn, 200, ~s<{"id": "340"}>)
240234
end)
241235

@@ -250,4 +244,9 @@ defmodule Sentry.PlugCaptureTest do
250244
defp call_plug_app(conn), do: Plug.run(conn, [{Sentry.ExamplePlugApplication, []}])
251245

252246
defp call_phoenix_endpoint(conn), do: Plug.run(conn, [{PhoenixEndpoint, []}])
247+
248+
defp decode_event_from_envelope!(envelope) do
249+
assert [{%{"type" => "event"}, event}] = decode_envelope!(envelope)
250+
event
251+
end
253252
end

test/sentry/client_test.exs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,10 @@ defmodule Sentry.ClientTest do
8585
Bypass.expect(bypass, fn conn ->
8686
assert {:ok, body, conn} = Plug.Conn.read_body(conn)
8787

88-
event = decode_event_from_envelope!(body)
88+
assert [{%{"type" => "event"}, event}] = decode_envelope!(body)
8989

90-
assert event.extra == %{"key" => "value"}
91-
assert event.user["id"] == 1
90+
assert event["extra"] == %{"key" => "value"}
91+
assert event["user"]["id"] == 1
9292

9393
Plug.Conn.resp(conn, 200, ~s<{"id": "340"}>)
9494
end)
@@ -224,15 +224,15 @@ defmodule Sentry.ClientTest do
224224
event =
225225
fn ->
226226
assert_receive {^ref, body}, 1000
227-
decode_event_from_envelope!(body)
227+
assert [{%{"type" => "event"}, event}] = decode_envelope!(body)
228+
event
228229
end
229230
|> Stream.repeatedly()
230231
|> Stream.reject(&is_nil/1)
231232
|> Stream.take(10)
232233
|> Enum.at(0)
233234

234-
assert %Event{} = event
235-
assert event.message == "Something went wrong"
235+
assert event["message"] == "Something went wrong"
236236
end
237237

238238
test "dedupes events", %{bypass: bypass} do

0 commit comments

Comments
 (0)