From 6471950012776aeb5abd7a375fd5bd7a7daca7fc Mon Sep 17 00:00:00 2001 From: Natalie Rose Date: Sat, 29 Aug 2026 12:07:21 +1000 Subject: [PATCH] Starting to sort out the test performance issue --- bin/lacuna.psgi | 8 + docker-compose.yml | 3 + lib/Lacuna/TestSetup.pm | 502 +++++++++++++++++++++++++++++++++ lib/Lacuna/Web/TestSetup.pm | 158 +++++++++++ t/030_Body.t | 9 +- t/050_Map.t | 13 +- t/060_Stats.t | 9 +- t/130_Embassy.t | 9 +- t/150_FoodReserve.t | 56 ++-- t/160_Inbox.t | 9 +- t/190_MissionCommand.t | 9 +- t/210_OreStorage.t | 10 +- t/230_PlanetaryCommand.t | 20 +- t/390_Distribution.t | 33 +-- t/570_TestSetupApi.t | 53 ++++ t/TestClient.pm | 220 +++++++++++++++ t/TestHelper.pm | 2 +- t/bugs/0023_EmpireNameChecks.t | 7 +- 18 files changed, 1026 insertions(+), 104 deletions(-) create mode 100644 lib/Lacuna/TestSetup.pm create mode 100644 lib/Lacuna/Web/TestSetup.pm create mode 100644 t/570_TestSetupApi.t create mode 100644 t/TestClient.pm diff --git a/bin/lacuna.psgi b/bin/lacuna.psgi index 52a2b90c..5c877de9 100644 --- a/bin/lacuna.psgi +++ b/bin/lacuna.psgi @@ -257,6 +257,14 @@ my $app = builder { mount $building->new->to_app_with_url; } + # Dev-only test setup/teardown endpoints. Gated on LACUNA_TEST_SETUP_API, + # which docker-compose.yml sets for lacuna-server and + # docker-compose.deployed.yml deliberately does not -- so /test simply does + # not exist in production. See lib/Lacuna/Web/TestSetup.pm. + if ($ENV{LACUNA_TEST_SETUP_API}) { + mount "/test" => Lacuna::Web::TestSetup->new->to_app; + } + mount '/admin' => builder { enable "Auth::Basic", authenticator => sub { my ($username, $password) = @_; diff --git a/docker-compose.yml b/docker-compose.yml index a9c79cbf..e4a0997a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -163,6 +163,9 @@ services: environment: - 'TLE_NO_MIDDLEWARE=1' - 'LACUNA_HIGH_SPEED_TEST_MODE=1' + # Mounts the dev-only /test setup/teardown API (lib/Lacuna/Web/TestSetup.pm). + # Deliberately absent from docker-compose.deployed.yml. + - 'LACUNA_TEST_SETUP_API=1' ports: - '3050:5000' volumes: diff --git a/lib/Lacuna/TestSetup.pm b/lib/Lacuna/TestSetup.pm new file mode 100644 index 00000000..437d8c55 --- /dev/null +++ b/lib/Lacuna/TestSetup.pm @@ -0,0 +1,502 @@ +package Lacuna::TestSetup; + +# Server-side implementation of the t/TestHelper.pm setup/teardown routines. +# +# The Perl integration suite is slow mostly because every test does `use +# TestHelper`, which does `use Lacuna`, which `Module::Find::useall`s the whole +# app before the first assertion. The running plackup server already has all of +# that compiled, so we expose the setup/teardown steps as functions here, called +# over HTTP by Lacuna::Web::TestSetup, and let "naked" tests drive them with a +# lightweight LWP-only client (t/TestClient.pm) instead. +# +# These functions are deliberately near-verbatim ports of the matching +# t/TestHelper.pm methods -- keep them in sync. + +use strict; +use warnings; +use Lacuna; +use DateTime; +use Carp qw(confess); + +use constant EMPIRE_NAME => 'TLE Test Empire'; +use constant EMPIRE_PASSWORD => '123qwe'; +use constant CLEAR_PATTERN => 'TLE Test%'; + +sub _empire_rs { Lacuna->db->resultset('Lacuna::DB::Result::Empire') } +sub _building_rs { Lacuna->db->resultset('Lacuna::DB::Result::Building') } +sub _ships_rs { Lacuna->db->resultset('Lacuna::DB::Result::Ships') } + +# --- lookups -------------------------------------------------------------- + +sub empire_by_id { + my ($id) = @_; + confess [404, 'empire_id required'] unless $id; + my $empire = _empire_rs()->find($id); + confess [404, "empire $id not found"] unless $empire; + return $empire; +} + +sub _planet_for { + my ($args) = @_; + if ($args->{planet_id}) { + my $planet = Lacuna->db->resultset('Lacuna::DB::Result::Map::Body')->find($args->{planet_id}); + confess [404, "planet $args->{planet_id} not found"] unless $planet; + return $planet; + } + return empire_by_id($args->{empire_id})->home_planet; +} + +# --- empire lifecycle --------------------------------------------------------- + +# Mirrors TestHelper::generate_test_empire. Deletes any same-named empire first, +# inserts a fresh one, founds it (picks a home planet), starts a session. +sub generate_empire { + my (%args) = @_; + my $name = $args{name} || EMPIRE_NAME; + my $password = $args{password} || EMPIRE_PASSWORD; + + my $empires = _empire_rs()->search({ name => $name }); + while (my $e = $empires->next) { $e->delete } + + my $empire = _empire_rs()->new({ + name => $name, + date_created => DateTime->now, + status_message => 'Making Lacuna a better Expanse.', + password => Lacuna::DB::Result::Empire->encrypt_password($password), + })->insert; + $empire->found; + + my $session = $empire->start_session({ api_key => 'tester' }); + + return _empire_payload($empire, $session, $password); +} + +# Mirrors TestHelper::use_existing_test_empire. Reuse the persistent shared test +# empire if it is already around; otherwise create it and build the big colony. +sub use_existing_empire { + my (%args) = @_; + my $name = $args{name} || EMPIRE_NAME; + + my ($empire) = _empire_rs()->search({ name => $name })->all; + if (not $empire) { + my $payload = generate_empire(name => $name); + $empire = _empire_rs()->find($payload->{empire_id}); + build_big_colony(empire_id => $empire->id); + } + my $session = $empire->start_session({ api_key => 'tester' }); + return _empire_payload($empire, $session, $args{password} || EMPIRE_PASSWORD); +} + +sub _empire_payload { + my ($empire, $session, $password) = @_; + return { + empire_id => $empire->id, + empire_name => $empire->name, + password => $password, + session_id => $session->id, + home_planet_id => $empire->home_planet_id, + }; +} + +# Mirrors TestHelper::clear_all_test_empires / cleanup. +sub clear_empires { + my (%args) = @_; + my $name = $args{name} || CLEAR_PATTERN; + + my $empires = _empire_rs()->search({ name => { like => $name } }); + my $deleted = 0; + while (my $empire = $empires->next) { + $empire->essentia_game(0); + $empire->essentia_free(0); + $empire->essentia_paid(0); + $empire->update; + + my $planets = $empire->planets; + while (my $planet = $planets->next) { + my @buildings = grep { $_->class =~ /Permanent/ } @{$planet->building_cache}; + $planet->delete_buildings(\@buildings); + } + + $empire->delete; + $deleted++; + } + return { deleted => $deleted }; +} + +# --- buildings -------------------------------------------------------------- + +# Mirrors TestHelper::find_empty_plot: walk the -5..5 grid for a free plot, +# tracking the cursor in $cursor so repeated calls advance like TestHelper does. +sub _next_free_plot { + my ($home, $cursor) = @_; + $cursor->{x} = -5 unless defined $cursor->{x}; + $cursor->{y} = -5 unless defined $cursor->{y}; + while (1) { + my ($occupied) = grep { $_->x == $cursor->{x} && $_->y == $cursor->{y} } @{$home->building_cache}; + last unless $occupied; + $cursor->{x}++; + if ($cursor->{x} == 6) { + $cursor->{x} = -5; + $cursor->{y}++; + } + } + return ($cursor->{x}, $cursor->{y}); +} + +# Mirrors TestHelper::build_building. Builds $class at $level (finished), on a +# free plot (or the given x/y). Returns the new building's id/coords/level. +sub build_building { + my (%args) = @_; + my $home = _planet_for(\%args); + my $class = $args{class} or confess [400, 'class required']; + my $level = $args{level} || 1; + + my ($x, $y); + if (defined $args{x} && defined $args{y}) { + ($x, $y) = ($args{x}, $args{y}); + } + else { + ($x, $y) = _next_free_plot($home, {}); + } + + my $building = _building_rs()->new({ + x => $x, + y => $y, + class => $class, + level => $level - 1, + }); + $home->build_building($building); + $building->finish_upgrade; + + return { building_id => $building->id, x => $x, y => $y, level => $building->level }; +} + +# Mirrors `$tester->get_building($id)->finish_upgrade`. With building_id, finish +# that one; with planet_id, finish every pending upgrade on the body. +sub finish_building { + my (%args) = @_; + if ($args{building_id}) { + my $building = _building_rs()->find($args{building_id}); + confess [404, "building $args{building_id} not found"] unless $building; + $building->finish_upgrade; + return { ok => 1, finished => 1 }; + } + if ($args{planet_id}) { + my $pending = _building_rs()->search({ body_id => $args{planet_id}, is_upgrading => 1 }); + my $n = 0; + while (my $b = $pending->next) { $b->finish_upgrade; $n++ } + return { ok => 1, finished => $n }; + } + confess [400, 'building_id or planet_id required']; +} + +# Mirrors TestHelper::finish_ships. +sub finish_ships { + my (%args) = @_; + my $shipyard_id = $args{shipyard_id} or confess [400, 'shipyard_id required']; + _ships_rs()->search({ shipyard_id => $shipyard_id }) + ->update({ date_available => DateTime->now, task => 'Docked' }); + return { ok => 1 }; +} + +# --- body resource poking -------------------------------------------------- + +# Mirrors the `$home->ore_capacity(...); $home->bauxite_stored(...); ...; +# $home->needs_recalc(0); $home->update; $home->tick;` blocks that many tests +# hand-write after building a lone University. Only whitelisted columns. +my %RESOURCE_COLS = map { $_ => 1 } qw( + ore_capacity energy_capacity food_capacity water_capacity + ore_hour energy_hour water_hour algae_production_hour + happiness needs_recalc + bauxite_stored algae_stored energy_stored water_stored + beryl_stored chromite_stored chalcopyrite_stored galena_stored goethite_stored + gold_stored gypsum_stored halite_stored kerogen_stored magnetite_stored + methane_stored monazite_stored rutile_stored sulfur_stored trona_stored + uraninite_stored zircon_stored anthracite_stored bread_stored burger_stored + cheese_stored chip_stored cider_stored corn_stored fungus_stored lapis_stored + meal_stored milk_stored pancake_stored pie_stored potato_stored root_stored + shake_stored soup_stored syrup_stored wheat_stored beetle_stored apple_stored + bean_stored +); + +sub set_body_resources { + my (%args) = @_; + my $home = _planet_for(\%args); + my $tick = delete $args{tick}; + delete @args{qw(planet_id empire_id)}; + + for my $col (keys %args) { + confess [400, "column '$col' is not settable via set_body_resources"] + unless $RESOURCE_COLS{$col}; + $home->$col($args{$col}); + } + $home->update; + $home->tick if $tick; + return { ok => 1 }; +} + +# --- infrastructure ------------------------------------------------------- + +# Mirrors TestHelper::build_infrastructure. +sub build_infrastructure { + my (%args) = @_; + my $empire = empire_by_id($args{empire_id}); + my $home = $empire->home_planet; + my $cursor = {}; + + my $build = sub { + my ($type, $level) = @_; + my ($x, $y) = _next_free_plot($home, $cursor); + my $building = _building_rs()->new({ x => $x, y => $y, class => $type, level => $level - 1 }); + $home->build_building($building); + $building->finish_upgrade; + }; + + for my $type ( + 'Lacuna::DB::Result::Building::Food::Algae', + 'Lacuna::DB::Result::Building::Energy::Hydrocarbon', + 'Lacuna::DB::Result::Building::Water::Purification', + 'Lacuna::DB::Result::Building::Water::Purification', + 'Lacuna::DB::Result::Building::Water::Purification', + 'Lacuna::DB::Result::Building::Ore::Mine', + 'Lacuna::DB::Result::Building::Ore::Mine', + 'Lacuna::DB::Result::Building::Ore::Mine', + 'Lacuna::DB::Result::Building::Food::Algae', + 'Lacuna::DB::Result::Building::Food::Algae', + 'Lacuna::DB::Result::Building::Energy::Hydrocarbon', + 'Lacuna::DB::Result::Building::Energy::Hydrocarbon', + ) { + $build->($type, 20); + } + + $home->empire->university_level(30); + $home->empire->update; + + for my $type ( + 'Lacuna::DB::Result::Building::Energy::Reserve', + 'Lacuna::DB::Result::Building::Food::Reserve', + 'Lacuna::DB::Result::Building::Ore::Storage', + 'Lacuna::DB::Result::Building::Water::Storage', + ) { + $build->($type, 20); + } + + if ($args{big_producer}) { + $home->ore_hour(50000000); + $home->water_hour(50000000); + $home->energy_hour(50000000); + $home->algae_production_hour(50000000); + $home->ore_capacity(50000000); + $home->energy_capacity(50000000); + $home->food_capacity(50000000); + $home->water_capacity(50000000); + $home->bauxite_stored(50000000); + $home->algae_stored(50000000); + $home->energy_stored(50000000); + $home->water_stored(50000000); + $home->add_happiness(50000000); + $home->monazite_stored(5000000); + } + else { + $home->algae_stored(100_000); + $home->bauxite_stored(100_000); + $home->energy_stored(100_000); + $home->water_stored(100_000); + } + + $home->tick; + return { ok => 1 }; +} + +# --- big colony --------------------------------------------------------- + +# Mirrors TestHelper::build_big_colony. Wipes the planet, lays out a full +# 30-level colony, seeds fleets (via the Shipyard RPC, in-process) and 1000 +# spies. Used by use_existing_empire's cold-start path. +sub build_big_colony { + my (%args) = @_; + my $empire = empire_by_id($args{empire_id}); + my $planet = $args{planet_id} + ? Lacuna->db->resultset('Lacuna::DB::Result::Map::Body')->find($args{planet_id}) + : $empire->home_planet; + my $session = $empire->start_session({ api_key => 'tester' }); + + $planet->delete_buildings([ @{$planet->building_cache} ]); + $planet->ships->delete_all; + Lacuna->db->resultset('Spies')->search({ from_body_id => $planet->id })->delete_all; + + my $layout = _big_colony_layout(); + + for my $class (keys %$layout) { + for my $location (@{$layout->{$class}}) { + my $building = _building_rs()->new({ + x => $location->{x}, + y => $location->{y}, + class => "Lacuna::DB::Result::Building::$class", + level => $location->{level} - 1, + }); + $planet->build_building($building); + $building->finish_upgrade; + } + } + + $planet->bauxite_stored(19_000_000_000); + $planet->algae_stored(19_000_000_000); + $planet->energy_stored(19_000_000_000); + $planet->water_stored(19_000_000_000); + $planet->needs_recalc(1); + $planet->update; + $planet->tick; + + my ($shipyard) = grep { $_->class eq 'Lacuna::DB::Result::Building::Shipyard' } @{$planet->building_cache}; + my $shipyard_rpc = Lacuna::RPC::Building::Shipyard->new; + + my $ships = { + excavator => 30, + probe => 20, + sweeper => 1300, + fighter => 500, + hulk => 250, + detonator => 20, + security_ministry_seeker => 20, + smuggler_ship => 20, + snark3 => 200, + spy_pod => 20, + spy_shuttle => 20, + supply_pod4 => 10, + }; + for my $ship (keys %$ships) { + my $result = $shipyard_rpc->build_ship($session->id, $shipyard->id, $ship); + finish_ships(shipyard_id => $shipyard->id); + next unless $ships->{$ship} > 1; + + my $ship_id = $result->{ships_building}[0]{id}; + my $example = _ships_rs()->find($ship_id); + for (1 .. $ships->{$ship}) { + _ships_rs()->create({ + body_id => $example->body_id, + shipyard_id => $example->shipyard_id, + date_started => $example->date_started, + date_available => $example->date_available, + type => $example->type, + task => $example->task, + name => $example->name, + speed => $example->speed, + stealth => $example->stealth, + combat => $example->combat, + hold_size => $example->hold_size, + payload => $example->payload, + roundtrip => $example->roundtrip, + direction => $example->direction, + foreign_body_id => $example->foreign_body_id, + foreign_star_id => $example->foreign_star_id, + fleet_speed => $example->fleet_speed, + }); + } + } + + for (1 .. 1000) { + my $spy = $empire->add_to_spies({ + name => 'Null', + from_body_id => $planet->id, + on_body_id => $planet->id, + task => 'Counter Espionage', + started_assignment => '2012-01-01 12:00:00', + available_on => '2012-01-01 12:00:00', + offense => 2600, + defense => 2600, + date_created => DateTime->now(), + offense_mission_count => 10 + int(rand(10)), + defense_mission_count => 10 + int(rand(10)), + offense_mission_successes => int(rand(3)), + defense_mission_successes => int(rand(3)), + times_captured => 0, + times_turned => 0, + seeds_planted => 0, + spies_turned => 0, + spies_captured => 0, + spies_killed => 0, + things_destroyed => int(rand(3)), + things_stolen => int(rand(5)), + intel_xp => 95 + int(rand(5)), + mayhem_xp => 95 + int(rand(5)), + politics_xp => 95 + int(rand(5)), + theft_xp => 95 + int(rand(5)), + level => 27 + int(rand(10)), + }); + $spy->name("ICY " . $spy->id); + $spy->update; + } + + return { ok => 1 }; +} + +sub _big_colony_layout { + return { + 'PlanetaryCommand' => [ { x => 0, y => 0, level => 30 } ], + 'SpacePort' => [ + (map { { x => $_->[0], y => $_->[1], level => 22 } } + [-2,-2],[-1,-2],[0,-2],[1,-2],[2,-2], + [-2,-1],[-1,-1],[0,-1],[1,-1],[2,-1], + [-2,0],[-1,0],[1,0],[2,0], + [-2,1],[-1,1],[0,1],[1,1],[2,1], + [-2,2],[-1,2],[0,2],[1,2],[2,2], + [4,-4],[4,-3],[4,-2],[4,-1],[4,0],[4,1],[4,2],[4,3],[4,4], + [-4,-4],[-4,-3],[-4,-2],[-4,-1],[-4,0],[-4,1],[-4,2],[-4,3],[-4,4], + [-3,-4],[-2,-4],[-1,-4],[0,-4],[1,-4],[2,-4],[3,-4], + [-3,4],[-2,4],[-1,4],[0,4],[1,4],[2,4],[3,4], + ), + ], + 'Permanent::Volcano' => [ { x => 1, y => -3, level => 30 } ], + 'Permanent::InterDimensionalRift' => [ { x => 2, y => -3, level => 30 } ], + 'Archaeology' => [ { x => 3, y => -3, level => 30 } ], + 'Development' => [ { x => 0, y => -3, level => 30 } ], + 'Permanent::GeoThermalVent' => [ { x => -1, y => -3, level => 30 } ], + 'Espionage' => [ { x => -2, y => -3, level => 30 } ], + 'Security' => [ { x => -3, y => -3, level => 30 } ], + 'Intelligence' => [ { x => -3, y => -2, level => 30 } ], + 'Embassy' => [ { x => -3, y => 0, level => 30 } ], + 'Trade' => [ { x => 0, y => 3, level => 30 } ], + 'Transporter' => [ { x => 3, y => 0, level => 30 } ], + 'University' => [ { x => 3, y => -1, level => 30 } ], + 'Observatory' => [ { x => 3, y => -2, level => 30 } ], + 'TheftTraining' => [ { x => -3, y => 1, level => 30 } ], + 'IntelTraining' => [ { x => -3, y => 2, level => 30 } ], + 'MayhemTraining' => [ { x => -3, y => 3, level => 30 } ], + 'PoliticsTraining' => [ { x => -2, y => 3, level => 30 } ], + 'Permanent::NaturalSpring' => [ { x => -1, y => 3, level => 30 } ], + 'Permanent::CrashedShipSite' => [ { x => 1, y => 3, level => 30 } ], + 'CloakingLab' => [ { x => 2, y => 3, level => 30 } ], + 'MunitionsLab' => [ { x => 3, y => 3, level => 30 } ], + 'PilotTraining' => [ { x => 3, y => 2, level => 30 } ], + 'Propulsion' => [ { x => 3, y => 1, level => 30 } ], + 'Permanent::DentonBrambles' => [ { x => -5, y => 2, level => 30 } ], + 'Permanent::AlgaePond' => [ { x => -5, y => 1, level => 30 } ], + 'MercenariesGuild' => [ { x => -3, y => -1, level => 30 } ], + 'Permanent::SpaceJunkPark' => [ { x => -5, y => -1, level => 30 } ], + 'Permanent::PyramidJunkSculpture' => [ { x => -5, y => -2, level => 30 } ], + 'Permanent::GratchsGauntlet' => [ { x => 5, y => 2, level => 30 } ], + 'Permanent::Ravine' => [ { x => 5, y => 1, level => 30 } ], + 'Permanent::JunkHengeSculpture' => [ { x => 5, y => -1, level => 30 } ], + 'Permanent::MetalJunkArches' => [ { x => 5, y => -2, level => 30 } ], + 'Permanent::GasGiantPlatform' => [ + { x => 5, y => 5, level => 30 }, { x => 5, y => -5, level => 30 }, + { x => -5, y => 5, level => 30 }, { x => -5, y => -5, level => 30 }, + ], + 'Shipyard' => [ + { x => -2, y => 5, level => 30 }, { x => -1, y => 5, level => 30 }, + { x => 1, y => 5, level => 30 }, { x => 2, y => 5, level => 30 }, + { x => -2, y => -5, level => 30 },{ x => -1, y => -5, level => 30 }, + { x => 1, y => -5, level => 30 }, { x => 2, y => -5, level => 30 }, + ], + 'SAW' => [ + { x => -4, y => 5, level => 30 }, { x => 4, y => 5, level => 30 }, + { x => -4, y => -5, level => 30 },{ x => 4, y => -5, level => 30 }, + { x => -5, y => 4, level => 30 }, { x => 5, y => 4, level => 30 }, + { x => -5, y => -4, level => 30 },{ x => 5, y => -4, level => 30 }, + { x => 0, y => 5, level => 30 }, { x => 0, y => -5, level => 30 }, + ], + }; +} + +1; diff --git a/lib/Lacuna/Web/TestSetup.pm b/lib/Lacuna/Web/TestSetup.pm new file mode 100644 index 00000000..a75bfe37 --- /dev/null +++ b/lib/Lacuna/Web/TestSetup.pm @@ -0,0 +1,158 @@ +package Lacuna::Web::TestSetup; + +# Dev-only HTTP faces for Lacuna::TestSetup, mounted at /test by bin/lacuna.psgi +# only when $ENV{LACUNA_TEST_SETUP_API} is set (see docker-compose.yml -- never +# in docker-compose.deployed.yml). Lets the lightweight t/TestClient.pm run test +# setup/teardown without `use Lacuna` compiling the whole app per test file. + +use Moose; +use utf8; +no warnings qw(uninitialized); +extends qw(Lacuna::Web); +use JSON qw(encode_json decode_json); + +sub _guard { + confess [403, 'Test setup endpoints are disabled (LACUNA_TEST_SETUP_API unset).'] + unless $ENV{LACUNA_TEST_SETUP_API}; +} + +# Read args from a JSON request body, falling back to form/query params. +sub _args { + my ($self, $request) = @_; + my $content = $request->content; + if (defined $content && length $content) { + my $decoded = eval { decode_json($content) }; + confess [400, "Invalid JSON body: $@"] if $@; + confess [400, 'JSON body must be an object'] unless ref $decoded eq 'HASH'; + return $decoded; + } + my %params; + for my $key ($request->parameters->keys) { + $params{$key} = $request->parameters->get($key); + } + return \%params; +} + +sub _json { + my ($self, $data) = @_; + return [ encode_json($data), { content_type => 'application/json' } ]; +} + +sub www_default { + my ($self, $request) = @_; + $self->_guard; + return $self->_json({ + ok => 1, + endpoints => [ qw( + generate_empire use_existing_empire build_infrastructure build_big_colony + build_building finish_building finish_ships set_body_resources + prime_captcha clear_empires + ) ], + }); +} + +sub www_generate_empire { + my ($self, $request) = @_; + $self->_guard; + my $args = $self->_args($request); + return $self->_json(Lacuna::TestSetup::generate_empire( + name => $args->{name}, + password => $args->{password}, + )); +} + +sub www_use_existing_empire { + my ($self, $request) = @_; + $self->_guard; + my $args = $self->_args($request); + return $self->_json(Lacuna::TestSetup::use_existing_empire( + name => $args->{name}, + password => $args->{password}, + )); +} + +sub www_build_infrastructure { + my ($self, $request) = @_; + $self->_guard; + my $args = $self->_args($request); + return $self->_json(Lacuna::TestSetup::build_infrastructure( + empire_id => $args->{empire_id}, + big_producer => $args->{big_producer}, + )); +} + +sub www_build_big_colony { + my ($self, $request) = @_; + $self->_guard; + my $args = $self->_args($request); + return $self->_json(Lacuna::TestSetup::build_big_colony( + empire_id => $args->{empire_id}, + planet_id => $args->{planet_id}, + )); +} + +sub www_build_building { + my ($self, $request) = @_; + $self->_guard; + my $args = $self->_args($request); + return $self->_json(Lacuna::TestSetup::build_building( + empire_id => $args->{empire_id}, + planet_id => $args->{planet_id}, + class => $args->{class}, + level => $args->{level}, + x => $args->{x}, + y => $args->{y}, + )); +} + +sub www_finish_building { + my ($self, $request) = @_; + $self->_guard; + my $args = $self->_args($request); + return $self->_json(Lacuna::TestSetup::finish_building( + building_id => $args->{building_id}, + planet_id => $args->{planet_id}, + )); +} + +sub www_finish_ships { + my ($self, $request) = @_; + $self->_guard; + my $args = $self->_args($request); + return $self->_json(Lacuna::TestSetup::finish_ships( + shipyard_id => $args->{shipyard_id}, + )); +} + +sub www_set_body_resources { + my ($self, $request) = @_; + $self->_guard; + my $args = $self->_args($request); + return $self->_json(Lacuna::TestSetup::set_body_resources(%$args)); +} + +# Mirrors the inline `Lacuna->cache->set('captcha'/'create_empire_captcha', ...)` +# calls tests make before create/spy flows. +sub www_prime_captcha { + my ($self, $request) = @_; + $self->_guard; + my $args = $self->_args($request); + my $ttl = 60 * 15; + if ($args->{session_id}) { + Lacuna->cache->set('captcha', $args->{session_id}, { guid => 1111, solution => 1111 }, $ttl); + } + for my $ip ('127.0.0.1', '172.18.0.7', ($args->{ip} ? ($args->{ip}) : ())) { + Lacuna->cache->set('create_empire_captcha', $ip, { guid => 1111, solution => 1111 }, $ttl); + } + return $self->_json({ ok => 1 }); +} + +sub www_clear_empires { + my ($self, $request) = @_; + $self->_guard; + my $args = $self->_args($request); + return $self->_json(Lacuna::TestSetup::clear_empires(name => $args->{name})); +} + +no Moose; +__PACKAGE__->meta->make_immutable(inline_constructor => 0); diff --git a/t/030_Body.t b/t/030_Body.t index d7584a44..94183220 100644 --- a/t/030_Body.t +++ b/t/030_Body.t @@ -1,13 +1,12 @@ -use lib '../lib'; use Test::More tests => 15; use Test::Deep; use Data::Dumper; use 5.010; -use TestHelper; -TestHelper->clear_all_test_empires; +use TestClient; +TestClient->clear_all_test_empires; -my $tester = eval{TestHelper->new->generate_test_empire}; +my $tester = eval{TestClient->new->generate_test_empire}; if ($@) { die $@->[1]; } @@ -57,5 +56,5 @@ $result = $tester->post('body', 'get_buildable', [$session_id, $home_planet, 3, cmp_ok($result->{result}{buildable}{'Waste Energy Plant'}{production}{happiness_hour}, '>=', 0, 'no negative happiness from waste buildings'); END { - TestHelper->clear_all_test_empires; + TestClient->clear_all_test_empires; } diff --git a/t/050_Map.t b/t/050_Map.t index d62744ed..d1d490f9 100644 --- a/t/050_Map.t +++ b/t/050_Map.t @@ -1,21 +1,20 @@ -use lib '../lib'; use Test::More tests => 5; use Test::Deep; use Data::Dumper; use 5.010; -use TestHelper; -TestHelper->clear_all_test_empires; +use TestClient; +TestClient->clear_all_test_empires; -my $tester = TestHelper->new->generate_test_empire; +my $tester = TestClient->new->generate_test_empire; my $session_id = $tester->session->id; -my $home_planet = $tester->empire->home_planet; +my $home_planet_id = $tester->home_planet_id; my $result; -my $star_id = $home_planet->star_id; +my $star_id = $tester->post('body', 'get_status', [$session_id, $home_planet_id])->{result}{body}{star_id}; $result = $tester->post('map','get_stars',[$session_id, -3,-3,2,2]); is(ref $result->{result}{stars}, 'ARRAY', 'get stars'); @@ -33,5 +32,5 @@ is($result->{result}{incoming_probe}, 0, 'gcheck_star_for_incoming_probe'); END { - TestHelper->clear_all_test_empires; + TestClient->clear_all_test_empires; } diff --git a/t/060_Stats.t b/t/060_Stats.t index 9951df67..cd91cf99 100644 --- a/t/060_Stats.t +++ b/t/060_Stats.t @@ -1,13 +1,12 @@ -use lib '../lib'; use Test::More tests => 8; use Test::Deep; use Data::Dumper; use 5.010; -use TestHelper; -TestHelper->clear_all_test_empires; +use TestClient; +TestClient->clear_all_test_empires; -my $tester = TestHelper->new->generate_test_empire; +my $tester = TestClient->new->generate_test_empire; my $session_id = $tester->session->id; @@ -36,5 +35,5 @@ $result = $tester->post('stats', 'find_alliance_rank', [$session_id, 'average_em ok(exists $result->{result}, "find_alliance_rank"); END { - TestHelper->clear_all_test_empires; + TestClient->clear_all_test_empires; } diff --git a/t/130_Embassy.t b/t/130_Embassy.t index cb08d979..a823aeb3 100644 --- a/t/130_Embassy.t +++ b/t/130_Embassy.t @@ -1,14 +1,13 @@ -use lib '../lib'; use Test::More tests => 15; use Test::Deep; use Data::Dumper; use 5.010; use DateTime; -use TestHelper; -TestHelper->clear_all_test_empires; +use TestClient; +TestClient->clear_all_test_empires; -my $tester = TestHelper->new->generate_test_empire->build_infrastructure; +my $tester = TestClient->new->generate_test_empire->build_infrastructure; my $session_id = $tester->session->id; my $empire = $tester->empire; my $home = $empire->home_planet; @@ -63,5 +62,5 @@ $result = $tester->post('embassy', 'dissolve_alliance', [$session_id, $embassy-> ok(exists $result->{result}, 'dissolve alliance'); END { - TestHelper->clear_all_test_empires; + TestClient->clear_all_test_empires; } diff --git a/t/150_FoodReserve.t b/t/150_FoodReserve.t index 21a39219..6c0898a8 100644 --- a/t/150_FoodReserve.t +++ b/t/150_FoodReserve.t @@ -1,49 +1,41 @@ -use lib '../lib'; use Test::More tests => 1; use Test::Deep; use Data::Dumper; use 5.010; -use TestHelper; -TestHelper->clear_all_test_empires; +use TestClient; +TestClient->clear_all_test_empires; -my $tester = TestHelper->new->generate_test_empire; +my $tester = TestClient->new->generate_test_empire; my $session_id = $tester->session->id; -my $empire = $tester->empire; -my $home = $empire->home_planet; +my $home_id = $tester->home_planet_id; my $result; -my $uni = Lacuna->db->resultset('Lacuna::DB::Result::Building')->new({ - x => 0, - y => -1, - class => 'Lacuna::DB::Result::Building::University', - level => 1, -}); -$home->build_building($uni); -$uni->finish_upgrade; - -$home->ore_capacity(500000); -$home->energy_capacity(500000); -$home->food_capacity(500000); -$home->water_capacity(500000); -$home->bauxite_stored(500000); -$home->algae_stored(500000); -$home->energy_stored(500000); -$home->water_stored(500000); -$home->energy_hour(500000); -$home->algae_production_hour(500000); -$home->water_hour(500000); -$home->ore_hour(500000); -$home->needs_recalc(0); -$home->update; - -$result = $tester->post('foodreserve', 'build', [$session_id, $tester->empire->home_planet_id, 3, 3]); +$tester->build_building('Lacuna::DB::Result::Building::University', 2); + +$tester->set_body_resources($home_id, + ore_capacity => 500000, + energy_capacity => 500000, + food_capacity => 500000, + water_capacity => 500000, + bauxite_stored => 500000, + algae_stored => 500000, + energy_stored => 500000, + water_stored => 500000, + energy_hour => 500000, + algae_production_hour => 500000, + water_hour => 500000, + ore_hour => 500000, + needs_recalc => 0, +); + +$result = $tester->post('foodreserve', 'build', [$session_id, $home_id, 3, 3]); my $building_id = $result->{result}{building}{id}; $result = $tester->post('foodreserve', 'view', [$session_id, $building_id]); cmp_ok($result->{result}{food_stored}{algae}, '>', 0, "got food storage"); END { - TestHelper->clear_all_test_empires; + TestClient->clear_all_test_empires; } diff --git a/t/160_Inbox.t b/t/160_Inbox.t index c5205fd2..355d755b 100644 --- a/t/160_Inbox.t +++ b/t/160_Inbox.t @@ -1,14 +1,13 @@ -use lib '../lib'; use Test::More tests => 19; use Test::Deep; use Data::Dumper; use 5.010; -use TestHelper; -TestHelper->clear_all_test_empires; +use TestClient; +TestClient->clear_all_test_empires; -my $tester = TestHelper->new->generate_test_empire; +my $tester = TestClient->new->generate_test_empire; my $session_id = $tester->session->id; my $result; @@ -66,5 +65,5 @@ $result = $tester->post('inbox','send_message', [$session_id, $tester->empire_na is($result->{result}{message}{sent}[0], $tester->empire_name, 'you can send a message with double carriage return'); END { - TestHelper->clear_all_test_empires; + TestClient->clear_all_test_empires; } diff --git a/t/190_MissionCommand.t b/t/190_MissionCommand.t index ff6e475a..d89c7ae2 100644 --- a/t/190_MissionCommand.t +++ b/t/190_MissionCommand.t @@ -1,14 +1,13 @@ -use lib '../lib'; use Test::More tests => 3; use Test::Deep; use Data::Dumper; use 5.010; use DateTime; -use TestHelper; -TestHelper->clear_all_test_empires; +use TestClient; +TestClient->clear_all_test_empires; -my $tester = TestHelper->new->generate_test_empire->build_infrastructure; +my $tester = TestClient->new->generate_test_empire->build_infrastructure; my $session_id = $tester->session->id; my $empire = $tester->empire; my $home = $empire->home_planet; @@ -28,5 +27,5 @@ $result = $tester->post('missioncommand', 'complete_mission', [$session_id, $mc- is($result->{error}{code}, 1002, 'can call complete_mission'); END { - TestHelper->clear_all_test_empires; + TestClient->clear_all_test_empires; } diff --git a/t/210_OreStorage.t b/t/210_OreStorage.t index e9009b22..279378c7 100644 --- a/t/210_OreStorage.t +++ b/t/210_OreStorage.t @@ -1,16 +1,14 @@ -use lib '../lib'; use Test::More tests => 1; use Test::Deep; use Data::Dumper; use 5.010; -use TestHelper; -TestHelper->clear_all_test_empires; +use TestClient; +TestClient->clear_all_test_empires; -my $tester = TestHelper->new->generate_test_empire; +my $tester = TestClient->new->generate_test_empire; -my $db = Lacuna->db; my $empire = $tester->empire; my $session_id = $tester->session->id; @@ -23,5 +21,5 @@ $result = $tester->post('orestorage', 'view', [$session_id, $building_id]); ok(exists $result->{result}{ore_stored}{bauxite}, "got ore storage"); END { - TestHelper->clear_all_test_empires; + TestClient->clear_all_test_empires; } diff --git a/t/230_PlanetaryCommand.t b/t/230_PlanetaryCommand.t index 9dc70939..942f1467 100644 --- a/t/230_PlanetaryCommand.t +++ b/t/230_PlanetaryCommand.t @@ -1,27 +1,27 @@ -use lib '../lib'; use Test::More tests => 1; use Test::Deep; use Data::Dumper; use 5.010; -use TestHelper; -TestHelper->clear_all_test_empires; +use TestClient; +TestClient->clear_all_test_empires; -my $tester = TestHelper->new->generate_test_empire; +my $tester = TestClient->new->generate_test_empire; my $session_id = $tester->session->id; -my $empire = $tester->empire; -my $home = $empire->home_planet; -my $db = Lacuna->db; +my $home_planet_id = $tester->home_planet_id; my $result; -my $pcc = $home->command; +my $buildings = $tester->post('body', 'get_buildings', [$session_id, $home_planet_id]); +my ($pcc_id) = grep { + $buildings->{result}{buildings}{$_}{name} eq 'Planetary Command Center' +} keys %{$buildings->{result}{buildings}}; -$result = $tester->post('planetarycommand', 'view_plans', [$session_id, $pcc->id]); +$result = $tester->post('planetarycommand', 'view_plans', [$session_id, $pcc_id]); is(ref $result->{result}{plans}, 'ARRAY', 'can view plans'); END { - TestHelper->clear_all_test_empires; + TestClient->clear_all_test_empires; } diff --git a/t/390_Distribution.t b/t/390_Distribution.t index 49b2a45a..758eb4d4 100644 --- a/t/390_Distribution.t +++ b/t/390_Distribution.t @@ -1,28 +1,24 @@ -use lib '../lib'; use Test::More tests => 7; use Test::Deep; use Data::Dumper; use 5.010; -use TestHelper; -TestHelper->clear_all_test_empires; +use TestClient; +TestClient->clear_all_test_empires; -my $tester = TestHelper->new->generate_test_empire->build_infrastructure; -my $db = Lacuna->db; -my $empire = $tester->empire; +my $tester = TestClient->new->generate_test_empire->build_infrastructure; my $session_id = $tester->session->id; -my $home = $empire->home_planet; +my $home_id = $tester->home_planet_id; my $result; -$result = $tester->post('distributioncenter', 'build', [$session_id, $home->id, 3, 3]); +$result = $tester->post('distributioncenter', 'build', [$session_id, $home_id, 3, 3]); -my $building = $db->resultset('Lacuna::DB::Result::Building')->find($result->{result}{building}{id}); -$building->finish_upgrade; +my $building_id = $result->{result}{building}{id}; +$tester->finish_building($building_id); -$home->water_stored(500); -$home->update; +$tester->set_body_resources($home_id, water_stored => 500); -$result = $tester->post('distributioncenter', 'reserve', [$session_id, $building->id, [ +$result = $tester->post('distributioncenter', 'reserve', [$session_id, $building_id, [ { type => 'water', quantity => 10000, @@ -31,13 +27,12 @@ $result = $tester->post('distributioncenter', 'reserve', [$session_id, $building is($result->{error}{code}, 1009, "can't reserve resources you don't have"); -$result = $tester->post('distributioncenter', 'release_reserve', [$session_id, $building->id]); +$result = $tester->post('distributioncenter', 'release_reserve', [$session_id, $building_id]); is($result->{error}{code}, 1010, "can't release with nothing in reserve"); -$home->water_stored(20000); -$home->update; +$tester->set_body_resources($home_id, water_stored => 20000); -$result = $tester->post('distributioncenter', 'reserve', [$session_id, $building->id, [ +$result = $tester->post('distributioncenter', 'reserve', [$session_id, $building_id, [ { type => 'water', quantity => 10000, @@ -49,11 +44,11 @@ is($result->{result}{reserve}{resources}[0]{quantity}, 10000, "correct amount re # expect a small positive excess (caused by production in the mean time) cmp_ok($result->{result}{status}{body}{water_stored}, '<', 10500, "resources removed from planet"); -$result = $tester->post('distributioncenter', 'release_reserve', [$session_id, $building->id]); +$result = $tester->post('distributioncenter', 'release_reserve', [$session_id, $building_id]); my $resources_stored = $result->{result}{status}{body}{water_stored}; cmp_ok($resources_stored, '>', 20000, "resources added to planet"); cmp_ok($resources_stored, '<', 20500, "not too many resources added to planet"); END { - TestHelper->clear_all_test_empires; + TestClient->clear_all_test_empires; } diff --git a/t/570_TestSetupApi.t b/t/570_TestSetupApi.t new file mode 100644 index 00000000..8c7a9f6e --- /dev/null +++ b/t/570_TestSetupApi.t @@ -0,0 +1,53 @@ +# Verifies the dev-only /test setup API (lib/Lacuna/Web/TestSetup.pm): the things +# it creates must be real and usable over the normal JSON-RPC API. This file is +# also the reference for the "converted" style -- no `use lib`, no `use Lacuna`, +# just TestClient. + +use strict; +use warnings; +use Test::More tests => 12; +use 5.010; + +use TestClient; + +TestClient->clear_all_test_empires; + +my $tester = TestClient->new->generate_test_empire; + +ok($tester->empire_id, 'generate_empire returned an empire_id'); +ok($tester->session_id, 'generate_empire returned a session_id'); +ok($tester->home_planet_id, 'generate_empire returned a home_planet_id'); +is($tester->empire_name, 'TLE Test Empire', 'empire has the default test name'); + +# The session works against the real Empire RPC. +my $status = $tester->post('empire', 'get_status', [$tester->session_id]); +is($status->{result}{empire}{id}, $tester->empire_id, 'get_status resolves the session to our empire'); + +# The empire is real enough to log into with its password. +my $login = $tester->post('empire', 'login', + [$tester->empire_name, $tester->empire_password, 'Anonymous']); +ok($login->{result}{session_id}, 'can log into the generated empire with its password'); + +# Infrastructure build-out ticks resources onto the home planet. +$tester->build_infrastructure; +my $body = $tester->post('body', 'get_status', [$tester->session_id, $tester->home_planet_id]); +ok(exists $body->{result}{body}{food_capacity}, 'body get_status has resource fields after build_infrastructure'); +cmp_ok($body->{result}{body}{energy_capacity}, '>', 0, 'build_infrastructure gave the planet energy capacity'); + +# build_building + finish_building produce a finished, viewable building. +my $shipyard = $tester->build_building('Lacuna::DB::Result::Building::Shipyard', 1); +ok($shipyard->id, 'build_building returned a building id'); +$shipyard->finish_upgrade; +my $view = $tester->post('shipyard', 'view', [$tester->session_id, $shipyard->id]); +is($view->{result}{building}{level}, 1, 'finished building is level 1 via the Shipyard RPC'); + +# set_body_resources pokes whitelisted columns. +$tester->set_body_resources($tester->home_planet_id, energy_stored => 12345, tick => 1); +$body = $tester->post('body', 'get_status', [$tester->session_id, $tester->home_planet_id]); +cmp_ok($body->{result}{body}{energy_stored}, '>=', 0, 'set_body_resources kept the planet in a sane state'); + +# clear_empires really deletes it. +TestClient->clear_all_test_empires; +my $after = $tester->post('empire', 'login', + [$tester->empire_name, $tester->empire_password, 'Anonymous']); +ok($after->{error}, 'login fails after clear_empires deleted the empire'); diff --git a/t/TestClient.pm b/t/TestClient.pm new file mode 100644 index 00000000..f7f72696 --- /dev/null +++ b/t/TestClient.pm @@ -0,0 +1,220 @@ +package TestClient; + +# Lightweight counterpart to t/TestHelper.pm for "naked" tests -- those that only +# need a test empire plus JSON-RPC HTTP calls. It talks to the dev-only /test +# endpoints (lib/Lacuna/Web/TestSetup.pm) for all setup/teardown, so it can load +# with just LWP + JSON and skip the multi-second `use Lacuna` compile that +# TestHelper pays per test file. +# +# Drop-in for the common TestHelper surface: ->new, ->generate_test_empire, +# ->use_existing_test_empire, ->build_infrastructure, ->post, ->get_building, +# ->session->id, ->empire->id, ->empire->home_planet->id, ->empire_name, +# TestClient->clear_all_test_empires. Tests that reach deeper into Lacuna objects +# are "bespoke" and should stay on TestHelper. + +use strict; +use warnings; +use LWP::UserAgent; +use JSON qw(to_json from_json); +use Carp qw(croak); + +sub new { + my ($class, $args) = @_; + $args ||= {}; + my $url = $ENV{LACUNA_TEST_SERVER_URL} || 'http://localhost:5000/'; + $url .= '/' unless $url =~ m{/$}; + my $self = bless { + server_url => $url, + ua => LWP::UserAgent->new(timeout => 30), + big_producer => $args->{big_producer} ? 1 : 0, + empire_name => $args->{empire_name}, + empire_id => undef, + session_id => undef, + home_planet_id => undef, + password => undef, + }, $class; + return $self; +} + +# --- low-level HTTP ------------------------------------------------------- + +# JSON-RPC call -- identical wire format to TestHelper::post. +sub post { + my ($self, $url, $method, $params) = @_; + my $content = { jsonrpc => '2.0', id => 1, method => $method, params => $params }; + print "REQUEST: " . to_json($content) . "\n"; + my $response = $self->{ua}->post( + $self->{server_url} . $url, + Content_Type => 'application/json', + Content => to_json($content), + Accept => 'application/json', + ); + print "RESPONSE: " . $response->content . "\n"; + return from_json($response->content); +} + +# POST a JSON body to /test/, return the decoded hash (croak on failure). +sub _setup { + my ($self, $path, $body) = @_; + my $response = $self->{ua}->post( + $self->{server_url} . "test/$path", + Content_Type => 'application/json', + Content => to_json($body || {}), + Accept => 'application/json', + ); + unless ($response->is_success) { + croak "POST /test/$path failed: " . $response->status_line . " -- " . $response->content; + } + return from_json($response->content); +} + +# --- empire lifecycle --------------------------------------------------------- + +sub generate_test_empire { + my ($self, %opts) = @_; + my $out = $self->_setup('generate_empire', { + name => $opts{name} || $self->{empire_name}, + password => $opts{password}, + }); + $self->_absorb($out); + return $self; +} + +sub use_existing_test_empire { + my ($self, %opts) = @_; + my $out = $self->_setup('use_existing_empire', { + name => $opts{name} || $self->{empire_name}, + password => $opts{password}, + }); + $self->_absorb($out); + return $self; +} + +sub _absorb { + my ($self, $out) = @_; + $self->{empire_id} = $out->{empire_id}; + $self->{empire_name} = $out->{empire_name}; + $self->{session_id} = $out->{session_id}; + $self->{home_planet_id} = $out->{home_planet_id}; + $self->{password} = $out->{password}; +} + +sub build_infrastructure { + my ($self, %opts) = @_; + $self->_setup('build_infrastructure', { + empire_id => $self->{empire_id}, + big_producer => (exists $opts{big_producer} ? $opts{big_producer} : $self->{big_producer}), + }); + return $self; +} + +sub build_big_colony { + my ($self, %opts) = @_; + $self->_setup('build_big_colony', { + empire_id => $self->{empire_id}, + planet_id => $opts{planet_id}, + }); + return $self; +} + +# --- buildings / resources ------------------------------------------------ + +# Returns a small stub so `$tester->build_building(...)->id` / +# `->finish_upgrade` keep working like TestHelper's building object. +sub build_building { + my ($self, $class, $level) = @_; + my $out = $self->_setup('build_building', { + empire_id => $self->{empire_id}, + class => $class, + level => $level, + }); + return TestClient::Building->new($self, $out); +} + +# TestHelper::get_building returns a live Building; here we only support the one +# thing naked tests do with it -- ->finish_upgrade (and ->id). +sub get_building { + my ($self, $building_id) = @_; + return TestClient::Building->new($self, { building_id => $building_id }); +} + +sub finish_building { + my ($self, $building_id) = @_; + return $self->_setup('finish_building', { building_id => $building_id }); +} + +sub finish_ships { + my ($self, $shipyard_id) = @_; + return $self->_setup('finish_ships', { shipyard_id => $shipyard_id }); +} + +# set_body_resources($planet_id, ore_capacity => ..., algae_stored => ..., tick => 1) +sub set_body_resources { + my ($self, $planet_id, %cols) = @_; + return $self->_setup('set_body_resources', { planet_id => $planet_id, %cols }); +} + +sub prime_captcha { + my ($self, %opts) = @_; + return $self->_setup('prime_captcha', { + session_id => (exists $opts{session_id} ? $opts{session_id} : $self->{session_id}), + ip => $opts{ip}, + }); +} + +# --- teardown ----------------------------------------------------------- + +# Callable as class or instance method (incl. from END {}). +sub clear_all_test_empires { + my ($invocant, $name) = @_; + my $self = ref $invocant ? $invocant : TestClient->new; + return $self->_setup('clear_empires', { name => $name }); +} +sub cleanup { goto &clear_all_test_empires } + +# --- accessors ----------------------------------------------------------- + +sub server_url { $_[0]->{server_url} } +sub session_id { $_[0]->{session_id} } +sub empire_id { $_[0]->{empire_id} } +sub home_planet_id { $_[0]->{home_planet_id} } +sub empire_name { $_[0]->{empire_name} // 'TLE Test Empire' } +sub empire_password { $_[0]->{password} // '123qwe' } + +# Back-compat object stubs so a header swap is usually the whole diff. +sub session { TestClient::Session->new($_[0]) } +sub empire { TestClient::Empire->new($_[0]) } + +# ====================================================================== + +package TestClient::Building; +sub new { + my ($class, $client, $out) = @_; + return bless { client => $client, %{ $out || {} } }, $class; +} +sub id { $_[0]->{building_id} } +sub x { $_[0]->{x} } +sub y { $_[0]->{y} } +sub level { $_[0]->{level} } +sub finish_upgrade { + my ($self) = @_; + $self->{client}->finish_building($self->{building_id}); + return $self; +} + +package TestClient::Session; +sub new { bless { client => $_[1] }, $_[0] } +sub id { $_[0]->{client}->session_id } + +package TestClient::Empire; +sub new { bless { client => $_[1] }, $_[0] } +sub id { $_[0]->{client}->empire_id } +sub name { $_[0]->{client}->empire_name } +sub home_planet_id { $_[0]->{client}->home_planet_id } +sub home_planet { TestClient::Body->new($_[0]->{client}) } + +package TestClient::Body; +sub new { bless { client => $_[1] }, $_[0] } +sub id { $_[0]->{client}->home_planet_id } + +1; diff --git a/t/TestHelper.pm b/t/TestHelper.pm index 8e682409..e0a15361 100644 --- a/t/TestHelper.pm +++ b/t/TestHelper.pm @@ -239,7 +239,7 @@ sub post { Accept => 'application/json', ); say "RESPONSE: ".$response->content; - sleep 2; + # sleep 2; return from_json($response->content); } diff --git a/t/bugs/0023_EmpireNameChecks.t b/t/bugs/0023_EmpireNameChecks.t index 161e4dc8..188ec02d 100644 --- a/t/bugs/0023_EmpireNameChecks.t +++ b/t/bugs/0023_EmpireNameChecks.t @@ -1,4 +1,3 @@ -use lib '../../lib','..'; use Test::More tests => 2; use 5.010; @@ -7,10 +6,10 @@ use warnings; my $result; -use TestHelper; -TestHelper->clear_all_test_empires; +use TestClient; +TestClient->clear_all_test_empires; -my $tester = TestHelper->new({empire_name => 'TLE Test Multiple Spaces'}); +my $tester = TestClient->new({empire_name => 'TLE Test Multiple Spaces'}); $tester->cleanup; $result = $tester->post('empire', 'is_name_available', [$tester->empire_name]); -- 2.51.2