mirror of
https://github.com/wanderer-industries/wanderer
synced 2026-10-16 00:19:19 +00:00
Review on the export/import PR turned up several things that did not hold. A system deleted from the target map still has its row, so building the "already there" set from every row disagreed with the add path, which goes by visibility. The system came back stripped of everything the document carried for it. The set now comes from the visible systems, matching both the add path and export. The counts reported to the user were not the same kind of number. Connections counted the whole payload and signatures counted upserts, so re-importing a file reported a pile of additions while changing nothing. Both now count what was actually missing beforehand. Signatures never made it into an export at all: the read was called with a map where the interface takes positional arguments, and the error was swallowed by the fallback clause. One bulk read replaces the query-per-system, which fixes that and drops the export to a single signature query. Also: the 422 for an unresolvable map owner could not fire, because the plug assigns nil rather than leaving the keys off - a guard makes the fallback reachable. Signatures are attributed to the character running the import rather than carrying an id from another instance. Export asks for view permission explicitly. The signature option is now on the dialog instead of being supported everywhere and reachable from nowhere, and an import confirms the counts before writing to a shared map. Covered by an integration test: round trip into an empty map, a second import as a no-op, and a deleted system coming back with its attributes.
246 lines
7.5 KiB
Elixir
246 lines
7.5 KiB
Elixir
defmodule WandererApp.Map.MapTransferTest do
|
|
@moduledoc """
|
|
The export/import round trip.
|
|
|
|
Covers the three things the shape of this feature rests on: what comes out of a map goes back
|
|
into an empty one, running the same document twice adds nothing the second time, and a system
|
|
deleted from the target comes back with the attributes the document carries for it.
|
|
"""
|
|
|
|
use WandererApp.DataCase
|
|
|
|
import WandererApp.MapTestHelpers
|
|
|
|
alias WandererApp.Api.MapSystemSignature
|
|
alias WandererApp.Map.Operations.Transfer
|
|
alias WandererApp.Map.Server
|
|
|
|
@system_a 31_000_101
|
|
@system_b 31_000_102
|
|
|
|
setup do
|
|
setup_transfer_test_systems()
|
|
:ok
|
|
end
|
|
|
|
describe "export/2 then import/5" do
|
|
test "carries systems, their attributes, connections and signatures into an empty map" do
|
|
source = start_test_map()
|
|
target = start_test_map()
|
|
|
|
seed_source_map(source)
|
|
|
|
{:ok, document} = Transfer.export(source.map_id)
|
|
|
|
assert document["version"] == 1
|
|
assert length(document["systems"]) == 2
|
|
assert length(document["connections"]) == 1
|
|
assert length(document["signatures"]) == 1
|
|
|
|
{:ok, stats} =
|
|
Transfer.import(target.map_id, document, target.user_id, target.character_id)
|
|
|
|
assert stats == %{systems: 2, connections: 1, signatures: 1}
|
|
|
|
assert wait_for_system_on_map(target.map_id, @system_a)
|
|
assert wait_for_system_on_map(target.map_id, @system_b)
|
|
|
|
{:ok, system} =
|
|
WandererApp.MapSystemRepo.get_by_map_and_solar_system_id(target.map_id, @system_a)
|
|
|
|
assert system.custom_name == "Home"
|
|
assert system.description == "staging"
|
|
assert system.tag == "A"
|
|
assert system.status == 1
|
|
assert system.temporary_name == "HS-1"
|
|
assert system.position_x == 120
|
|
assert system.position_y == 240
|
|
|
|
{:ok, connections} = WandererApp.MapConnectionRepo.get_by_map(target.map_id)
|
|
assert length(connections) == 1
|
|
|
|
{:ok, signatures} = MapSystemSignature.by_system_id(system.id)
|
|
assert [%{eve_id: "ABC-123", name: "Wormhole"}] = signatures
|
|
|
|
cleanup_test_data(source.map_id)
|
|
cleanup_test_data(target.map_id)
|
|
end
|
|
|
|
test "a second import of the same document adds nothing and says so" do
|
|
source = start_test_map()
|
|
target = start_test_map()
|
|
|
|
seed_source_map(source)
|
|
|
|
{:ok, document} = Transfer.export(source.map_id)
|
|
|
|
{:ok, _first} =
|
|
Transfer.import(target.map_id, document, target.user_id, target.character_id)
|
|
|
|
assert wait_for_system_on_map(target.map_id, @system_a)
|
|
|
|
{:ok, second} =
|
|
Transfer.import(target.map_id, document, target.user_id, target.character_id)
|
|
|
|
# the counts are the only feedback anyone gets about an import being safe to re-run, so a
|
|
# no-op has to report zeroes rather than the size of the document
|
|
assert second == %{systems: 0, connections: 0, signatures: 0}
|
|
|
|
cleanup_test_data(source.map_id)
|
|
cleanup_test_data(target.map_id)
|
|
end
|
|
|
|
test "a system deleted from the target comes back with its attributes" do
|
|
source = start_test_map()
|
|
target = start_test_map()
|
|
|
|
seed_source_map(source)
|
|
|
|
{:ok, document} = Transfer.export(source.map_id)
|
|
{:ok, _} = Transfer.import(target.map_id, document, target.user_id, target.character_id)
|
|
|
|
assert wait_for_system_on_map(target.map_id, @system_a)
|
|
|
|
:ok = Server.delete_systems(target.map_id, [@system_a], target.user_id, target.character_id)
|
|
|
|
refute system_visible?(target.map_id, @system_a)
|
|
|
|
{:ok, stats} =
|
|
Transfer.import(target.map_id, document, target.user_id, target.character_id)
|
|
|
|
# deletion leaves the row behind with visible: false, so counting rows rather than systems
|
|
# on the map used to treat this one as already present - it came back stripped
|
|
assert stats.systems == 1
|
|
|
|
assert wait_for_system_on_map(target.map_id, @system_a)
|
|
|
|
{:ok, system} =
|
|
WandererApp.MapSystemRepo.get_by_map_and_solar_system_id(target.map_id, @system_a)
|
|
|
|
assert system.custom_name == "Home"
|
|
assert system.tag == "A"
|
|
assert system.status == 1
|
|
assert system.temporary_name == "HS-1"
|
|
|
|
cleanup_test_data(source.map_id)
|
|
cleanup_test_data(target.map_id)
|
|
end
|
|
end
|
|
|
|
describe "import/5 with a document it cannot read" do
|
|
test "refuses another version and anything that is not a document" do
|
|
%{map_id: map_id, user_id: user_id, character_id: character_id} = start_test_map()
|
|
|
|
assert {:error, {:unsupported_version, 99}} =
|
|
Transfer.import(map_id, %{"version" => 99}, user_id, character_id)
|
|
|
|
assert {:error, :invalid_document} = Transfer.import(map_id, %{}, user_id, character_id)
|
|
|
|
cleanup_test_data(map_id)
|
|
end
|
|
end
|
|
|
|
defp seed_source_map(%{map_id: map_id, user_id: user_id, character_id: character_id}) do
|
|
:ok =
|
|
Server.add_system(
|
|
map_id,
|
|
%{solar_system_id: @system_a, coordinates: %{"x" => 120, "y" => 240}},
|
|
user_id,
|
|
character_id
|
|
)
|
|
|
|
:ok =
|
|
Server.add_system(
|
|
map_id,
|
|
%{solar_system_id: @system_b, coordinates: %{"x" => 300, "y" => 240}},
|
|
user_id,
|
|
character_id
|
|
)
|
|
|
|
assert wait_for_system_on_map(map_id, @system_a)
|
|
assert wait_for_system_on_map(map_id, @system_b)
|
|
|
|
Server.update_system_custom_name(map_id, %{solar_system_id: @system_a, custom_name: "Home"})
|
|
|
|
Server.update_system_description(map_id, %{solar_system_id: @system_a, description: "staging"})
|
|
|
|
Server.update_system_tag(map_id, %{solar_system_id: @system_a, tag: "A"})
|
|
Server.update_system_status(map_id, %{solar_system_id: @system_a, status: 1})
|
|
|
|
Server.update_system_temporary_name(map_id, %{
|
|
solar_system_id: @system_a,
|
|
temporary_name: "HS-1"
|
|
})
|
|
|
|
:ok =
|
|
Server.add_connection(
|
|
map_id,
|
|
%{
|
|
solar_system_source_id: @system_a,
|
|
solar_system_target_id: @system_b,
|
|
character_id: character_id
|
|
}
|
|
)
|
|
|
|
{:ok, system} = WandererApp.MapSystemRepo.get_by_map_and_solar_system_id(map_id, @system_a)
|
|
|
|
{:ok, _} =
|
|
MapSystemSignature.create(%{
|
|
system_id: system.id,
|
|
eve_id: "ABC-123",
|
|
character_eve_id: "90000001",
|
|
name: "Wormhole",
|
|
kind: "cosmic_signature",
|
|
group: "Wormhole"
|
|
})
|
|
|
|
:ok
|
|
end
|
|
|
|
defp system_visible?(map_id, solar_system_id) do
|
|
case WandererApp.MapSystemRepo.get_visible_by_map(map_id) do
|
|
{:ok, systems} -> Enum.any?(systems, &(&1.solar_system_id == solar_system_id))
|
|
_ -> false
|
|
end
|
|
end
|
|
|
|
defp start_test_map do
|
|
setup_ddrt_mocks()
|
|
|
|
user = insert(:user)
|
|
character = insert(:character, %{user_id: user.id})
|
|
map = insert(:map, %{owner_id: character.id})
|
|
|
|
:ok = ensure_map_started(map.id)
|
|
|
|
%{map_id: map.id, user_id: user.id, character_id: character.id}
|
|
end
|
|
|
|
defp setup_transfer_test_systems do
|
|
setup_system_static_info_cache(%{
|
|
@system_a => %{
|
|
solar_system_id: @system_a,
|
|
solar_system_name: "J101101",
|
|
solar_system_name_lc: "j101101",
|
|
region_id: 11_000_001,
|
|
constellation_id: 21_000_001,
|
|
region_name: "A-R00001",
|
|
constellation_name: "A-C00001",
|
|
system_class: 1,
|
|
security: "-1.0"
|
|
},
|
|
@system_b => %{
|
|
solar_system_id: @system_b,
|
|
solar_system_name: "J101102",
|
|
solar_system_name_lc: "j101102",
|
|
region_id: 11_000_001,
|
|
constellation_id: 21_000_001,
|
|
region_name: "A-R00001",
|
|
constellation_name: "A-C00001",
|
|
system_class: 2,
|
|
security: "-1.0"
|
|
}
|
|
})
|
|
end
|
|
end
|