Created
July 11, 2025 16:10
-
-
Save pachun/b2cff09e6f15ae72f9421f37a0e27446 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule EmmaApi.ExpectDSL do | |
defmacro expect({op, _meta, _args} = assertion_ast) when op in [:==, :=~, :>, :<, :<=, :>=] do | |
quote do | |
assert unquote(assertion_ast) | |
end | |
end | |
defmacro expect(module) do | |
quote do: unquote(module) | |
end | |
defmacro to_have_received(module, function, args \\ []) do | |
call_ast = {{:., [], [module, function]}, [], args} | |
quote do | |
require Patch | |
Patch.assert_called(unquote(call_ast)) | |
end | |
end | |
end | |
defmodule EmmaApi.AllowDSL do | |
defstruct [:module, :function, :arity] | |
def allow(module), do: %__MODULE__{module: module} | |
def to_receive(%__MODULE__{module: module} = dsl, function, arity) do | |
args = for _ <- 1..arity, do: Macro.var(:_, nil) | |
fun = | |
quote do | |
fn unquote_splicing(args) -> :stubbed end | |
end | |
Code.eval_quoted( | |
quote do | |
Patch.patch(unquote(module), unquote(function), unquote(fun)) | |
end | |
) | |
%__MODULE__{dsl | function: function, arity: arity} | |
end | |
def and_respond( | |
%__MODULE__{module: module, function: function}, | |
%Patch.Mock.Values.Sequence{} = sequence | |
) do | |
Patch.patch(module, function, sequence) | |
end | |
def and_respond( | |
%__MODULE__{module: module, function: function, arity: expected_arity}, | |
response_fn | |
) | |
when is_function(response_fn) do | |
actual_arity = | |
case :erlang.fun_info(response_fn, :arity) do | |
{:arity, arity} -> arity | |
_ -> raise ArgumentError, "Could not determine function arity" | |
end | |
if actual_arity != expected_arity do | |
raise ArgumentError, | |
"Function given to `and_respond` has arity #{actual_arity}, but expected arity #{expected_arity}" | |
end | |
Patch.patch(module, function, response_fn) | |
end | |
end | |
defmodule EmmaApi.Aliases do | |
defmacro __using__(_opts) do | |
quote do | |
alias EmmaApi.{ | |
GmailAccount, | |
Gmail.SendNewInboxEmailsPushNotifications, | |
Factory | |
} | |
alias EmmaApiWeb.GoogleJWTTokenValidator | |
end | |
end | |
end | |
defmodule EmmaApi.TestDSL do | |
defmacro __using__(_opts) do | |
quote do | |
use Patch | |
import EmmaApi.AllowDSL | |
import EmmaApi.ExpectDSL | |
use EmmaApi.Aliases | |
end | |
end | |
end | |
defmodule EmmaApiWeb.Gmail.WebhookControllerTest do | |
use EmmaApiWeb.ConnCase | |
use EmmaApi.TestDSL | |
test "sends new inbox email push notifications and returns an ok status", %{conn: conn} do | |
%{id: id, gmail_address: gmail_address} = Factory.insert(:gmail_account) | |
allow(SendNewInboxEmailsPushNotifications) | |
|> to_receive(:send, 2) | |
allow(GoogleJWTTokenValidator) | |
|> to_receive(:verify_and_validate, 1) | |
|> and_respond( | |
sequence([ | |
fn "valid_token" -> {:ok, %{}} end, | |
fn "valid_token" -> {:error, "BAD"} end | |
]) | |
) | |
response = | |
send_post(conn, "valid_token", %{ | |
emailAddress: gmail_address, | |
historyId: "new_history_id" | |
}) | |
expect(json_response(response, 200) == %{}) | |
expect(EmmaApi.Gmail.SendNewInboxEmailsPushNotifications) | |
|> to_have_received(:send, [%GmailAccount{id: ^id}, "new_history_id"]) | |
end | |
defp send_post(conn, jwt_token, body) do | |
conn | |
|> put_req_header("authorization", "Bearer " <> jwt_token) | |
|> post( | |
"/api/gmail_webhook", | |
%{ | |
"message" => %{ | |
"data" => Base.encode64(Jason.encode!(body)) | |
} | |
} | |
) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment