mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-10-11 14:06:20 +08:00
Fix some integration intermittent tests (#2594)
This commit is contained in:
parent
0d9a6e2fb8
commit
8d15d5e93c
9 changed files with 86 additions and 83 deletions
|
@ -1024,10 +1024,14 @@ defmodule Livebook.FileSystem.S3Test do
|
|||
|
||||
describe "FileSystem.dump/1" do
|
||||
test "dumps into a map ready to be stored" do
|
||||
file_system = build(:fs_s3)
|
||||
file_system =
|
||||
build(:fs_s3,
|
||||
id: "personal-hub-s3-HsVlgSlWeap3BklGso76U1WdxiX7afWg2k4cXs8z7hI",
|
||||
bucket_url: "https://mybucket.s3.amazonaws.com"
|
||||
)
|
||||
|
||||
assert FileSystem.dump(file_system) == %{
|
||||
id: "personal-hub-s3-86IzUeRugmgK2-X2FmlkurFD0UPsr4Qs1IwieDqfQpA",
|
||||
id: "personal-hub-s3-HsVlgSlWeap3BklGso76U1WdxiX7afWg2k4cXs8z7hI",
|
||||
bucket_url: "https://mybucket.s3.amazonaws.com",
|
||||
region: "us-east-1",
|
||||
access_key_id: "key",
|
||||
|
|
|
@ -10,7 +10,7 @@ defmodule Livebook.Integration.AppsTest do
|
|||
|
||||
hub = create_team_hub(user, node)
|
||||
hub_id = hub.id
|
||||
secret = insert_secret(name: "DB_PASSWORD", value: "postgres", hub_id: hub.id)
|
||||
secret = insert_secret(name: "APP_DB_PASSWORD", value: "postgres", hub_id: hub.id)
|
||||
secret_name = secret.name
|
||||
slug = Livebook.Utils.random_short_id()
|
||||
|
||||
|
|
|
@ -710,12 +710,27 @@ defmodule Livebook.Hubs.TeamClientTest do
|
|||
}}
|
||||
end
|
||||
|
||||
test "dispatches the agents list", %{team: team, agent_connected: agent_connected} do
|
||||
test "dispatches the agents list",
|
||||
%{
|
||||
team: team,
|
||||
agent_connected: agent_connected,
|
||||
deployment_group: %{id: deployment_group_id}
|
||||
} do
|
||||
deployment_group_id = to_string(deployment_group_id)
|
||||
org_id = to_string(team.org_id)
|
||||
hub_id = team.id
|
||||
|
||||
pid = connect_to_teams(team)
|
||||
|
||||
# Since we're connecting as Agent, we should receive the
|
||||
# `:agent_joined` event from `:agent_connected` event
|
||||
assert_receive {:agent_joined, agent}
|
||||
assert_receive {:agent_joined,
|
||||
%{
|
||||
hub_id: ^hub_id,
|
||||
org_id: ^org_id,
|
||||
deployment_group_id: ^deployment_group_id
|
||||
} = agent}
|
||||
|
||||
assert agent in TeamClient.get_agents(team.id)
|
||||
|
||||
assert_receive {:deployment_group_created, deployment_group}
|
||||
|
|
|
@ -96,14 +96,14 @@ defmodule Livebook.HubsTest do
|
|||
secret = build(:secret, name: name, value: value, hub_id: hub.id)
|
||||
|
||||
assert Hubs.create_secret(hub, secret) == :ok
|
||||
assert_receive {:secret_created, %{name: ^name, value: ^value}}
|
||||
assert_receive {:secret_created, %{name: ^name, value: ^value, hub_id: ^value}}
|
||||
assert secret in Hubs.get_secrets(hub)
|
||||
|
||||
new_value = "BAZ"
|
||||
updated_secret = Map.replace!(secret, :value, new_value)
|
||||
|
||||
assert Hubs.update_secret(hub, updated_secret) == :ok
|
||||
assert_receive {:secret_updated, %{name: ^name, value: ^new_value}}
|
||||
assert_receive {:secret_updated, %{name: ^name, value: ^new_value, hub_id: ^value}}
|
||||
refute secret in Hubs.get_secrets(hub)
|
||||
assert updated_secret in Hubs.get_secrets(hub)
|
||||
end
|
||||
|
@ -131,11 +131,11 @@ defmodule Livebook.HubsTest do
|
|||
secret = build(:secret, name: name, value: value, hub_id: hub.id)
|
||||
|
||||
assert Hubs.create_secret(hub, secret) == :ok
|
||||
assert_receive {:secret_created, %{name: ^name, value: ^value}}
|
||||
assert_receive {:secret_created, %{name: ^name, value: ^value, hub_id: ^value}}
|
||||
assert secret in Hubs.get_secrets(hub)
|
||||
|
||||
assert Hubs.delete_secret(hub, secret) == :ok
|
||||
assert_receive {:secret_deleted, %{name: ^name, value: ^value}}
|
||||
assert_receive {:secret_deleted, %{name: ^name, value: ^value, hub_id: ^value}}
|
||||
refute secret in Hubs.get_secrets(hub)
|
||||
|
||||
# Guarantee it's been removed and will return HTTP status 404
|
||||
|
@ -176,7 +176,10 @@ defmodule Livebook.HubsTest do
|
|||
file_system = build(:fs_s3, bucket_url: bucket_url)
|
||||
|
||||
assert Hubs.create_file_system(hub, file_system) == :ok
|
||||
assert_receive {:file_system_created, %{external_id: external_id} = file_system}
|
||||
|
||||
assert_receive {:file_system_created,
|
||||
%{bucket_url: ^bucket_url, external_id: external_id} = file_system}
|
||||
|
||||
assert file_system in Hubs.get_file_systems(hub)
|
||||
|
||||
updated_file_system = Map.replace!(file_system, :region, "eu-central-1")
|
||||
|
@ -210,7 +213,10 @@ defmodule Livebook.HubsTest do
|
|||
file_system = build(:fs_s3, bucket_url: bucket_url)
|
||||
|
||||
assert Hubs.create_file_system(hub, file_system) == :ok
|
||||
assert_receive {:file_system_created, %{external_id: external_id} = file_system}
|
||||
|
||||
assert_receive {:file_system_created,
|
||||
%{bucket_url: ^bucket_url, external_id: external_id} = file_system}
|
||||
|
||||
assert file_system in Hubs.get_file_systems(hub)
|
||||
|
||||
assert Hubs.delete_file_system(hub, file_system) == :ok
|
||||
|
|
|
@ -264,8 +264,8 @@ defmodule Livebook.TeamsTest do
|
|||
|
||||
defp connect_to_teams(user, node) do
|
||||
%{id: id} = team = create_team_hub(user, node)
|
||||
assert_receive {:hub_connected, ^id}
|
||||
assert_receive {:client_connected, ^id}
|
||||
assert_receive {:hub_connected, ^id}, 3_000
|
||||
assert_receive {:client_connected, ^id}, 3_000
|
||||
|
||||
team
|
||||
end
|
||||
|
|
|
@ -19,12 +19,8 @@ defmodule LivebookWeb.Integration.Hub.DeploymentGroupTest do
|
|||
end
|
||||
|
||||
test "creates a deployment group", %{conn: conn, hub: hub} do
|
||||
deployment_group =
|
||||
build(:deployment_group,
|
||||
name: "TEAMS_ADD_DEPLOYMENT_GROUP",
|
||||
mode: :offline,
|
||||
hub_id: hub.id
|
||||
)
|
||||
deployment_group = build(:deployment_group, mode: :offline, hub_id: hub.id)
|
||||
name = deployment_group.name
|
||||
|
||||
attrs = %{
|
||||
deployment_group: %{
|
||||
|
@ -52,8 +48,7 @@ defmodule LivebookWeb.Integration.Hub.DeploymentGroupTest do
|
|||
|> element("#deployment-group-form")
|
||||
|> render_submit(attrs)
|
||||
|
||||
assert_receive {:deployment_group_created,
|
||||
%DeploymentGroup{name: "TEAMS_ADD_DEPLOYMENT_GROUP"} = deployment_group}
|
||||
assert_receive {:deployment_group_created, %DeploymentGroup{name: ^name} = deployment_group}
|
||||
|
||||
assert_patch(view, "/hub/#{hub.id}")
|
||||
assert render(view) =~ "Deployment group added successfully"
|
||||
|
@ -72,22 +67,11 @@ defmodule LivebookWeb.Integration.Hub.DeploymentGroupTest do
|
|||
end
|
||||
|
||||
test "creates a secret", %{conn: conn, hub: hub} do
|
||||
%{id: id} =
|
||||
insert_deployment_group(
|
||||
name: "TEAMS_EDIT_DEPLOYMENT_GROUP",
|
||||
mode: :online,
|
||||
hub_id: hub.id
|
||||
)
|
||||
%{id: id} = insert_deployment_group(mode: :online, hub_id: hub.id)
|
||||
secret = build(:secret, hub_id: hub.id, deployment_group_id: id)
|
||||
|
||||
{:ok, view, _html} = live(conn, ~p"/hub/#{hub.id}")
|
||||
|
||||
secret =
|
||||
build(:secret,
|
||||
name: "DEPLOYMENT_GROUP_SECRET",
|
||||
hub_id: hub.id,
|
||||
deployment_group_id: id
|
||||
)
|
||||
|
||||
attrs = %{
|
||||
secret: %{
|
||||
name: secret.name,
|
||||
|
@ -123,7 +107,7 @@ defmodule LivebookWeb.Integration.Hub.DeploymentGroupTest do
|
|||
deployment_group}
|
||||
|
||||
assert_patch(view, ~p"/hub/#{hub.id}")
|
||||
assert render(view) =~ "Secret DEPLOYMENT_GROUP_SECRET added successfully"
|
||||
assert render(view) =~ "Secret #{secret.name} added successfully"
|
||||
assert render(element(view, "#hub-deployment-group-#{id}")) =~ secret.name
|
||||
assert secret in deployment_group.secrets
|
||||
|
||||
|
@ -136,11 +120,8 @@ defmodule LivebookWeb.Integration.Hub.DeploymentGroupTest do
|
|||
end
|
||||
|
||||
test "updates an existing secret", %{conn: conn, hub: hub} do
|
||||
%{id: id} =
|
||||
insert_deployment_group(name: "TEAMS_EDIT_DEPLOYMENT_GROUP", mode: :online, hub_id: hub.id)
|
||||
|
||||
secret =
|
||||
insert_secret(name: "DEPLOYMENT_GROUP_EDIT_SECRET", hub_id: hub.id, deployment_group_id: id)
|
||||
%{id: id} = insert_deployment_group(mode: :online, hub_id: hub.id)
|
||||
secret = insert_secret(hub_id: hub.id, deployment_group_id: id)
|
||||
|
||||
assert_receive {:deployment_group_updated,
|
||||
%Livebook.Teams.DeploymentGroup{id: ^id, secrets: [^secret]}}
|
||||
|
@ -182,21 +163,14 @@ defmodule LivebookWeb.Integration.Hub.DeploymentGroupTest do
|
|||
deployment_group}
|
||||
|
||||
assert_patch(view, "/hub/#{hub.id}")
|
||||
assert render(view) =~ "Secret DEPLOYMENT_GROUP_EDIT_SECRET updated successfully"
|
||||
assert render(view) =~ "Secret #{secret.name} updated successfully"
|
||||
assert render(element(view, "#hub-deployment-group-#{id}")) =~ secret.name
|
||||
assert updated_secret in deployment_group.secrets
|
||||
end
|
||||
|
||||
test "deletes an existing secret", %{conn: conn, hub: hub} do
|
||||
%{id: id} =
|
||||
insert_deployment_group(name: "TEAMS_EDIT_DEPLOYMENT_GROUP", mode: :online, hub_id: hub.id)
|
||||
|
||||
secret =
|
||||
insert_secret(
|
||||
name: "DEPLOYMENT_GROUP_DELETE_SECRET",
|
||||
hub_id: hub.id,
|
||||
deployment_group_id: id
|
||||
)
|
||||
%{id: id} = insert_deployment_group(mode: :online, hub_id: hub.id)
|
||||
secret = insert_secret(hub_id: hub.id, deployment_group_id: id)
|
||||
|
||||
{:ok, view, _html} = live(conn, ~p"/hub/#{hub.id}")
|
||||
|
||||
|
@ -210,13 +184,12 @@ defmodule LivebookWeb.Integration.Hub.DeploymentGroupTest do
|
|||
%Livebook.Teams.DeploymentGroup{id: ^id, secrets: []}}
|
||||
|
||||
assert_patch(view, ~p"/hub/#{hub.id}")
|
||||
assert render(view) =~ "Secret DEPLOYMENT_GROUP_DELETE_SECRET deleted successfully"
|
||||
assert render(view) =~ "Secret #{secret.name} deleted successfully"
|
||||
refute render(element(view, "#hub-deployment-group-#{id}")) =~ secret.name
|
||||
end
|
||||
|
||||
test "shows the agent count", %{conn: conn, hub: hub} do
|
||||
name = "TEAMS_EDIT_DEPLOYMENT_GROUP2"
|
||||
%{id: id} = insert_deployment_group(name: name, mode: :online, hub_id: hub.id)
|
||||
%{id: id} = insert_deployment_group(mode: :online, hub_id: hub.id)
|
||||
|
||||
{:ok, view, _html} = live(conn, ~p"/hub/#{hub.id}")
|
||||
|
||||
|
@ -255,11 +228,13 @@ defmodule LivebookWeb.Integration.Hub.DeploymentGroupTest do
|
|||
|
||||
@tag :tmp_dir
|
||||
test "shows the app deployed count", %{conn: conn, hub: hub, tmp_dir: tmp_dir} do
|
||||
name = "TEAMS_EDIT_DEPLOYMENT_GROUP3"
|
||||
%{id: id} = insert_deployment_group(name: name, mode: :online, hub_id: hub.id)
|
||||
%{id: id} = insert_deployment_group(mode: :online, hub_id: hub.id)
|
||||
id = to_string(id)
|
||||
|
||||
{:ok, view, _html} = live(conn, ~p"/hub/#{hub.id}")
|
||||
|
||||
refute_received {:app_deployment_started, %{deployment_group_id: ^id}}
|
||||
|
||||
assert view
|
||||
|> element("#hub-deployment-group-#{id} [aria-label=\"apps deployed\"]")
|
||||
|> render()
|
||||
|
@ -274,7 +249,7 @@ defmodule LivebookWeb.Integration.Hub.DeploymentGroupTest do
|
|||
| app_settings: app_settings,
|
||||
name: "MyNotebook",
|
||||
hub_id: hub.id,
|
||||
deployment_group_id: to_string(id)
|
||||
deployment_group_id: id
|
||||
}
|
||||
|
||||
files_dir = Livebook.FileSystem.File.local(tmp_dir)
|
||||
|
@ -282,7 +257,7 @@ defmodule LivebookWeb.Integration.Hub.DeploymentGroupTest do
|
|||
{:ok, app_deployment} = Livebook.Teams.AppDeployment.new(notebook, files_dir)
|
||||
:ok = Livebook.Teams.deploy_app(hub, app_deployment)
|
||||
|
||||
assert_receive {:app_deployment_started, _}
|
||||
assert_receive {:app_deployment_started, %{deployment_group_id: ^id}}, 2_000
|
||||
|
||||
assert view
|
||||
|> element("#hub-deployment-group-#{id} [aria-label=\"apps deployed\"]")
|
||||
|
|
|
@ -9,11 +9,12 @@ defmodule LivebookWeb.Integration.Hub.EditLiveTest do
|
|||
describe "user" do
|
||||
setup %{user: user, node: node} do
|
||||
Livebook.Hubs.Broadcasts.subscribe([:crud, :connection, :secrets, :file_systems])
|
||||
Livebook.Teams.Broadcasts.subscribe([:deployment_groups])
|
||||
Livebook.Teams.Broadcasts.subscribe([:clients, :deployment_groups])
|
||||
hub = create_team_hub(user, node)
|
||||
id = hub.id
|
||||
|
||||
assert_receive {:hub_connected, ^id}
|
||||
assert_receive {:client_connected, ^id}
|
||||
|
||||
{:ok, hub: hub}
|
||||
end
|
||||
|
@ -67,7 +68,7 @@ defmodule LivebookWeb.Integration.Hub.EditLiveTest do
|
|||
|
||||
test "creates a secret", %{conn: conn, hub: hub} do
|
||||
{:ok, view, _html} = live(conn, ~p"/hub/#{hub.id}")
|
||||
secret = build(:secret, name: "TEAM_ADD_SECRET", hub_id: hub.id)
|
||||
secret = build(:secret, hub_id: hub.id)
|
||||
|
||||
attrs = %{
|
||||
secret: %{
|
||||
|
@ -100,7 +101,7 @@ defmodule LivebookWeb.Integration.Hub.EditLiveTest do
|
|||
|
||||
assert_receive {:secret_created, ^secret}
|
||||
assert_patch(view, "/hub/#{hub.id}")
|
||||
assert render(view) =~ "Secret TEAM_ADD_SECRET added successfully"
|
||||
assert render(view) =~ "Secret #{secret.name} added successfully"
|
||||
assert render(element(view, "#hub-secrets-list")) =~ secret.name
|
||||
assert secret in Livebook.Hubs.get_secrets(hub)
|
||||
|
||||
|
@ -113,7 +114,8 @@ defmodule LivebookWeb.Integration.Hub.EditLiveTest do
|
|||
end
|
||||
|
||||
test "updates existing secret", %{conn: conn, hub: hub} do
|
||||
secret = insert_secret(name: "TEAM_EDIT_SECRET", hub_id: hub.id)
|
||||
secret = insert_secret(hub_id: hub.id)
|
||||
|
||||
assert_receive {:secret_created, ^secret}
|
||||
|
||||
{:ok, view, _html} = live(conn, ~p"/hub/#{hub.id}")
|
||||
|
@ -151,13 +153,13 @@ defmodule LivebookWeb.Integration.Hub.EditLiveTest do
|
|||
|
||||
assert_receive {:secret_updated, ^updated_secret}
|
||||
assert_patch(view, "/hub/#{hub.id}")
|
||||
assert render(view) =~ "Secret TEAM_EDIT_SECRET updated successfully"
|
||||
assert render(view) =~ "Secret #{secret.name} updated successfully"
|
||||
assert render(element(view, "#hub-secrets-list")) =~ secret.name
|
||||
assert updated_secret in Livebook.Hubs.get_secrets(hub)
|
||||
end
|
||||
|
||||
test "deletes existing secret", %{conn: conn, hub: hub} do
|
||||
secret = insert_secret(name: "TEAM_DELETE_SECRET", hub_id: hub.id)
|
||||
secret = insert_secret(hub_id: hub.id)
|
||||
assert_receive {:secret_created, ^secret}
|
||||
|
||||
{:ok, view, _html} = live(conn, ~p"/hub/#{hub.id}")
|
||||
|
@ -174,7 +176,7 @@ defmodule LivebookWeb.Integration.Hub.EditLiveTest do
|
|||
|
||||
assert_receive {:secret_deleted, ^secret}
|
||||
assert_patch(view, "/hub/#{hub.id}")
|
||||
assert render(view) =~ "Secret TEAM_DELETE_SECRET deleted successfully"
|
||||
assert render(view) =~ "Secret #{secret.name} deleted successfully"
|
||||
refute secret in Livebook.Hubs.get_secrets(hub)
|
||||
end
|
||||
|
||||
|
@ -306,7 +308,7 @@ defmodule LivebookWeb.Integration.Hub.EditLiveTest do
|
|||
|
||||
test "shows an error when creating a secret", %{conn: conn, hub: hub} do
|
||||
{:ok, view, _html} = live(conn, ~p"/hub/#{hub.id}")
|
||||
secret = build(:secret, name: "TEAM_ADD_SECRET", hub_id: hub.id)
|
||||
secret = build(:secret, hub_id: hub.id)
|
||||
|
||||
attrs = %{
|
||||
secret: %{
|
||||
|
@ -377,10 +379,12 @@ defmodule LivebookWeb.Integration.Hub.EditLiveTest do
|
|||
|
||||
setup %{user: user, node: node} do
|
||||
Livebook.Hubs.Broadcasts.subscribe([:crud, :connection, :secrets, :file_systems])
|
||||
Livebook.Teams.Broadcasts.subscribe([:clients])
|
||||
hub = create_team_hub(user, node)
|
||||
id = hub.id
|
||||
|
||||
assert_receive {:hub_connected, ^id}
|
||||
assert_receive {:client_connected, ^id}
|
||||
|
||||
{:ok, hub: hub}
|
||||
end
|
||||
|
|
|
@ -128,16 +128,8 @@ defmodule LivebookWeb.Integration.SessionLiveTest do
|
|||
assert_receive {:hub_connected, ^id}
|
||||
|
||||
# creates a secret
|
||||
secret_name = "BIG_IMPORTANT_SECRET_TO_BE_UPDATED_OR_DELETED"
|
||||
secret_value = "123"
|
||||
|
||||
insert_secret(
|
||||
name: secret_name,
|
||||
value: secret_value,
|
||||
hub_id: team.id
|
||||
)
|
||||
|
||||
assert_receive {:secret_created, %{name: ^secret_name, value: ^secret_value}}
|
||||
secret = insert_secret(hub_id: team.id)
|
||||
assert_receive {:secret_created, ^secret}
|
||||
|
||||
# selects the notebook's hub with team hub id
|
||||
Session.set_notebook_hub(session.pid, team.id)
|
||||
|
@ -147,12 +139,12 @@ defmodule LivebookWeb.Integration.SessionLiveTest do
|
|||
|
||||
# clicks the button to edit a secret
|
||||
view
|
||||
|> element("#hub-#{id}-secret-#{secret_name}-edit-button")
|
||||
|> element("#hub-#{id}-secret-#{secret.name}-edit-button")
|
||||
|> render_click()
|
||||
|
||||
# redirects to hub page and loads the modal with
|
||||
# the secret name and value filled
|
||||
assert_redirect(view, ~p"/hub/#{id}/secrets/edit/#{secret_name}")
|
||||
assert_redirect(view, ~p"/hub/#{id}/secrets/edit/#{secret.name}")
|
||||
end
|
||||
|
||||
test "toggle a secret from team hub", %{conn: conn, session: session, user: user, node: node} do
|
||||
|
|
|
@ -49,8 +49,8 @@ defmodule Livebook.Factory do
|
|||
|
||||
def build(:secret) do
|
||||
%Livebook.Secrets.Secret{
|
||||
name: "FOO",
|
||||
value: "123",
|
||||
name: unique_value("FOO_"),
|
||||
value: Livebook.Utils.random_short_id(),
|
||||
hub_id: Livebook.Hubs.Personal.id(),
|
||||
deployment_group_id: nil
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ defmodule Livebook.Factory do
|
|||
|
||||
def build(:deployment_group) do
|
||||
%Livebook.Teams.DeploymentGroup{
|
||||
name: "FOO",
|
||||
name: unique_value("FOO_"),
|
||||
mode: :offline,
|
||||
agent_keys: [],
|
||||
secrets: []
|
||||
|
@ -76,7 +76,7 @@ defmodule Livebook.Factory do
|
|||
end
|
||||
|
||||
def build(:fs_s3) do
|
||||
bucket_url = "https://mybucket.s3.amazonaws.com"
|
||||
bucket_url = "https://#{unique_value("mybucket-")}.s3.amazonaws.com"
|
||||
hash = :crypto.hash(:sha256, bucket_url)
|
||||
hub_id = Livebook.Hubs.Personal.id()
|
||||
|
||||
|
@ -108,7 +108,7 @@ defmodule Livebook.Factory do
|
|||
|
||||
%Livebook.Teams.AppDeployment{
|
||||
id: "1",
|
||||
title: "MyNotebook",
|
||||
title: unique_value("MyNotebook-"),
|
||||
sha: shasum,
|
||||
slug: slug,
|
||||
file: content,
|
||||
|
@ -124,7 +124,7 @@ defmodule Livebook.Factory do
|
|||
def build(:agent) do
|
||||
%Livebook.Teams.Agent{
|
||||
id: "agent_name-#{Livebook.Utils.random_short_id()}",
|
||||
name: "agent_name",
|
||||
name: unique_value("agent_name"),
|
||||
hub_id: Livebook.Hubs.Personal.id(),
|
||||
org_id: "1",
|
||||
deployment_group_id: "1"
|
||||
|
@ -176,4 +176,11 @@ defmodule Livebook.Factory do
|
|||
# already running)
|
||||
Livebook.Hubs.save_hub(hub)
|
||||
end
|
||||
|
||||
def unique_value(prefix \\ nil) do
|
||||
value = unique_integer()
|
||||
if prefix, do: "#{prefix}#{value}", else: value
|
||||
end
|
||||
|
||||
defp unique_integer(), do: System.unique_integer([:positive])
|
||||
end
|
||||
|
|
Loading…
Add table
Reference in a new issue