Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions copi.owasp.org/test/copi/cornucopia_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -222,4 +222,25 @@ defmodule Copi.CornucopiaTest do
assert %Ecto.Changeset{} = Cornucopia.change_card(card)
end
end
describe "schema changesets and find functions" do
alias Copi.Cornucopia.{DealtCard, Vote, Player}

test "DealtCard.changeset/2 returns a changeset" do
changeset = DealtCard.changeset(%DealtCard{}, %{})
assert changeset.valid?
end

test "DealtCard.find/1 returns error when not found" do
assert {:error, :not_found} = DealtCard.find(999_999_999)
end

test "Vote.changeset/2 returns a changeset" do
changeset = Vote.changeset(%Vote{}, %{})
assert changeset.valid?
end

test "Player.find/1 returns error when not found" do
assert {:error, :not_found} = Player.find("00000000000000000000000000")
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,51 @@ defmodule CopiWeb.CoreComponentsTest do
assert html =~ "Title"
assert html =~ "Item 1"
end
test "renders copy_url_button" do
assigns = %{uri: "http://example.com/game/123"}
html = rendered_to_string(~H"""
<CopiWeb.CoreComponents.Buttons.copy_url_button uri={@uri} />
""")
assert html =~ "copy-url-btn"
assert html =~ "http://example.com/game/123"
end

test "renders header component" do
assigns = %{}
html = rendered_to_string(~H"""
<CopiWeb.CoreComponents.Headers.header>
Page Title
<:subtitle>A subtitle</:subtitle>
</CopiWeb.CoreComponents.Headers.header>
""")
assert html =~ "Page Title"
assert html =~ "A subtitle"
end

test "renders header2 component" do
assigns = %{}
html = rendered_to_string(~H"""
<CopiWeb.CoreComponents.Headers.header2>Section Title</CopiWeb.CoreComponents.Headers.header2>
""")
assert html =~ "Section Title"
assert html =~ "<h2"
end

test "renders button component" do
assigns = %{}
html = rendered_to_string(~H"""
<CopiWeb.CoreComponents.Buttons.button>Click me</CopiWeb.CoreComponents.Buttons.button>
""")
assert html =~ "Click me"
assert html =~ "<button"
end

test "renders primary_button component" do
assigns = %{}
html = rendered_to_string(~H"""
<CopiWeb.CoreComponents.Buttons.primary_button>Submit</CopiWeb.CoreComponents.Buttons.primary_button>
""")
assert html =~ "Submit"
assert html =~ "bg-indigo-600"
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,24 @@ defmodule CopiWeb.ApiControllerTest do

assert json_response(conn, 403)["error"] == "Player already played a card in this round"
end

test "play_card fails if game not found", %{conn: conn, player: player, dealt_card: dealt_card} do
conn = put(conn, "/api/games/nonexistent-game-id/players/#{player.id}/card", %{
"game_id" => "nonexistent-game-id",
"player_id" => player.id,
"dealt_card_id" => to_string(dealt_card.id)
})

assert json_response(conn, 404)["error"] == "Could not find game"
end

test "play_card fails if player not in game", %{conn: conn, game: game, dealt_card: dealt_card} do
conn = put(conn, "/api/games/#{game.id}/players/nonexistent-player-id/card", %{
"game_id" => game.id,
"player_id" => "nonexistent-player-id",
"dealt_card_id" => to_string(dealt_card.id)
})

assert json_response(conn, 404)["error"] == "Could not find player and dealt card"
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,11 @@ defmodule CopiWeb.CardControllerTest do
end


describe "format_capec" do
test "returns refs unchanged" do
alias CopiWeb.CardController
assert CardController.format_capec([1, 2, 3]) == [1, 2, 3]
assert CardController.format_capec([]) == []
end
end
end
16 changes: 16 additions & 0 deletions copi.owasp.org/test/copi_web/live/game_live/show_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -128,5 +128,21 @@ defmodule CopiWeb.GameLive.ShowTest do
alias CopiWeb.GameLive.Show
assert Show.card_played_in_round([], 1) == nil
end

test "handle_info with non-matching topic is ignored", %{conn: conn, game: game} do
{:ok, show_live, _html} = live(conn, "/games/#{game.id}")

{:ok, other_game} = Cornucopia.create_game(%{name: "Other Game", edition: "webapp"})
{:ok, updated_other} = Cornucopia.Game.find(other_game.id)

send(show_live.pid, %{
topic: "game:#{other_game.id}",
event: "game:updated",
payload: updated_other
})

:timer.sleep(50)
assert render(show_live) =~ game.name
end
end
end
133 changes: 133 additions & 0 deletions copi.owasp.org/test/copi_web/live/player_live/show_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,30 @@ defmodule CopiWeb.PlayerLive.ShowTest do
# With no players, no one is still to play → round_open? is false → round_closed? is true
assert Show.round_closed?(%{players: [], rounds_played: 0}) == true

# last_round? returns false when a player still has a nil-round card
player_with_unplayed = %{dealt_cards: [%{played_in_round: nil}]}
refute Show.last_round?(%{players: [player_with_unplayed], rounds_played: 0})

# last_round? returns true when all cards are played
player_all_played = %{dealt_cards: [%{played_in_round: 1}]}
assert Show.last_round?(%{players: [player_all_played], rounds_played: 0})

# player_first places current player first
other = %{id: "other-id"}
current = %{id: "current-id"}
current_player = %{id: "current-id"}
sorted = Show.player_first([other, current], current_player)
assert List.first(sorted).id == "current-id"

# get_vote returns nil when no vote, returns vote when found
dealt_no_vote = %{votes: []}
fake_player = %{id: "player-id"}
assert Show.get_vote(dealt_no_vote, fake_player) == nil

vote = %{player_id: "player-id"}
dealt_with_vote = %{votes: [vote]}
assert Show.get_vote(dealt_with_vote, fake_player) == vote

assert Show.display_game_session("webapp") == "Cornucopia Web Session:"
assert Show.display_game_session("ecommerce") == "Cornucopia Web Session:"
assert Show.display_game_session("mobileapp") == "Cornucopia Mobile Session:"
Expand All @@ -119,5 +143,114 @@ defmodule CopiWeb.PlayerLive.ShowTest do
assert Show.display_game_session("mlsec") == "Elevation of MLSec Session:"
assert Show.display_game_session("eop") == "EoP Session:"
end

test "next_round when round is closed and not last round advances rounds_played",
%{conn: conn, player: player} do
game_id = player.game_id
{:ok, game} = Cornucopia.Game.find(game_id)

Copi.Repo.update!(
Ecto.Changeset.change(game, started_at: DateTime.truncate(DateTime.utc_now(), :second))
)

{:ok, card1} =
Cornucopia.create_card(%{
category: "C", value: "NR1", description: "D", edition: "webapp",
version: "2.2", external_id: "NR_CLOSED1", language: "en", misc: "m",
owasp_scp: [], owasp_devguide: [], owasp_asvs: [], owasp_appsensor: [],
capec: [], safecode: [], owasp_mastg: [], owasp_masvs: []
})

{:ok, card2} =
Cornucopia.create_card(%{
category: "C", value: "NR2", description: "D", edition: "webapp",
version: "2.2", external_id: "NR_CLOSED2", language: "en", misc: "m",
owasp_scp: [], owasp_devguide: [], owasp_asvs: [], owasp_appsensor: [],
capec: [], safecode: [], owasp_mastg: [], owasp_masvs: []
})

# Card played in round 1 → round_open? = false
Copi.Repo.insert!(%Copi.Cornucopia.DealtCard{
player_id: player.id, card_id: card1.id, played_in_round: 1
})
# Unplayed card → last_round? = false
Copi.Repo.insert!(%Copi.Cornucopia.DealtCard{
player_id: player.id, card_id: card2.id, played_in_round: nil
})

{:ok, show_live, _html} = live(conn, "/games/#{game_id}/players/#{player.id}")
render_click(show_live, "next_round", %{})
:timer.sleep(100)

{:ok, updated_game} = Cornucopia.Game.find(game_id)
assert updated_game.rounds_played == 1
assert updated_game.finished_at == nil
end

test "next_round when round is closed and IS last round sets finished_at",
%{conn: conn, player: player} do
game_id = player.game_id
{:ok, game} = Cornucopia.Game.find(game_id)

Copi.Repo.update!(
Ecto.Changeset.change(game, started_at: DateTime.truncate(DateTime.utc_now(), :second))
)

{:ok, card} =
Cornucopia.create_card(%{
category: "C", value: "NR3", description: "D", edition: "webapp",
version: "2.2", external_id: "NR_LAST1", language: "en", misc: "m",
owasp_scp: [], owasp_devguide: [], owasp_asvs: [], owasp_appsensor: [],
capec: [], safecode: [], owasp_mastg: [], owasp_masvs: []
})

# Only card is played → no nil-round cards → last_round? = true
Copi.Repo.insert!(%Copi.Cornucopia.DealtCard{
player_id: player.id, card_id: card.id, played_in_round: 1
})

{:ok, show_live, _html} = live(conn, "/games/#{game_id}/players/#{player.id}")
render_click(show_live, "next_round", %{})
:timer.sleep(100)

{:ok, updated_game} = Cornucopia.Game.find(game_id)
assert updated_game.rounds_played == 1
assert updated_game.finished_at != nil
end

test "toggle_vote adds then removes a vote for a dealt card", %{conn: conn, player: player} do
game_id = player.game_id
{:ok, game} = Cornucopia.Game.find(game_id)

Copi.Repo.update!(
Ecto.Changeset.change(game, started_at: DateTime.truncate(DateTime.utc_now(), :second))
)

{:ok, card} =
Cornucopia.create_card(%{
category: "C", value: "TV1", description: "D", edition: "webapp",
version: "2.2", external_id: "TV_CARD1", language: "en", misc: "m",
owasp_scp: [], owasp_devguide: [], owasp_asvs: [], owasp_appsensor: [],
capec: [], safecode: [], owasp_mastg: [], owasp_masvs: []
})

dealt = Copi.Repo.insert!(%Copi.Cornucopia.DealtCard{
player_id: player.id, card_id: card.id, played_in_round: 1
})

{:ok, show_live, _html} = live(conn, "/games/#{game_id}/players/#{player.id}")

render_click(show_live, "toggle_vote", %{"dealt_card_id" => to_string(dealt.id)})
:timer.sleep(100)

{:ok, updated_dealt} = Copi.Cornucopia.DealtCard.find(to_string(dealt.id))
assert length(updated_dealt.votes) == 1

render_click(show_live, "toggle_vote", %{"dealt_card_id" => to_string(dealt.id)})
:timer.sleep(100)

{:ok, updated_dealt2} = Copi.Cornucopia.DealtCard.find(to_string(dealt.id))
assert length(updated_dealt2.votes) == 0
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,8 @@ defmodule CopiWeb.Plugs.RateLimiterPlugTest do
assert conn.status != 429
refute conn.halted
end
test "init/1 returns opts unchanged" do
assert RateLimiterPlug.init([]) == []
assert RateLimiterPlug.init(foo: :bar) == [foo: :bar]
end
end
4 changes: 2 additions & 2 deletions scripts/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -812,7 +812,7 @@ def get_replacement_value_from_dict(el_text: str, replacement_values: List[Tuple

for k, v in replacement_values:
# Avoid expensive regex if key is not even in text
if k.strip() not in el_text:
if not isinstance(k, str) or k.strip() not in el_text:
continue

el_new = get_replacement_mapping_value(k, v, el_text)
Expand Down Expand Up @@ -891,7 +891,7 @@ def get_template_for_edition(layout: str = "guide", template: str = "bridge", ed

def get_valid_layout_choices() -> List[str]:
layouts = []
if convert_vars.args.layout.lower() == "all" or convert_vars.args.layout == "":
if not convert_vars.args.layout or convert_vars.args.layout.lower() == "all" or convert_vars.args.layout == "":
for layout in convert_vars.LAYOUT_CHOICES:
if layout not in ("all", "guide"):
layouts.append(layout)
Expand Down
Loading