# frozen_string_literal: true
describe ESM::Exile::Territory do
let!(:community) { create(:community) }
let!(:server) { create(:server, :v2, :with_territories, community_id: community.id) }
let!(:settings) { server.server_setting }
let!(:territory_example) do
owner_uid = Faker::Steam.uid
moderator_uids = Array.new(2) { Faker::Steam.uid }
builder_uids = Array.new(2) { Faker::Steam.uid }
create(
:exile_territory,
owner_uid: owner_uid,
moderators: [owner_uid] + moderator_uids,
build_rights: [owner_uid] + moderator_uids + builder_uids,
level: Faker::Number.between(from: 0, to: 3),
server_id: server.id
)
end
# Not loaded every time so we can change the values of our example
let(:territory) { ESM::Exile::Territory.new(server: server, territory: territory_example.to_h) }
let(:same_level_territory) do
server.territories.where(territory_level: territory_example.level).first
end
let(:next_level_territory) do
server.territories.where(territory_level: territory_example.level + 1).first
end
describe "#id" do
it "is expected to return the value" do
id = territory_example.esm_custom_id || territory_example.encoded_id
expect(territory.id).to eq(id)
end
end
describe "#name" do
it "is expected to return the value" do
expect(territory.name).to eq(territory_example.territory_name)
end
end
describe "#owner" do
it "is expected to return the value" do
expect(territory.owner).to eq("#{territory_example.owner_name} (#{territory_example.owner_uid})")
end
end
describe "#level" do
it "is expected to return the value" do
expect(territory.level).to eq(territory_example.level)
end
end
describe "#object_count" do
it "is expected to return the value" do
expect(territory.object_count).to eq(territory_example.object_count)
end
end
describe "#radius" do
it "is expected to return the value" do
expect(territory.radius).to eq(territory_example.radius)
end
end
describe "#flag_path" do
it "is expected to return the value" do
expect(territory.flag_path).to eq(
territory.send(:convert_flag_path, territory_example.flag_texture)
)
end
end
describe "#stolen?" do
it "is expected to return the value" do
expect(territory.stolen?).to eq(territory_example.flag_stolen)
end
end
describe "#flag_status" do
it "is expected to return the value" do
expect(territory.flag_status).to eq(territory_example.flag_stolen ? "Stolen!" : "Secure")
end
end
describe "#last_paid_at" do
it "is expected to return the value" do
expect(territory.last_paid_at.strftime(ESM::Time::Format::TIME)).to eq(
territory_example.last_paid_at.strftime(ESM::Time::Format::TIME)
)
end
end
describe "#next_due_date" do
it "is expected to return the value" do
next_due_date = territory_example.last_paid_at + settings.territory_lifetime.days
expect(territory.next_due_date.strftime(ESM::Time::Format::TIME)).to eq(
next_due_date.strftime(ESM::Time::Format::TIME)
)
end
end
describe "#max_object_count" do
it "is expected to return the value" do
expect(territory.max_object_count).to eq(same_level_territory.territory_object_count)
end
end
describe "#renew_price" do
it "is expected to return the value the price (No tax)" do
settings.update(territory_payment_tax: 0)
expected_price = territory_example.level *
territory_example.object_count *
settings.territory_price_per_object
expect(territory.renew_price).to eq("#{expected_price.to_delimitated_s} poptabs")
end
it "is expected to return the value the price (With tax)" do
expected_price = territory_example.level *
territory_example.object_count *
settings.territory_price_per_object
expected_price += (expected_price * (settings.territory_payment_tax.to_f / 100)).round
expect(territory.renew_price).to eq(
"#{expected_price.to_delimitated_s} poptabs (#{settings.territory_payment_tax}% tax added)"
)
end
end
describe "#upgradeable?" do
it "is expected to return the value" do
expect(territory.upgradeable?).to eq(next_level_territory.present?)
end
end
describe "Requires Upgraded Territory" do
before do
territory_example.level = Faker::Number.between(from: 1, to: ESM::Territory.all.size - 2)
end
describe "#upgrade_level" do
it "is expected to return the value" do
expect(territory.upgrade_level).to eq(next_level_territory.territory_level)
end
end
describe "#upgrade_price" do
it "is expected to return the value the price (No tax)" do
settings.update(territory_upgrade_tax: 0)
expect(territory.upgrade_price).to eq(
"#{next_level_territory.territory_purchase_price.to_delimitated_s} poptabs"
)
end
it "is expected to return the value the price (With tax)" do
expected_price = next_level_territory.territory_purchase_price
expected_price += (expected_price * (settings.territory_upgrade_tax.to_f / 100)).round
expect(territory.upgrade_price).to eq(
"#{expected_price.to_delimitated_s} poptabs (#{settings.territory_upgrade_tax}% tax added)"
)
end
end
describe "#upgrade_radius" do
it "is expected to return the value" do
expect(territory.upgrade_radius).to eq(next_level_territory.territory_radius)
end
end
describe "#upgrade_object_count" do
it "is expected to return the value" do
expect(territory.upgrade_object_count).to eq(next_level_territory.territory_object_count)
end
end
end
describe "#moderators" do
let(:moderators) do
territory_example.moderators_as_hash.reject do |hash|
territory_example.owner_uid == hash[:uid]
end
end
before do
territory_example.moderators.each do |uid|
create(:exile_account, uid:) if ESM::ExileAccount.find_by(uid:).nil?
end
end
it "is expected to return the value" do
expect(territory.moderators).to eq(
moderators.join_map("\n") { |hash| "#{hash[:name]} (#{hash[:uid]})" }
)
end
end
describe "#builders" do
let(:builders) do
territory_example.builders_as_hash.reject do |hash|
territory_example.owner_uid == hash[:uid] ||
territory_example.moderators.include?(hash[:uid])
end
end
before do
territory_example.build_rights.each do |uid|
create(:exile_account, uid:) if ESM::ExileAccount.find_by(uid:).nil?
end
end
it "is expected to return the value" do
expect(territory.builders).to eq(
builders.join_map("\n") { |hash| "#{hash[:name]} (#{hash[:uid]})" }
)
end
end
describe "#status_color" do
it "is green" do
territory_example.flag_stolen = false
territory_example.last_paid_at = ::Time.current.strftime(TerritoryGenerator::TIME_FORMAT)
expect(territory.status_color).to eq(ESM::Color::Toast::GREEN)
end
it "is yellow" do
territory_example.flag_stolen = false
last_paid_at = ::Time.zone.today - (settings.territory_lifetime - 3).days
territory_example.last_paid_at = last_paid_at.to_time.strftime(TerritoryGenerator::TIME_FORMAT)
expect(territory.status_color).to eq(ESM::Color::Toast::YELLOW)
end
it "is red (Stolen)" do
territory_example.flag_stolen = true
expect(territory.status_color).to eq(ESM::Color::Toast::RED)
end
it "is red (Payment due)" do
territory_example.flag_stolen = false
last_paid_at = ::Time.zone.today - (settings.territory_lifetime - 1).days
territory_example.last_paid_at = last_paid_at.to_time.strftime(TerritoryGenerator::TIME_FORMAT)
expect(territory.status_color).to eq(ESM::Color::Toast::RED)
end
end
describe "#days_left_until_payment_due" do
it "is expected to return the value (Just Paid)" do
last_paid_at = ::Time.current
territory_example.last_paid_at = last_paid_at.strftime(TerritoryGenerator::TIME_FORMAT)
expect(territory.days_left_until_payment_due).to eq(settings.territory_lifetime)
end
it "is expected to return the value (Needs payment)" do
last_paid_at = ::Time.zone.today - (settings.territory_lifetime - 3).days
territory_example.last_paid_at = last_paid_at.to_time.strftime(TerritoryGenerator::TIME_FORMAT)
expect(territory.days_left_until_payment_due).to eq(3)
end
it "is expected to return the value (Payment ASAP)" do
last_paid_at = ::Time.zone.today - (settings.territory_lifetime - 1).days
territory_example.last_paid_at = last_paid_at.to_time.strftime(TerritoryGenerator::TIME_FORMAT)
expect(territory.days_left_until_payment_due).to eq(1)
end
end
describe "#payment_reminder_message" do
let(:time_left_message) do
next_due_date = territory_example.last_paid_at + settings.territory_lifetime.days
"You have `#{ESM::Time.distance_of_time_in_words(next_due_date, precise: false)}` until your next payment is due."
end
it "is expected to return a message (Just paid)" do
last_paid_at = ::Time.current
territory_example.last_paid_at = last_paid_at.strftime(TerritoryGenerator::TIME_FORMAT)
expect(territory.payment_reminder_message).to eq("")
end
it "is expected to return a message (Needing to pay)" do
last_paid_at = ::Time.current - (settings.territory_lifetime - 3).days
territory_example.last_paid_at = last_paid_at.strftime(TerritoryGenerator::TIME_FORMAT)
expect(territory.payment_reminder_message).to eq(":warning: **You should consider making a base payment soon.**\n#{time_left_message}")
end
it "is expected to return a message (pay ASAP)" do
last_paid_at = ::Time.current - (settings.territory_lifetime - 1).days
territory_example.last_paid_at = last_paid_at.strftime(TerritoryGenerator::TIME_FORMAT)
expect(territory.payment_reminder_message).to eq(":alarm_clock: **You should make a base payment ASAP to avoid losing your base!**\n#{time_left_message}")
end
end
describe "#normalize_territory" do
let(:owner_uid) { "owner_steam_uid" }
let(:moderator_uid) { "moderator_steam_uid" }
let(:builder_uid) { "builder_steam_uid" }
# Account names are intentionally out of order (and mixed case) so the sort
# is observable: alice, Bob, Charlie.
let(:input) do
{
owner_uid: owner_uid,
territory_name: "My Lovely Base",
moderators: [
{uid: owner_uid, name: "Charlie"},
{uid: moderator_uid, name: "alice"}
],
build_rights: [
{uid: owner_uid, name: "Charlie"},
{uid: moderator_uid, name: "alice"},
{uid: builder_uid, name: "Bob"}
]
}
end
subject(:normalized) { territory.send(:normalize_territory, input) }
it "exposes territory_name as :name" do
expect(normalized[:name]).to eq("My Lovely Base")
end
it "defaults a missing esm_custom_id to nil" do
expect(normalized[:esm_custom_id]).to be_nil
end
it "preserves an existing esm_custom_id" do
input[:esm_custom_id] = "custom-123"
expect(normalized[:esm_custom_id]).to eq("custom-123")
end
it "coerces a non-hash input via #to_h" do
normalized = territory.send(:normalize_territory, territory_example)
expect(normalized[:name]).to eq(territory_example.territory_name)
end
describe "account labeling and sorting" do
it "sorts moderators and build rights by name (case-insensitive)" do
expect(normalized[:moderators].map { |account| account[:name] }).to eq(["alice", "Charlie"])
expect(normalized[:build_rights].map { |account| account[:name] }).to eq(["alice", "Bob", "Charlie"])
end
it "flags the owner as owner, moderator, and builder" do
owner = normalized[:build_rights].find { |account| account[:uid] == owner_uid }
expect([owner[:owner], owner[:moderator], owner[:builder]]).to eq([true, true, true])
end
it "flags a build-rights-only account as a builder alone" do
builder = normalized[:build_rights].find { |account| account[:uid] == builder_uid }
expect([builder[:owner], builder[:moderator], builder[:builder]]).to eq([false, false, true])
end
end
context "when moderators and build rights are absent" do
it "normalizes without raising" do
input[:moderators] = nil
input[:build_rights] = nil
expect { normalized }.not_to raise_error
expect(normalized[:name]).to eq("My Lovely Base")
end
end
end
describe "#convert_flag_path" do
let(:base_path) { "https://exile-server-manager.s3.amazonaws.com/flags" }
let(:default_flag) { "#{base_path}/flag_white_co.jpg" }
it "parses arma path" do
expect(territory.send(:convert_flag_path, "\\A3\\Data_F\\Flags\\flag_us_co.paa")).to eq("#{base_path}/flag_us_co.jpg")
end
it "parses exile mod path" do
expect(territory.send(:convert_flag_path, "exile_assets\\texture\\flag\\flag_misc_knuckles_co.paa")).to eq("#{base_path}/flag_misc_knuckles_co.jpg")
end
it "is expected to return the value default" do
expect(territory.send(:convert_flag_path, "")).to eq(default_flag)
expect(territory.send(:convert_flag_path, "foo_bar_flag")).to eq(default_flag)
expect(territory.send(:convert_flag_path, "\\A3\\SomeDirectory\\flag_something.paa")).to eq(default_flag)
end
end
end