228 lines
7.4 KiB
Elixir
228 lines
7.4 KiB
Elixir
defmodule Towerops.Agents.AgentTokenTest do
|
|
use Towerops.DataCase, async: true
|
|
|
|
alias Towerops.Agents.AgentToken
|
|
|
|
describe "build_token/3" do
|
|
test "generates a token with 48 bytes of entropy" do
|
|
org_id = Ecto.UUID.generate()
|
|
{token, _changeset} = AgentToken.build_token(org_id, "Test Agent")
|
|
|
|
# Decode the base64-encoded token
|
|
decoded = Base.url_decode64!(token, padding: false)
|
|
|
|
# Should be exactly 48 bytes
|
|
assert byte_size(decoded) == 48
|
|
end
|
|
|
|
test "returns token string and valid changeset for organization agent" do
|
|
org_id = Ecto.UUID.generate()
|
|
{token, changeset} = AgentToken.build_token(org_id, "My Agent")
|
|
|
|
assert is_binary(token)
|
|
assert changeset.valid?
|
|
assert changeset.changes.token == token
|
|
assert changeset.changes.name == "My Agent"
|
|
assert changeset.changes.organization_id == org_id
|
|
assert Ecto.Changeset.get_field(changeset, :is_cloud_poller) == false
|
|
end
|
|
|
|
test "sets is_cloud_poller to false by default" do
|
|
org_id = Ecto.UUID.generate()
|
|
{_token, changeset} = AgentToken.build_token(org_id, "Test Agent")
|
|
|
|
assert Ecto.Changeset.get_field(changeset, :is_cloud_poller) == false
|
|
end
|
|
|
|
test "generates unique tokens on each call" do
|
|
org_id = Ecto.UUID.generate()
|
|
{token1, _} = AgentToken.build_token(org_id, "Agent 1")
|
|
{token2, _} = AgentToken.build_token(org_id, "Agent 2")
|
|
{token3, _} = AgentToken.build_token(org_id, "Agent 3")
|
|
|
|
# All tokens should be different
|
|
assert token1 != token2
|
|
assert token2 != token3
|
|
assert token1 != token3
|
|
end
|
|
|
|
test "creates cloud poller token when is_cloud_poller is true" do
|
|
{token, changeset} = AgentToken.build_token(nil, "Cloud Poller", is_cloud_poller: true)
|
|
|
|
assert is_binary(token)
|
|
assert changeset.valid?
|
|
assert changeset.changes.is_cloud_poller == true
|
|
refute Map.has_key?(changeset.changes, :organization_id)
|
|
end
|
|
|
|
test "requires name" do
|
|
org_id = Ecto.UUID.generate()
|
|
{_token, changeset} = AgentToken.build_token(org_id, nil)
|
|
|
|
refute changeset.valid?
|
|
assert %{name: ["can't be blank"]} = errors_on(changeset)
|
|
end
|
|
|
|
test "adds error if cloud poller has organization_id" do
|
|
org_id = Ecto.UUID.generate()
|
|
{_token, changeset} = AgentToken.build_token(org_id, "Test", is_cloud_poller: true)
|
|
|
|
refute changeset.valid?
|
|
assert %{organization_id: ["must be nil for cloud pollers"]} = errors_on(changeset)
|
|
end
|
|
|
|
test "adds error if non-cloud poller has nil organization_id" do
|
|
{_token, changeset} = AgentToken.build_token(nil, "Test", is_cloud_poller: false)
|
|
|
|
refute changeset.valid?
|
|
assert %{organization_id: ["can't be blank for non-cloud pollers"]} = errors_on(changeset)
|
|
end
|
|
|
|
test "token is URL-safe base64 encoded without padding" do
|
|
org_id = Ecto.UUID.generate()
|
|
{token, _changeset} = AgentToken.build_token(org_id, "Test Agent")
|
|
|
|
# Should not contain padding characters
|
|
refute String.contains?(token, "=")
|
|
|
|
# Should only contain URL-safe base64 characters
|
|
assert token =~ ~r/^[A-Za-z0-9_-]+$/
|
|
end
|
|
end
|
|
|
|
describe "update_changeset/2" do
|
|
test "allows updating name" do
|
|
agent_token = %AgentToken{
|
|
id: Ecto.UUID.generate(),
|
|
token: "existing-token",
|
|
name: "Old Name",
|
|
organization_id: Ecto.UUID.generate()
|
|
}
|
|
|
|
attrs = %{name: "New Name"}
|
|
changeset = AgentToken.update_changeset(agent_token, attrs)
|
|
|
|
assert changeset.valid?
|
|
assert changeset.changes.name == "New Name"
|
|
end
|
|
|
|
test "allows updating allow_remote_debug" do
|
|
agent_token = %AgentToken{
|
|
id: Ecto.UUID.generate(),
|
|
token: "existing-token",
|
|
name: "Test Agent",
|
|
organization_id: Ecto.UUID.generate(),
|
|
allow_remote_debug: false
|
|
}
|
|
|
|
attrs = %{allow_remote_debug: true}
|
|
changeset = AgentToken.update_changeset(agent_token, attrs)
|
|
|
|
assert changeset.valid?
|
|
assert changeset.changes.allow_remote_debug == true
|
|
end
|
|
|
|
test "allows updating both name and allow_remote_debug" do
|
|
agent_token = %AgentToken{
|
|
id: Ecto.UUID.generate(),
|
|
token: "existing-token",
|
|
name: "Old Name",
|
|
organization_id: Ecto.UUID.generate(),
|
|
allow_remote_debug: false
|
|
}
|
|
|
|
attrs = %{name: "New Name", allow_remote_debug: true}
|
|
changeset = AgentToken.update_changeset(agent_token, attrs)
|
|
|
|
assert changeset.valid?
|
|
assert changeset.changes.name == "New Name"
|
|
assert changeset.changes.allow_remote_debug == true
|
|
end
|
|
|
|
test "requires name" do
|
|
agent_token = %AgentToken{
|
|
id: Ecto.UUID.generate(),
|
|
token: "existing-token",
|
|
name: "Old Name",
|
|
organization_id: Ecto.UUID.generate()
|
|
}
|
|
|
|
attrs = %{name: ""}
|
|
changeset = AgentToken.update_changeset(agent_token, attrs)
|
|
|
|
refute changeset.valid?
|
|
assert %{name: ["can't be blank"]} = errors_on(changeset)
|
|
end
|
|
|
|
test "ignores attempts to update token" do
|
|
agent_token = %AgentToken{
|
|
id: Ecto.UUID.generate(),
|
|
token: "original-token",
|
|
name: "Test Agent",
|
|
organization_id: Ecto.UUID.generate()
|
|
}
|
|
|
|
attrs = %{name: "New Name", token: "hacker-token"}
|
|
changeset = AgentToken.update_changeset(agent_token, attrs)
|
|
|
|
assert changeset.valid?
|
|
assert changeset.changes.name == "New Name"
|
|
refute Map.has_key?(changeset.changes, :token)
|
|
end
|
|
|
|
test "ignores attempts to update organization_id" do
|
|
agent_token = %AgentToken{
|
|
id: Ecto.UUID.generate(),
|
|
token: "existing-token",
|
|
name: "Test Agent",
|
|
organization_id: Ecto.UUID.generate()
|
|
}
|
|
|
|
new_org_id = Ecto.UUID.generate()
|
|
attrs = %{name: "New Name", organization_id: new_org_id}
|
|
changeset = AgentToken.update_changeset(agent_token, attrs)
|
|
|
|
assert changeset.valid?
|
|
assert changeset.changes.name == "New Name"
|
|
refute Map.has_key?(changeset.changes, :organization_id)
|
|
end
|
|
|
|
test "ignores attempts to update enabled" do
|
|
agent_token = %AgentToken{
|
|
id: Ecto.UUID.generate(),
|
|
token: "existing-token",
|
|
name: "Test Agent",
|
|
organization_id: Ecto.UUID.generate(),
|
|
enabled: true
|
|
}
|
|
|
|
attrs = %{name: "New Name", enabled: false}
|
|
changeset = AgentToken.update_changeset(agent_token, attrs)
|
|
|
|
assert changeset.valid?
|
|
assert changeset.changes.name == "New Name"
|
|
refute Map.has_key?(changeset.changes, :enabled)
|
|
end
|
|
end
|
|
|
|
describe "verify_token/1" do
|
|
test "returns {:ok, token} for valid binary token" do
|
|
token = "valid-token-string"
|
|
assert AgentToken.verify_token(token) == {:ok, token}
|
|
end
|
|
|
|
test "returns {:ok, token} for any binary string" do
|
|
assert AgentToken.verify_token("abc123") == {:ok, "abc123"}
|
|
assert AgentToken.verify_token("") == {:ok, ""}
|
|
assert AgentToken.verify_token("with-dashes") == {:ok, "with-dashes"}
|
|
end
|
|
|
|
test "returns {:error, :invalid_token} for non-binary values" do
|
|
assert AgentToken.verify_token(nil) == {:error, :invalid_token}
|
|
assert AgentToken.verify_token(12_345) == {:error, :invalid_token}
|
|
assert AgentToken.verify_token([]) == {:error, :invalid_token}
|
|
assert AgentToken.verify_token(%{}) == {:error, :invalid_token}
|
|
assert AgentToken.verify_token(:atom) == {:error, :invalid_token}
|
|
end
|
|
end
|
|
end
|