Skip to content

Commit

Permalink
Gateway tests (#333)
Browse files Browse the repository at this point in the history
* General fixes to Champions.Units

* Add doc to unselect_unit.ex

* Refactor units

* Create GameBackend.Transaction

* Remove Jason.Encoder derive

* Add CurrencyCost

* Add Currency operations

* Add game_id util

* Set slots range to 1..6

* Return ok tuple in register user

* Update unit.ex

* Add add_level to GameBackend.Units

* Implement Champions unit level up

- Also create CurrencyCost

* Run format after protos

* Add gateway commands for new functionalities

* Format Champions.Units

* Add doc

* Add ChampionsSocketHandler handlers

* Update SocketTester

* Fix rarity type

* Rename unit_level to level and level assoc to campaign_level

* Revert "Rename unit_level to level and level assoc to campaign_level"

This reverts commit 062905c.

* Rename unit_level to level and level assoc to campaign_level

* Rename update_selected

* Fix selection

* Format

* Add rank to unit

* Add add_tier to GameBackend.Units

* Add tier_up to Champions.Units

* Fix item doc

* Fix units calculate_tier_up_cost

* Fix doc

Co-authored-by: Agustin Escobar <[email protected]>

* Update substract_currencies doc

* Add example

* Update substract_currencies docs

* Remove empty line

* Allow slot 6

* Fix seeds

* Add messages

* Add fusion

* Move rank to the back of the cast list, do not require tier

* Fix SocketTester

* Add alias

* Add messages

* Fix seeds

* Add get_units_by_ids

* Default to 0 in get_amount_of_currency

* Add useful currencies functions

* Add tests

* Add doc

* Restore delete_units

* Update apps/game_backend/lib/game_backend/units.ex

Co-authored-by: Agustin Escobar <[email protected]>

* Check that target unit is not in consumed list

* Add rank to add_sample_units

* Refactor get_amount_of_currency_by_name

* Refactor validate_units

* Remove unnecesary nested function

* Changed inconclusive comment to TODO with issue number

* Rename validate_consumed_units to meets_fuse_requirements?

* Fix tests

* Format tests

* Check unselected_unit id

* Refactor create_units_to_consume

* Add username validation

* Remove unnecesary calls and add an assertion

* Format

* User username variable instead of string

* Add more assertions

* Comment unused code

* Fix seeds

---------

Co-authored-by: Agustin Escobar <[email protected]>
  • Loading branch information
lotuuu and agustinesco authored Mar 11, 2024
1 parent 0ce32db commit 1d6b38a
Show file tree
Hide file tree
Showing 6 changed files with 270 additions and 16 deletions.
2 changes: 1 addition & 1 deletion apps/game_backend/lib/game_backend/units.ex
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ defmodule GameBackend.Units do
def delete_unit(id), do: Repo.get(Unit, id) |> delete_unit()

@doc """
Deletes all units in a given list.
Deletes all units in a list by ids.
"""
def delete_units(unit_ids), do: Repo.delete_all(from(u in Unit, where: u.id in ^unit_ids))

Expand Down
266 changes: 266 additions & 0 deletions apps/gateway/test/champions_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
defmodule Gateway.Test.Champions do
@moduledoc """
Test for Champions of Mirra messages.
"""
use ExUnit.Case

alias Champions.{Units, Users, Utils}
alias GameBackend.Repo
alias GameBackend.Users.Currencies.CurrencyCost
alias GameBackend.Users.Currencies

alias Gateway.Serialization.{
Currency,
Error,
Unit,
UnitAndCurrencies,
User,
UserCurrency,
WebSocketResponse
}

alias Gateway.SocketTester

setup_all do
# Start Phoenix endpoint
{:ok, _} =
Plug.Cowboy.http(Gateway.Endpoint, [],
ip: {127, 0, 0, 1},
port: 4001,
dispatch: [
_: [{"/2", Gateway.ChampionsSocketHandler, []}]
]
)

:ok
end

setup do
{:ok, socket_tester} = SocketTester.start_link()

{:ok, %{socket_tester: socket_tester}}
end

describe "users" do
test "users", %{socket_tester: socket_tester} do
username = "Username"

# Create our user
:ok = SocketTester.create_user(socket_tester, username)
fetch_last_message(socket_tester)
%WebSocketResponse{response_type: {:user, %User{} = user}} = get_last_message()

assert user.username == username

# Creating another user with the same name fails
:ok = SocketTester.create_user(socket_tester, username)
fetch_last_message(socket_tester)
assert_receive %WebSocketResponse{response_type: {:error, %Error{reason: "username_taken"}}}

# Get user by name
:ok = SocketTester.get_user_by_username(socket_tester, username)
fetch_last_message(socket_tester)
assert_receive %WebSocketResponse{response_type: {:user, ^user}}
end
end

describe "units" do
test "unit selection", %{socket_tester: socket_tester} do
{:ok, user} = Users.register("SelectUser")

[unit_to_unselect | _] = user.units
slot = unit_to_unselect.slot

# Unit is selected by default (this will change when we remove sample data in user creation)
assert unit_to_unselect.selected

# Unselect the unit
:ok = SocketTester.unselect_unit(socket_tester, user.id, unit_to_unselect.id)
fetch_last_message(socket_tester)
%WebSocketResponse{response_type: {:unit, %Unit{} = unselected_unit}} = get_last_message()

assert not unselected_unit.selected
# Protobuf doesn't support nil values, returns zero instead
assert unselected_unit.slot == 0
assert unselected_unit.id == unit_to_unselect.id

:ok = SocketTester.select_unit(socket_tester, user.id, unselected_unit.id, slot)
fetch_last_message(socket_tester)
%WebSocketResponse{response_type: {:unit, %Unit{} = selected_unit}} = get_last_message()

assert selected_unit.selected
assert selected_unit.slot == slot
end

test "unit progression", %{socket_tester: socket_tester} do
muflus = GameBackend.Units.Characters.get_character_by_name("Muflus")
{:ok, user} = Users.register("LevelUpUser")
Currencies.add_currency_by_name!(user.id, "Gold", 9999)

{:ok, unit} =
GameBackend.Units.insert_unit(%{
user_id: user.id,
level: 19,
tier: 1,
rank: 2,
selected: false,
character_id: muflus.id
})

gold = Currencies.get_amount_of_currency_by_name(user.id, "Gold")
level = unit.level

#### LevelUpUnit
[%CurrencyCost{currency_id: _gold_id, amount: level_up_cost}] = Units.calculate_level_up_cost(unit)

:ok = SocketTester.level_up_unit(socket_tester, user.id, unit.id)
fetch_last_message(socket_tester)

%WebSocketResponse{
response_type: {:unit_and_currencies, %UnitAndCurrencies{unit: unit, user_currency: [user_currency]}}
} = get_last_message()

assert unit.level == level + 1
assert user_currency.currency.name == "Gold"
assert user_currency.amount == gold - level_up_cost

# Cannot level up because unit is level 20 with tier 1
assert unit.level == 20
assert unit.tier == 1
:ok = SocketTester.level_up_unit(socket_tester, user.id, unit.id)
fetch_last_message(socket_tester)
assert_receive %WebSocketResponse{response_type: {:error, %Error{reason: "cant_level_up"}}}

#### TierUpUnit

user_gold = Currencies.get_amount_of_currency_by_name(user.id, "Gold")
user_gems = Currencies.get_amount_of_currency_by_name(user.id, "Gems")

[%CurrencyCost{currency_id: _gold_id, amount: gold_cost}, %CurrencyCost{currency_id: _gems_id, amount: gems_cost}] =
Units.calculate_tier_up_cost(unit)

:ok = SocketTester.tier_up_unit(socket_tester, user.id, unit.id)
fetch_last_message(socket_tester)

%WebSocketResponse{
response_type: {:unit_and_currencies, %UnitAndCurrencies{unit: unit, user_currency: user_currencies}}
} = get_last_message()

user_currencies =
Enum.into(user_currencies, %{}, fn %UserCurrency{
currency: %Currency{name: name},
amount: amount
} ->
{name, amount}
end)

assert unit.level == 20
assert unit.tier == 2
assert user_currencies["Gold"] == user_gold - gold_cost
assert user_currencies["Gems"] == user_gems - gems_cost

# Check that we can now LevelUpUnit
:ok = SocketTester.level_up_unit(socket_tester, user.id, unit.id)
fetch_last_message(socket_tester)

%WebSocketResponse{
response_type: {:unit_and_currencies, %UnitAndCurrencies{unit: unit}}
} = get_last_message()

assert unit.level == level + 2

#### Rank up (FuseUnit)

{:ok, unit} =
GameBackend.Units.insert_unit(%{
user_id: user.id,
level: 100,
tier: 5,
rank: Units.get_rank(:star5),
selected: false,
character_id: muflus.id
})

unit = Repo.preload(unit, [:character])

rank = unit.rank
user_units_count = user.id |> GameBackend.Units.get_units() |> Enum.count()

# Add to-be-consumed units

{:ok, same_faction_character} =
GameBackend.Units.Characters.insert_character(%{
game_id: Utils.game_id(),
active: true,
name: "SameFactionUnit",
faction: muflus.faction,
quality: Champions.Units.get_quality(:rare)
})

# We will need three 5* Muflus and two i2 of the same faction
# For the same faction, we will do one of each unit.
units_to_consume = create_units_to_consume(user, muflus, same_faction_character)

# Characters need a certain quality to rank up
assert unit.character.quality == Units.get_quality(:epic)

# Check that we cant TierUpUnit again without ranking up
:ok = SocketTester.tier_up_unit(socket_tester, user.id, unit.id)
fetch_last_message(socket_tester)

%WebSocketResponse{
response_type: {:error, %Error{reason: "cant_tier_up"}}
} = get_last_message()

# FuseUnit
:ok = SocketTester.fuse_unit(socket_tester, user.id, unit.id, units_to_consume)
fetch_last_message(socket_tester)

%WebSocketResponse{
response_type: {:unit, %Unit{} = unit}
} = get_last_message()

assert unit.rank == rank + 1
assert user_units_count == user.id |> GameBackend.Units.get_units() |> Enum.count()
end
end

defp get_last_message() do
receive do
message ->
message
after
5000 ->
raise "No message"
end
end

defp fetch_last_message(socket_tester) do
:timer.sleep(50)
send(socket_tester, {:last_message, self()})
end

defp create_units_to_consume(user, same_character, same_faction) do
params = %{
user_id: user.id,
level: 100,
tier: 5,
rank: Units.get_rank(:star5),
selected: false
}

same_character_ids =
Enum.map(1..3, fn _ ->
{:ok, unit} = params |> Map.put(:character_id, same_character.id) |> GameBackend.Units.insert_unit()
unit.id
end)

same_faction_ids =
Enum.map(1..2, fn _ ->
{:ok, unit} = params |> Map.put(:character_id, same_faction.id) |> GameBackend.Units.insert_unit()
unit.id
end)

same_character_ids ++ same_faction_ids
end
end
12 changes: 0 additions & 12 deletions apps/gateway/test/gateway_web/controllers/error_json_test.exs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule SocketTester do
defmodule Gateway.SocketTester do
@moduledoc """
Module for manually testing the CoM websocket.
Logs received messages and keeps the last one received in the state.
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ defmodule MirraBackend.MixProject do
setup: ["deps.get", "ecto.setup"],
"ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
"ecto.reset": ["ecto.drop", "ecto.setup"],
test: ["ecto.create --quiet", "ecto.migrate --quiet", "test"]
test: ["ecto.reset --quiet", "ecto.migrate --quiet", "run priv/repo/seeds.exs", "test"]
]
end

Expand Down
2 changes: 1 addition & 1 deletion priv/repo/seeds.exs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ Items.insert_item_template(%{

{:ok, gold_currency} = Users.Currencies.insert_currency(%{game_id: champions_of_mirra_id, name: "Gold"})
{:ok, _gems_currency} = Users.Currencies.insert_currency(%{game_id: champions_of_mirra_id, name: "Gems"})
{:ok, scrolls_currency} = Users.Currencies.insert_currency(%{game_id: champions_of_mirra_id, name: "Summon Scrolls"})
{:ok, _scrolls_currency} = Users.Currencies.insert_currency(%{game_id: champions_of_mirra_id, name: "Summon Scrolls"})

######################
# Campaigns creation #
Expand Down

0 comments on commit 1d6b38a

Please sign in to comment.