<?php

namespace Tests\Feature;

use App\Models\Asns;
use App\Models\GeoIp\PrefixGeoIp;
use App\Models\Goliath\GoliathAsnAttributes;
use App\Models\Goliath\GoliathRegion;
use App\Models\Goliath\GoliathSubregion;
use App\Models\Goliath\PrefixGoliathFirewall;
use App\Models\Goliath\PrefixGoliathFlag;
use App\Models\Goliath\PrefixGoliathInline;
use App\Models\Goliath\PrefixGoliathLoose;
use App\Models\Organisation;
use App\Models\PrefixCommunity;
use App\Models\Prefixes;
use App\Models\PrefixInclusions;
use App\Models\PrefixSubtenants;
use App\Models\Supernets;
use Carbon\Carbon;
use Exception;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Notification;
use Tests\TestCase;

class PrefixLifecycleTest extends TestCase
{

    use RefreshDatabase;

    private $organisation = null;
    private $asn = null;

    // 7578:92 - Mercury, %99 - Locational

    private $configuration_detailed_1 = [

        // AS137409 - Approved, No Goliath Defaults
        [ 'prefix' => '103.0.0.0/22', 'communities' => [ '7578:92', '7578:99', '7578:199' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],                 // Mercury
        [ 'prefix' => '113.0.0.0/24', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],               // Random 24 
        [ 'prefix' => '114.0.0.0/24', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],
        [ 'prefix' => '115.0.0.0/24', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],
        [ 'prefix' => '116.0.0.0/24', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],
        [ 'prefix' => '117.0.0.0/24', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],

        // AS123456 - Approved, Goliath Default (H3, Inline, Syn-On) - Should be deannounced after the announcement test.
        [ 'prefix' => '8.0.0.0/16', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 123456, 'asn_owner' => 456 ],     // Variation between originator and owner, large 16
        [ 'prefix' => '9.1.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 123456, 'asn_owner' => 123456 ],
        [ 'prefix' => '9.2.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 123456, 'asn_owner' => 123456 ],
        [ 'prefix' => '9.3.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 123456, 'asn_owner' => 123456 ],
        [ 'prefix' => '9.4.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 123456, 'asn_owner' => 456 ],
        [ 'prefix' => '9.5.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 123456, 'asn_owner' => 456 ],
        [ 'prefix' => '9.6.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 123456, 'asn_owner' => 123456 ],
        [ 'prefix' => '9.7.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 123456, 'asn_owner' => 123456 ],
        [ 'prefix' => '9.8.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 123456, 'asn_owner' => 123456 ],
        [ 'prefix' => '9.9.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 123456, 'asn_owner' => 123456 ],
        [ 'prefix' => '9.10.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 123456, 'asn_owner' => 123456 ],
        
        // AS123457 - Approved, No Goliath Defaults
        [ 'prefix' => '8.0.0.0/24', 'communities' => [ '7578:199', '137409:1:1' ], 'asn_originator' => 123457, 'asn_owner' => 456 ],                              // Complex scenario where three different sizes of the same address have differing owners.
        [ 'prefix' => '8.0.0.0/28', 'communities' => [ '7578:199', '137409:1:1' ], 'asn_originator' => 123457, 'asn_owner' => 456 ],

        // AS137410 - Not Approved, No Goliath Defaults - First one (6) is intended to be deannounced then re-announced.
        [ 'prefix' => '11.0.0.0/22', 'communities' => [ '7578:99', '137409:10:1' ], 'asn_originator' => 137410, 'asn_owner' => 137410 ],                            // Pretty Standard
        [ 'prefix' => '11.2.1.255/22', 'communities' => [ '7578:99', '137409:10:1' ], 'asn_originator' => 137410, 'asn_owner' => 137410 ]                           // Should always fail

    ];

    private $configuration_detailed_v6_1 = [
        [ 'prefix' => '2001:0004:0112:0000:0000:0000:0000:0000/48', 'communities' => [ '7578:92', '7578:99', '7578:199' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],                 // Mercury
        [ 'prefix' => '2001:0500:0003:0000:0000:0000:0000:0000/48', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],
        [ 'prefix' => '2001:0500:009e:0000:0000:0000:0000:0000/47', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 123456, 'asn_owner' => 123456 ],
        [ 'prefix' => '2001:0500:009f:0000:0000:0000:0000:0000/48', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 123456, 'asn_owner' => 123456 ],
        [ 'prefix' => '2001:0500:009f:0000:0000:0000:0000:0001/48', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 123456, 'asn_owner' => 123456 ],
    ];

    protected function setUp(): void
    {
        parent::setUp();

        /**
         * Make sure environment is safe
         */
        $this->mockEndpoints();

        /**
         * Database setup and preamble
         */

        $this->organisation = [
            'a' => Organisation::create([ 'id' => 1, 'name' => config('creatia.automation.main_organisation_name') ]),
            'b' => Organisation::create([ 'id' => 2, 'name' => 'B' ]),
            'c' => Organisation::create([ 'id' => 3, 'name' => 'C' ]),
            'd' => Organisation::create([ 'id' => 4, 'name' => 'D' ])
        ];

        $this->asn = [
            'a' => Asns::create([ 'asn' => '137409', 'approved' => 1, 'organisation_id' => $this->organisation['a']->id ]),
            'b' => Asns::create([ 'asn' => '123456', 'approved' => 1, 'organisation_id' => $this->organisation['b']->id ]),
            'c' => Asns::create([ 'asn' => '123457', 'approved' => 1, 'organisation_id' => $this->organisation['c']->id ]),
            'd' => Asns::create([ 'asn' => '137410', 'approved' => 0, 'organisation_id' => $this->organisation['d']->id ])
        ];

        // Setup some goliath defaults for ASN B
        GoliathAsnAttributes::create([
            'asn_id'                => $this->asn['b']->id,
            'default_synproxy'      => 1,
            'default_inline'        => 1,
            'default_heuristics'    => 3
        ]);

        // Detail existing configurations to ensure tests are always clean.
        file_put_contents(storage_path('prefix.detailed.json'), '[]');
        file_put_contents(storage_path('prefix.detailed6.json'), '[]');

        // Insert Regions
        DB::table('goliath_regions')->insert([
            ['id' => 5, 'name' => 'APAC - Asia-Pacific', 'visible' => 1, 'telemetry_identifier' => 'APAC'],
            ['id' => 6, 'name' => 'EMEA - Europe, Middle East, Africa', 'visible' => 1, 'telemetry_identifier' => 'EMEA'],
            ['id' => 7, 'name' => 'NA - North America', 'visible' => 1, 'telemetry_identifier' => 'NA'],
            ['id' => 8, 'name' => 'OCEANIC - Australia + New Zealand', 'visible' => 1, 'telemetry_identifier' => 'Oceanic'],
            ['id' => 9, 'name' => 'SA - South America', 'visible' => 1, 'telemetry_identifier' => 'SA'],
        ]);

        // Insert Subregions
        DB::table('goliath_subregions')->insert([
            ['id' => 32, 'name' => 'ADL - Adelaide, Australia', 'visible' => 1, 'region_id' => 8, 'telemetry_identifier' => 'adl'],
            ['id' => 33, 'name' => 'AKL - Auckland, New Zealand', 'visible' => 1, 'region_id' => 8, 'telemetry_identifier' => 'akl'],
            ['id' => 34, 'name' => 'AMS - Amsterdam, Netherlands', 'visible' => 1, 'region_id' => 6, 'telemetry_identifier' => 'ams'],
            ['id' => 35, 'name' => 'ASH - Ashburn, VA, US', 'visible' => 1, 'region_id' => 7, 'telemetry_identifier' => 'ash'],
            ['id' => 36, 'name' => 'ATL - Atlanta, GA, US', 'visible' => 1, 'region_id' => 7, 'telemetry_identifier' => 'atl'],
            ['id' => 37, 'name' => 'BNE - Brisbane, Australia', 'visible' => 1, 'region_id' => 8, 'telemetry_identifier' => 'bne'],
            ['id' => 38, 'name' => 'CHI - Chicago, IL, US', 'visible' => 1, 'region_id' => 7, 'telemetry_identifier' => 'chi'],
            ['id' => 39, 'name' => 'DAL - Dallas, TX, US', 'visible' => 1, 'region_id' => 7, 'telemetry_identifier' => 'dal'],
            ['id' => 40, 'name' => 'FRA - Frankfurt, Germany', 'visible' => 1, 'region_id' => 6, 'telemetry_identifier' => 'fra'],
            ['id' => 41, 'name' => 'GUAM - Guam', 'visible' => 1, 'region_id' => 5, 'telemetry_identifier' => 'guam'],
            ['id' => 42, 'name' => 'HK - Hong Kong, China', 'visible' => 1, 'region_id' => 5, 'telemetry_identifier' => 'hk'],
            ['id' => 43, 'name' => 'LAX - Los Angeles, CA, US', 'visible' => 1, 'region_id' => 7, 'telemetry_identifier' => 'lax'],
            ['id' => 44, 'name' => 'LON - London, England', 'visible' => 1, 'region_id' => 6, 'telemetry_identifier' => 'lon'],
            ['id' => 45, 'name' => 'MEL - Melbourne, Australia', 'visible' => 1, 'region_id' => 8, 'telemetry_identifier' => 'mel'],
            ['id' => 46, 'name' => 'MRS - Marseilles, France', 'visible' => 1, 'region_id' => 6, 'telemetry_identifier' => 'mrs'],
            ['id' => 47, 'name' => 'NYC - New York, NY, US', 'visible' => 1, 'region_id' => 7, 'telemetry_identifier' => 'nyc'],
            ['id' => 48, 'name' => 'PER - Perth, Australia', 'visible' => 1, 'region_id' => 8, 'telemetry_identifier' => 'per'],
            ['id' => 49, 'name' => 'PHX - Phoenix, AZ, US', 'visible' => 1, 'region_id' => 7, 'telemetry_identifier' => 'phx'],
            ['id' => 50, 'name' => 'PIT - Pittsburgh, PA, US', 'visible' => 1, 'region_id' => 7, 'telemetry_identifier' => 'pit'],
            ['id' => 51, 'name' => 'SEA - Seattle, WA, US', 'visible' => 1, 'region_id' => 7, 'telemetry_identifier' => 'sea'],
            ['id' => 52, 'name' => 'SG - Singapore', 'visible' => 1, 'region_id' => 5, 'telemetry_identifier' => 'sg'],
            ['id' => 53, 'name' => 'SYD - Sydney, Australia', 'visible' => 1, 'region_id' => 8, 'telemetry_identifier' => 'syd'],
            ['id' => 54, 'name' => 'TY - Tokyo, Japan', 'visible' => 1, 'region_id' => 5, 'telemetry_identifier' => 'ty'],
            ['id' => 55, 'name' => 'MIA - Miami, FL, US', 'visible' => 1, 'region_id' => 7, 'telemetry_identifier' => 'mia'],
            ['id' => 56, 'name' => 'PAR - Paris, France', 'visible' => 1, 'region_id' => 6, 'telemetry_identifier' => 'par'],
            ['id' => 57, 'name' => 'HEL - Helsinki, Finland', 'visible' => 1, 'region_id' => 6, 'telemetry_identifier' => 'hel'],
            ['id' => 58, 'name' => 'HEL - Helsinki, Finland', 'visible' => 1, 'region_id' => 6, 'telemetry_identifier' => 'hel'],
            ['id' => 59, 'name' => 'MAD - Madrid, Spain', 'visible' => 1, 'region_id' => 6, 'telemetry_identifier' => 'mad'],
            ['id' => 60, 'name' => 'MIL - Milan, Italy', 'visible' => 1, 'region_id' => 6, 'telemetry_identifier' => 'mil'],
            ['id' => 61, 'name' => 'MUS - Muscat, Oman', 'visible' => 1, 'region_id' => 6, 'telemetry_identifier' => 'mus'],
            ['id' => 62, 'name' => 'SOF - Sofia, Bulgaria', 'visible' => 1, 'region_id' => 6, 'telemetry_identifier' => 'sof'],
            ['id' => 63, 'name' => 'TAI - Taipei, Taiwan', 'visible' => 1, 'region_id' => 6, 'telemetry_identifier' => 'tai'],
            ['id' => 64, 'name' => 'VIE - Vienna, Austria', 'visible' => 1, 'region_id' => 6, 'telemetry_identifier' => 'vie'],
            ['id' => 65, 'name' => 'WAR - Warsaw, Poland', 'visible' => 1, 'region_id' => 6, 'telemetry_identifier' => 'war'],
            ['id' => 66, 'name' => 'MCT - Muscat, Oman', 'visible' => 1, 'region_id' => 6, 'telemetry_identifier' => 'mct'],
            ['id' => 67, 'name' => 'TPE - Taipei, Taiwan', 'visible' => 1, 'region_id' => 6, 'telemetry_identifier' => 'tpe'],
            ['id' => 68, 'name' => 'CHC - Christchurch, New Zealand', 'visible' => 1, 'region_id' => 5, 'telemetry_identifier' => 'chc'],
            ['id' => 69, 'name' => 'WLG - Wellington, New Zealand', 'visible' => 1, 'region_id' => 5, 'telemetry_identifier' => 'wlg'],
            ['id' => 70, 'name' => 'KR - Korea', 'visible' => 1, 'region_id' => 5, 'telemetry_identifier' => 'kr'],
        ]);

    }

    private function mockEndpoints(): void
    {
        Http::clearResolvedInstances();

        Http::fake([
            'http://' . config('creatia.endpoint.route_server') . '/api/routes/v4' => fn () => Http::response(json_encode($this->configuration_detailed_1), 200),
            'http://' . config('creatia.endpoint.route_server') . '/api/routes/v6' => fn () => Http::response(json_encode($this->configuration_detailed_v6_1), 200),
            '*' => Http::response(['Fallback Error'], 500),
        ]);

        Notification::fake();
    }

    public function testPrefixListImport(): void
    {
        /**
         * This test should simply ensure that we end up correctly loading, and saving the prefix lists.
         * 
         * If this test succeeds it is a safe foundation for later tests.
         */

        $this->mockEndpoints();

        $hash_for_configuration_detailed_v4    = hash('sha512', json_encode($this->configuration_detailed_1));
        $hash_for_configuration_detailed_v6    = hash('sha512', json_encode($this->configuration_detailed_v6_1));

        $this->artisan('prefix:list-import', []);

        $hash_stored_detailed_v4 = hash('sha512', file_get_contents(storage_path('prefix.detailed.json')));
        $hash_stored_detailed_v6 = hash('sha512', file_get_contents(storage_path('prefix.detailed6.json')));
        
        $this->assertEquals($hash_for_configuration_detailed_v4, $hash_stored_detailed_v4, "The detailed V4 list did not import correctly.");
        $this->assertEquals($hash_for_configuration_detailed_v6, $hash_stored_detailed_v6, "The detailed V6 list did not import correctly.");
        
    }

    public function testAsnImport(): void
    {
        /**
         * Two primary scenarios to keep in mind.
         * 
         * 1. The ASN does not exist and needs to be imported to Organisation 1
         *      - Good to go, as is.
         * 2. The ASN does exist and nothing will happen.
         *      - We need to delete an ASN to test it.
         *
         * If this test succeeds it is a safe foundation for later tests.
         */

        $this->testPrefixListImport();

        // Do Scenario 1
        $this->artisan('asn:import', []);

        // Check that there's an appropriate amount of ASNs each with a unique organisation.
        $this->assertCount(4, Asns::distinct('organisation_id')->get(), 'There should be four ASNs with a unique organisation each.');

        // Do Scenario 2
        $this->asn['c']->delete();

        $this->artisan('asn:import', []);

        // Check that there's an appropriate amount of ASNs each with a unique organisation.
        $this->assertCount(3, Asns::distinct('organisation_id')->get(), 'There should be three distinct organisations in this scenario.');
        $this->assertCount(4, Asns::distinct('asn')->get(), 'There should be four distinct ASNs in this scenario.');

    }

    public function testPrefixImport(): void
    {

        /**
         * This is complicated as while there is a single responsibility it has to do many things.
         * 
         * - Prefixes should not be re-inserted when they have not been deleted.
         * - Prefixes must be valid to be inserted.
         * - A newly inserted prefix should be asserted as announced.
         * - A newly inserted prefix should be set to organisation 1 without an ASN.
         * - Sorting Supernets should be created for newly inserted prefixes.
         * 
         * So with this in mind, we need to do the following scenarios:
         * 1. Run once and determine whether the correct amount of prefixes were created as announced, ASN null and organisation 1. Correct supernets.
         * 2. Run it again and ensure the prefixes remain the same.
         * 3. Add a prefix inclusion, then test whether or not it was actually added.
         */
        
        $this->testPrefixListImport();
        $this->artisan('prefix:import', []);

        $this->assertCount(24, Prefixes::all(), "There should only be ten prefixes imported.");
        $this->assertCount(24, Prefixes::whereNull('asn_id')->where([['organisation_id', '=', 1], ['still_announced', '=', 1]])->get(), "Of the ten prefixes imported, not all of them fulfilled required criteria.");
        $this->assertCount(24, Supernets::all(), "There should only be ten sorting supernets.");

        $this->artisan('prefix:import', []);

        $this->assertCount(24, Prefixes::all(), "There should be no more prefixes imported.");
        $this->assertCount(24, Prefixes::whereNull('asn_id')->where([['organisation_id', '=', 1], ['still_announced', '=', 1]])->get(), "Prefixes should not have changed.");
        $this->assertCount(24, Supernets::all(), "Supernets should not have changed.");

        PrefixInclusions::insert([
            [
                'address'   => '12.0.0.0',
                'mask'      => 22,
                'asn_id'    => $this->asn['b']->id
            ]
        ]);

        $this->artisan('prefix:import', []);

        $this->assertCount(25, Prefixes::all(), "There only be one more prefix imported.");
        $this->assertCount(25, Prefixes::whereNull('asn_id')->where([['organisation_id', '=', 1], ['still_announced', '=', 1]])->get(), "The imported inclusion prefix isn't behaving as intended.");
        $this->assertCount(25, Supernets::all(), "There should only be one new prefix supernet.");

    }

    public function testAssociation(): void
    {
        /**
         * The primary responsibility here is to associate unassociated prefixes to ASNs - The following must be kept in mind:
         *      - A prefix cannot and should never be associated with a CURRENTLY unapproved ASN.
         *      - A prefix should only ever associate if it does not have a set ASN.
         *      - The prefix should have the setup flag be true, the name be null, the correct organisation and asn assigned.
         *      - Inclusion prefixes should also be correctly associated.
         * 
         * With this in mind, this should be the outcome of the test:
         *          AS137409 (6 + 2), AS123456 (12 + 2), AS123457 (2), AS137410 (0), NULL (1)
         */

        $this->testPrefixImport();

        $this->artisan('prefix:associate', []);

        $this->assertCount(1, Prefixes::whereNull('asn_id')->get(), "There should be one unassigned V4 prefix.");
        
        $this->assertCount(8, Prefixes::where([
            ['organisation_id', '=', $this->organisation['a']->id], ['asn_id', '=', $this->asn['a']->id], ['setup_required', '=', 1]
        ])->get(), "AS137409's prefixes did not associate correctly.");
        
        $this->assertCount(14, Prefixes::where([
            ['organisation_id', '=', $this->organisation['b']->id], ['asn_id', '=', $this->asn['b']->id], ['setup_required', '=', 1]
        ])->get(), "AS123456's prefixes did not associate correctly.");

        $this->assertCount(2, Prefixes::where([
            ['organisation_id', '=', $this->organisation['c']->id], ['asn_id', '=', $this->asn['c']->id], ['setup_required', '=', 1]
        ])->get(), "AS123457's prefixes did not associate correctly.");

        $this->assertCount(0, Prefixes::where([
            ['organisation_id', '=', $this->organisation['d']->id], ['asn_id', '=', $this->asn['d']->id]
        ])->get(), "AS137410's prefixes did not associate correctly.");

        $this->assertCount(25, Prefixes::all(), "You've done something to the amount of prefixes. This is not good.");
        $this->assertCount(25, Supernets::all(), "Youo've done something to the amount of supernets. This is not good.");

    }

    public function testAnnounceV2(): void
    {
        /**
         * The primary responsibility for this is to determine whether or not annuncements are appropriately handled. 
         *      - A prefix should be set to announced if it appears in the list.
         *      - A prefix should be set to unannounced if it does not.
         * 
         *      1. Standard scenario where we check that everything is still announced.
         *      2. Check that two V4 and two V6 have been unannounced.
         *      3. Reannounce one V4 and one V6.
         *      4. De-announce V6.
         *      5. Re-announce V6.
         */

        $this->testAssociation();

        // Scenario 1
        $this->artisan('prefix:announce-v2');

        $this->assertCount(25, Prefixes::where('still_announced', '=', 1)->get(), "You've done something to the amount of prefixes announced. This is not good.");

        // Scenario 2

        // Unannounce and then re-import
        $this->configuration_detailed_1 = [
            [ 'prefix' => '103.0.0.0/22', 'communities' => [ '7578:92', '7578:99', '7578:199' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],
            [ 'prefix' => '113.0.0.0/24', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ], 
            [ 'prefix' => '8.0.0.0/28', 'communities' => [ '7578:199', '137409:1:1' ], 'asn_originator' => 123457, 'asn_owner' => 456 ],
            [ 'prefix' => '11.0.0.0/22', 'communities' => [ '7578:99', '137409:10:1' ], 'asn_originator' => 137410, 'asn_owner' => 137410 ],
            [ 'prefix' => '11.2.1.255/22', 'communities' => [ '7578:99', '137409:10:1' ], 'asn_originator' => 137410, 'asn_owner' => 137410 ]
        ];

        $this->configuration_detailed_v6_1 = [
            [ 'prefix' => '2001:0500:0003:0000:0000:0000:0000:0000/48', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],
            [ 'prefix' => '2001:0500:009f:0000:0000:0000:0000:0000/48', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 123456, 'asn_owner' => 123456 ],
        ];

        $this->testPrefixListImport();

        $this->artisan('prefix:announce-v2');

        $this->assertCount(7, Prefixes::where('still_announced', '=', 1)->get(), "Two prefixes should have been de-announced.");

        // Scenario 3
        $this->configuration_detailed_1 = [
            [ 'prefix' => '103.0.0.0/22', 'communities' => [ '7578:92', '7578:99', '7578:199' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],
            [ 'prefix' => '113.0.0.0/24', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ], 
            [ 'prefix' => '8.0.0.0/24', 'communities' => [ '7578:199', '137409:1:1' ], 'asn_originator' => 123457, 'asn_owner' => 456 ],
            [ 'prefix' => '8.0.0.0/28', 'communities' => [ '7578:199', '137409:1:1' ], 'asn_originator' => 123457, 'asn_owner' => 456 ],
            [ 'prefix' => '11.0.0.0/22', 'communities' => [ '7578:99', '137409:10:1' ], 'asn_originator' => 137410, 'asn_owner' => 137410 ],
            [ 'prefix' => '11.2.1.255/22', 'communities' => [ '7578:99', '137409:10:1' ], 'asn_originator' => 137410, 'asn_owner' => 137410 ]
        ];

        $this->configuration_detailed_v6_1 = [
            [ 'prefix' => '2001:0500:0003:0000:0000:0000:0000:0000/48', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],
            [ 'prefix' => '2001:0500:009e:0000:0000:0000:0000:0000/47', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 123456, 'asn_owner' => 123456 ],
            [ 'prefix' => '2001:0500:009f:0000:0000:0000:0000:0000/48', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 123456, 'asn_owner' => 123456 ]
        ];

        $this->testPrefixListImport();

        $this->artisan('prefix:announce-v2');

        $this->assertCount(9, Prefixes::where('still_announced', '=', 1)->get(), "Only one prefix should be de-announced.");

        // Scenario 4
        $this->configuration_detailed_v6_1 = [
            [ 'prefix' => '2001:0500:0003:0000:0000:0000:0000:0000/48', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],
            [ 'prefix' => '2001:0500:009e:0000:0000:0000:0000:0000/47', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 123456, 'asn_owner' => 123456 ],
            [ 'prefix' => '2001:0500:009f:0000:0000:0000:0000:0000/48', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 123456, 'asn_owner' => 123456 ],
            [ 'prefix' => '2001:0500:009f:0000:0000:0000:0000:0001/48', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 123456, 'asn_owner' => 123456 ],
        ];

        $this->testPrefixListImport();

        $this->artisan('prefix:announce-v2');

        $this->assertCount(9, Prefixes::where('still_announced', '=', 1)->get(), "Only one prefix should be de-announced.");

        // Scenario 5
        $this->configuration_detailed_v6_1 = [
            [ 'prefix' => '2001:0004:0112:0000:0000:0000:0000:0000/48', 'communities' => [ '7578:92', '7578:99', '7578:199' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],                 // Mercury
            [ 'prefix' => '2001:0500:0003:0000:0000:0000:0000:0000/48', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],
            [ 'prefix' => '2001:0500:009e:0000:0000:0000:0000:0000/47', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 123456, 'asn_owner' => 123456 ],
            [ 'prefix' => '2001:0500:009f:0000:0000:0000:0000:0000/48', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 123456, 'asn_owner' => 123456 ],
            [ 'prefix' => '2001:0500:009f:0000:0000:0000:0000:0001/48', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 123456, 'asn_owner' => 123456 ],
        ];

        $this->testPrefixListImport();

        $this->artisan('prefix:announce-v2');

        $this->assertCount(10, Prefixes::where('still_announced', '=', 1)->get(), "Only one prefix should have been re-announced.");

    }

    public function testCommunitiesV2(): void
    {

        /**
         * This exists to check whether the communities we import are accurate.
         * 
         * We need to try it with the default communities, then try it with a changed set of communities.
         */

        $scenario_1_matrix = [
            '103.0.0.0/22'  => [ '7578:92', '7578:99', '7578:199' ],
            '113.0.0.0/24'  => [ '7578:399', '7578:499', '7578:1:2' ], 
            '114.0.0.0/24'  => [ '7578:399', '7578:499', '7578:1:2' ],
            '115.0.0.0/24'  => [ '7578:399', '7578:499', '7578:1:2' ],
            '116.0.0.0/24'  => [ '7578:399', '7578:499', '7578:1:2' ],
            '117.0.0.0/24'  => [ '7578:399', '7578:499', '7578:1:2' ],
            '8.0.0.0/16'    => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ],
            '9.1.0.0/24'    => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ],
            '9.2.0.0/24'    => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ],
            '9.3.0.0/24'    => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ],
            '9.4.0.0/24'    => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ],
            '9.5.0.0/24'    => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ],
            '9.6.0.0/24'    => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ],
            '9.7.0.0/24'    => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ],
            '9.8.0.0/24'    => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ],
            '9.9.0.0/24'    => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ],
            '9.10.0.0/24'   => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ],
            '8.0.0.0/24'    => [ '7578:199', '137409:1:1' ],
            '8.0.0.0/28'    => [ '7578:199', '137409:1:1' ],
            '11.0.0.0/22'   => [ '7578:99', '137409:10:1' ],
            '2001:0004:0112:0000:0000:0000:0000:0000/48' => [ '7578:92', '7578:99', '7578:199' ],
            '2001:0500:0003:0000:0000:0000:0000:0000/48' => [ '7578:399', '7578:499', '7578:1:2' ],
            '2001:0500:009e:0000:0000:0000:0000:0000/47' => [ '7578:399', '7578:499', '7578:1:2' ],
            '2001:0500:009f:0000:0000:0000:0000:0000/48' => [ '7578:399', '7578:499', '7578:1:2' ]
        ];

        $scenario_2_matrix = [
            '103.0.0.0/22'  => [ '7578:92', '7578:99', '7578:199', '7578:399' ],
            '113.0.0.0/24'  => [ '7578:399', '7578:499', '7578:1:2' ], 
            '114.0.0.0/24'  => [ '7578:399', '7578:499', '7578:1:2' ],
            '115.0.0.0/24'  => [ '7578:1:2' ],
            '116.0.0.0/24'  => [ '7578:399', '7578:499', '7578:1:2' ],
            '117.0.0.0/24'  => [ '7578:399', '7578:499', '7578:1:2' ],
            '8.0.0.0/16'    => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ],
            '9.1.0.0/24'    => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ],
            '9.2.0.0/24'    => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ],
            '9.3.0.0/24'    => [ '7578:999', '7578:299', '65550:1', '137409:1:0', '137409:1:1' ],
            '9.4.0.0/24'    => [ '7578:999', '137409:1:1' ],
            '9.5.0.0/24'    => [ '7578:399', '65550:1', '137409:1:0', '137409:1:1' ],
            '9.6.0.0/24'    => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ],
            '9.7.0.0/24'    => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ],
            '9.8.0.0/24'    => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ],
            '9.9.0.0/24'    => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ],
            '9.10.0.0/24'   => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ],
            '8.0.0.0/24'    => [ '7578:199', '137409:1:1' ],
            '8.0.0.0/28'    => [ '7578:199' ],
            '11.0.0.0/22'   => [ '137409:10:1' ],
            '2001:0004:0112:0000:0000:0000:0000:0000/48' => [ '7578:92', '7578:99' ],
            '2001:0500:0003:0000:0000:0000:0000:0000/48' => [ '7578:399', '7578:499' ],
            '2001:0500:009e:0000:0000:0000:0000:0000/47' => [ '7578:399', '7578:499', '7578:599', '7578:1:2' ],
            '2001:0500:009f:0000:0000:0000:0000:0000/48' => [ '7578:499', '7578:1:2' ]
        ];


        // Scenario 1 - Default
        $this->testPrefixListImport();
        $this->artisan('prefix:import', []);
        $this->artisan('prefix:associate', []);
        $this->artisan('prefix:announce-v2');

        $this->artisan('prefix:communities-v2');

        foreach($scenario_1_matrix as $ip => $communities) {

            $this->assertTrue(Prefixes::where(DB::raw("CONCAT(address, '/', mask)::cidr"), '=', $ip)->count() === 1, 'The prefix needs to exist.');
            $this->assertEquals(
                sizeof($communities),
                PrefixCommunity::where('prefix_id', '=', Prefixes::where(DB::raw("CONCAT(address, '/', mask)::cidr"), '=', $ip)->get()->first()->id)->count(),
                'The amount of communities need to be exact.'
            );
        }

        // Scenario 2 - Changed communities
        $this->configuration_detailed_1 = [
            [ 'prefix' => '103.0.0.0/22', 'communities' => [ '7578:92', '7578:99', '7578:199', '7578:399' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],
            [ 'prefix' => '113.0.0.0/24', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ], 
            [ 'prefix' => '114.0.0.0/24', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],
            [ 'prefix' => '115.0.0.0/24', 'communities' => [ '7578:1:2' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],
            [ 'prefix' => '116.0.0.0/24', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],
            [ 'prefix' => '117.0.0.0/24', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],
            [ 'prefix' => '8.0.0.0/16', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 123456, 'asn_owner' => 456 ],
            [ 'prefix' => '9.1.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 123456, 'asn_owner' => 123456 ],
            [ 'prefix' => '9.2.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 123456, 'asn_owner' => 123456 ],
            [ 'prefix' => '9.3.0.0/24', 'communities' => [ '7578:999', '7578:299', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 123456, 'asn_owner' => 123456 ],
            [ 'prefix' => '9.4.0.0/24', 'communities' => [ '7578:999', '137409:1:1' ], 'asn_originator' => 123456, 'asn_owner' => 456 ],
            [ 'prefix' => '9.5.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 123456, 'asn_owner' => 456 ],
            [ 'prefix' => '9.6.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 123456, 'asn_owner' => 123456 ],
            [ 'prefix' => '9.7.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 123456, 'asn_owner' => 123456 ],
            [ 'prefix' => '9.8.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 123456, 'asn_owner' => 123456 ],
            [ 'prefix' => '9.9.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 123456, 'asn_owner' => 123456 ],
            [ 'prefix' => '9.10.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 123456, 'asn_owner' => 123456 ],
            [ 'prefix' => '8.0.0.0/24', 'communities' => [ '7578:199', '137409:1:1' ], 'asn_originator' => 123457, 'asn_owner' => 456 ],
            [ 'prefix' => '8.0.0.0/28', 'communities' => [ '7578:199' ], 'asn_originator' => 123457, 'asn_owner' => 456 ],
            [ 'prefix' => '11.0.0.0/22', 'communities' => [ '7578:199' ], 'asn_originator' => 137410, 'asn_owner' => 137410 ],
            [ 'prefix' => '11.2.1.255/22', 'communities' => [ '137409:10:1' ], 'asn_originator' => 137410, 'asn_owner' => 137410 ]
        ];

        $this->configuration_detailed_v6_1 = [
            [ 'prefix' => '2001:0004:0112:0000:0000:0000:0000:0000/48', 'communities' => [ '7578:92', '7578:99' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],
            [ 'prefix' => '2001:0500:0003:0000:0000:0000:0000:0000/48', 'communities' => [ '7578:399', '7578:499' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],
            [ 'prefix' => '2001:0500:009e:0000:0000:0000:0000:0000/47', 'communities' => [ '7578:399', '7578:499', '7578:599', '7578:1:2' ], 'asn_originator' => 123456, 'asn_owner' => 123456 ],
            [ 'prefix' => '2001:0500:009f:0000:0000:0000:0000:0000/48', 'communities' => [ '7578:499', '7578:1:2' ], 'asn_originator' => 123456, 'asn_owner' => 123456 ],
            [ 'prefix' => '2001:0500:009f:0000:0000:0000:0000:0001/48', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 123456, 'asn_owner' => 123456 ],
        ];

        $this->testPrefixListImport();
        $this->artisan('prefix:import', []);
        $this->artisan('prefix:associate', []);
        $this->artisan('prefix:announce-v2');

        $this->artisan('prefix:communities-v2');

        foreach($scenario_2_matrix as $ip => $communities) {

            $this->assertTrue(Prefixes::where(DB::raw("CONCAT(address, '/', mask)::cidr"), '=', $ip)->count() === 1, 'The prefix needs to exist.');
            $this->assertEquals(
                sizeof($communities),
                PrefixCommunity::where('prefix_id', '=', Prefixes::where(DB::raw("CONCAT(address, '/', mask)::cidr"), '=', $ip)->get()->first()->id)->count(),
                'The amount of communities need to be exact.'
            );
        }

    }

    public function testGoliath(): void
    {
        /**
         * The purpose of this test is to determine whether or not prefixes actually generate the correct Goliath entries.
         * 
         *      1. Test that prefixes setup correctly
         *      2. Test that they do not setup twice.
         */

        // Scenario 1

        $this->testCommunitiesV2();

        $testing_matrix = [
            '103.0.0.0/22'      => [ // Mercury
                'flag'      => [ 'flag_synproxy' => 0, 'flag_heuristics' => 1, 'flag_heuristics_level' => 3, 'flag_special' => null ],
                'inline'    => false
            ],
            '113.0.0.0/24'      => [ 
                'flag'      => [ 'flag_synproxy' => 0, 'flag_heuristics' => 1, 'flag_heuristics_level' => 3, 'flag_special' => null ],
                'inline'    => false
            ],
            '114.0.0.0/24'      => [ 
                'flag'      => [ 'flag_synproxy' => 0, 'flag_heuristics' => 1, 'flag_heuristics_level' => 3, 'flag_special' => null ],
                'inline'    => false
            ],
            '115.0.0.0/24'      => [ 
                'flag'      => [ 'flag_synproxy' => 0, 'flag_heuristics' => 1, 'flag_heuristics_level' => 3, 'flag_special' => null ],
                'inline'    => false
            ],
            '116.0.0.0/24'      => [ 
                'flag'      => [ 'flag_synproxy' => 0, 'flag_heuristics' => 1, 'flag_heuristics_level' => 3, 'flag_special' => null ],
                'inline'    => false
            ],
            '117.0.0.0/24'      => [ 
                'flag'      => [ 'flag_synproxy' => 0, 'flag_heuristics' => 1, 'flag_heuristics_level' => 3, 'flag_special' => null ],
                'inline'    => false
            ],
            '8.0.0.0/16'      => [ // H3, Inline, Syn-On
                'flag'      => [ 'flag_synproxy' => 1, 'flag_heuristics' => 1, 'flag_heuristics_level' => 3, 'flag_special' => null ],
                'inline'    => true
            ],
            '9.1.0.0/24'      => [ // H3, Inline, Syn-On
                'flag'      => [ 'flag_synproxy' => 1, 'flag_heuristics' => 1, 'flag_heuristics_level' => 3, 'flag_special' => null ],
                'inline'    => true
            ],
            '9.2.0.0/24'      => [ // H3, Inline, Syn-On
                'flag'      => [ 'flag_synproxy' => 1, 'flag_heuristics' => 1, 'flag_heuristics_level' => 3, 'flag_special' => null ],
                'inline'    => true
            ],
            '9.3.0.0/24'      => [ // H3, Inline, Syn-On
                'flag'      => [ 'flag_synproxy' => 1, 'flag_heuristics' => 1, 'flag_heuristics_level' => 3, 'flag_special' => null ],
                'inline'    => true
            ],
            '9.4.0.0/24'      => [ // H3, Inline, Syn-On
                'flag'      => [ 'flag_synproxy' => 1, 'flag_heuristics' => 1, 'flag_heuristics_level' => 3, 'flag_special' => null ],
                'inline'    => true
            ],
            '9.5.0.0/24'      => [ // H3, Inline, Syn-On
                'flag'      => [ 'flag_synproxy' => 1, 'flag_heuristics' => 1, 'flag_heuristics_level' => 3, 'flag_special' => null ],
                'inline'    => true
            ],
            '9.6.0.0/24'      => [ // H3, Inline, Syn-On
                'flag'      => [ 'flag_synproxy' => 1, 'flag_heuristics' => 1, 'flag_heuristics_level' => 3, 'flag_special' => null ],
                'inline'    => true
            ],
            '9.7.0.0/24'      => [ // H3, Inline, Syn-On
                'flag'      => [ 'flag_synproxy' => 1, 'flag_heuristics' => 1, 'flag_heuristics_level' => 3, 'flag_special' => null ],
                'inline'    => true
            ],
            '9.8.0.0/24'      => [ // H3, Inline, Syn-On
                'flag'      => [ 'flag_synproxy' => 1, 'flag_heuristics' => 1, 'flag_heuristics_level' => 3, 'flag_special' => null ],
                'inline'    => true
            ],
            '9.9.0.0/24'      => [ // H3, Inline, Syn-On
                'flag'      => [ 'flag_synproxy' => 1, 'flag_heuristics' => 1, 'flag_heuristics_level' => 3, 'flag_special' => null ],
                'inline'    => true
            ],
            '9.10.0.0/24'      => [ // H3, Inline, Syn-On
                'flag'      => [ 'flag_synproxy' => 1, 'flag_heuristics' => 1, 'flag_heuristics_level' => 3, 'flag_special' => null ],
                'inline'    => true
            ],
            '8.0.0.0/24'      => [ // Fallback
                'flag'      => [ 'flag_synproxy' => 0, 'flag_heuristics' => 1, 'flag_heuristics_level' => 3, 'flag_special' => null ],
                'inline'    => false
            ],
            '8.0.0.0/28'      => [ // Fallback
                'flag'      => [ 'flag_synproxy' => 0, 'flag_heuristics' => 1, 'flag_heuristics_level' => 3, 'flag_special' => null ],
                'inline'    => false
            ],
            '11.0.0.0/22'     => null, // No approved ASN, and thus nothing should EVER happen.
            '2001:0004:0112:0000:0000:0000:0000:0000/48' => [
                'flag'      => [ 'flag_synproxy' => 0, 'flag_heuristics' => 1, 'flag_heuristics_level' => 3, 'flag_special' => null ],
                'inline'    => false
            ],
            '2001:0500:0003:0000:0000:0000:0000:0000/48' => [
                'flag'      => [ 'flag_synproxy' => 0, 'flag_heuristics' => 1, 'flag_heuristics_level' => 3, 'flag_special' => null ],
                'inline'    => false
            ],
            '2001:0500:009e:0000:0000:0000:0000:0000/47' => [
                'flag'      => [ 'flag_synproxy' => 1, 'flag_heuristics' => 1, 'flag_heuristics_level' => 3, 'flag_special' => null ],
                'inline'    => true
            ],
            '2001:0500:009f:0000:0000:0000:0000:0000/48' => [
                'flag'      => [ 'flag_synproxy' => 1, 'flag_heuristics' => 1, 'flag_heuristics_level' => 3, 'flag_special' => null ],
                'inline'    => true
            ]
        ];

        $this->artisan('goliath:setup');

        foreach($testing_matrix as $address => $criteria) {

            $prefix = Prefixes::where(DB::raw('CONCAT(address, \'/\', mask)::cidr'), '=', $address)->get()->first();
        
            $flags = PrefixGoliathFlag::where('prefix_id', '=', $prefix->id)->get()->first();
            $inline = PrefixGoliathInline::where('prefix_id', '=', $prefix->id)->get()->first();

            if($criteria === null) {
                $this->assertEquals($flags, $criteria, "A criteria of null means the prefix flags should never be automated here, because no ASN can be assigned.");
                $this->assertEquals($inline, $criteria, "A criteria of null means the prefix flags should never be automated here, because no ASN can be assigned.");
                
                continue;
            }

            foreach(['flag_synproxy', 'flag_heuristics', 'flag_heuristics_level', 'flag_special'] as $flag) {
                $this->assertEquals($criteria['flag'][$flag], $flags->$flag, "The correct post-condition for `$flag` did not occur for $address.");
            }

            $this->assertEquals($inline->forced, $criteria['inline'], "The correct inline post-condition did not occur for $address.");

            $this->assertEquals(1, PrefixGoliathLoose::where([['prefix_id', '=', $prefix->id], ['loose', '=', 1]])->count(), 'A loose entry should have been generated.');

        }

        $this->assertEquals(23, PrefixGoliathFlag::all()->count(), 'The amount of flags generated did not matter.');
        $this->assertEquals(23, PrefixGoliathInline::all()->count(), 'The amount of flags generated did not matter.');
        $this->assertEquals(23, PrefixGoliathLoose::all()->count(), 'The amount of OAHs generated did not matter.');

        // Scenario 2

        $this->artisan('goliath:setup');

        $this->assertEquals(23, PrefixGoliathFlag::all()->count(), 'The amount of flags generated did not matter.');
        $this->assertEquals(23, PrefixGoliathInline::all()->count(), 'The amount of flags generated did not matter.');
        $this->assertEquals(23, PrefixGoliathLoose::all()->count(), 'The amount of OAHs generated did not matter.');

    }

    public function testGeoIp(): void
    {
        /**
         * The purpose of this test is to determine whether or not prefixes actually generate the correct Geo entries and only for the primary organisation.
         * 
         *      1. Test that prefixes setup correctly
         *      2. Test that they do not setup twice.
         */

        // Should not be available to enterprise:
        if(config('app.enterprise')) {
            $this->markTestIncomplete('Test skipped because the `APP_ENTERPRISE` flag is truthy.');
        }

        // Scenario 1

        $this->testCommunitiesV2();

        $this->artisan('geoip:setup');

        $prefix_count = Prefixes::where('organisation_id', '=', 1)->count();

        $this->assertEquals($prefix_count, PrefixGeoIp::count(), 'The amount of geo-ip entries must match the amount of prefixes.');

        // Scenario 2
        $this->artisan('geoip:setup');

        $this->assertEquals($prefix_count, PrefixGeoIp::count(), 'The amount of geo-ip entries must not increase in this scenario.');

    }

    public function testReassociatePrefix(): void
    {
        /**
         * The purpose of this is to test whether prefix reassociation functions correctly.
         * Reassociation is a bit of a misnomer as we actually DELETE prefixes when they shift between organisations.
         * 
         *      1. Run it and determine that nothing has happened.
         *      2. Transition 11 V4 prefixes and 1 V6 from AS B -> AS A and make sure nothing has happened.
         *      3. Transition 5 of the AS B -> AS A prefixes and make sure it correctly reallocates. 
         *      4. Run it again and make sure nothing happens.
         */

        $criteria_1 = [
            '137409' => [ '103.0.0.0/22', '113.0.0.0/24', '114.0.0.0/24', '115.0.0.0/24', '116.0.0.0/24', '117.0.0.0/24', '2001:0004:0112:0000:0000:0000:0000:0000/48', '2001:0500:0003:0000:0000:0000:0000:0000/48' ],
            '123456' => [ '8.0.0.0/16', '9.1.0.0/24', '9.2.0.0/24', '9.3.0.0/24', '9.4.0.0/24', '9.5.0.0/24', '9.6.0.0/24', '9.7.0.0/24', '9.8.0.0/24', '9.9.0.0/24', '9.10.0.0/24', '2001:0500:009e:0000:0000:0000:0000:0000/47', '2001:0500:009f:0000:0000:0000:0000:0000/48' ],
            '123457' => [ '8.0.0.0/24', '8.0.0.0/28' ],
            '137410' => []
        ];

        $criteria_2 = $criteria_1;

        $criteria_2['137409'] = [ ...$criteria_2['137409'], '8.0.0.0/16', '9.1.0.0/24', '9.2.0.0/24', '9.3.0.0/24', '9.4.0.0/24', '9.5.0.0/24', '9.6.0.0/24', '9.7.0.0/24', '9.8.0.0/24', '9.9.0.0/24', '9.10.0.0/24', '2001:0500:009e:0000:0000:0000:0000:0000/47', '2001:0500:009f:0000:0000:0000:0000:0000/48' ];
        $criteria_2['123456'] = [];

        $criteria_3 = [
            '137409' => [ '103.0.0.0/22', '113.0.0.0/24', '114.0.0.0/24', '115.0.0.0/24', '116.0.0.0/24', '117.0.0.0/24', '9.6.0.0/24', '9.7.0.0/24', '9.8.0.0/24', '9.9.0.0/24', '9.10.0.0/24', '2001:0004:0112:0000:0000:0000:0000:0000/48', '2001:0500:0003:0000:0000:0000:0000:0000/48' ],
            '123456' => [ '8.0.0.0/16', '9.1.0.0/24', '9.2.0.0/24', '9.3.0.0/24', '9.4.0.0/24', '9.5.0.0/24', '2001:0500:009e:0000:0000:0000:0000:0000/47', '2001:0500:009f:0000:0000:0000:0000:0000/48' ],
            '123457' => [ '8.0.0.0/24', '8.0.0.0/28' ],
            '137410' => []
        ];

        $criteria_4 = $criteria_1;

        $original_configuration = $this->configuration_detailed_1;

        // Scenario 1
        $this->testPrefixListImport();
        $this->artisan('prefix:reassociate');
        $this->artisan('prefix:import', []);
        $this->artisan('prefix:associate', []);

        foreach($criteria_1 as $asn => $prefixes) {
            $asn = Asns::where('asn', '=', $asn)->get()->first()->id;

            $this->assertEquals(sizeof($prefixes), Prefixes::where('asn_id', '=', $asn)->whereIn(DB::raw('CONCAT(address, \'/\', mask)::cidr'), $prefixes)->count(), 'The number of prefixes for this ASN were inaccurate.');
        }

        // Scenario 2

        $this->configuration_detailed_1 = [
            [ 'prefix' => '103.0.0.0/22', 'communities' => [ '7578:92', '7578:99', '7578:199' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],                 
            [ 'prefix' => '113.0.0.0/24', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],             
            [ 'prefix' => '114.0.0.0/24', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],
            [ 'prefix' => '115.0.0.0/24', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],
            [ 'prefix' => '116.0.0.0/24', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],
            [ 'prefix' => '117.0.0.0/24', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],
            [ 'prefix' => '8.0.0.0/16', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 137409, 'asn_owner' => 456 ],    
            [ 'prefix' => '9.1.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 137409, 'asn_owner' => 123456 ],
            [ 'prefix' => '9.2.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 137409, 'asn_owner' => 123456 ],
            [ 'prefix' => '9.3.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 137409, 'asn_owner' => 123456 ],
            [ 'prefix' => '9.4.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 137409, 'asn_owner' => 456 ],
            [ 'prefix' => '9.5.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 137409, 'asn_owner' => 456 ],
            [ 'prefix' => '9.6.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 137409, 'asn_owner' => 123456 ],
            [ 'prefix' => '9.7.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 137409, 'asn_owner' => 123456 ],
            [ 'prefix' => '9.8.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 137409, 'asn_owner' => 123456 ],
            [ 'prefix' => '9.9.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 137409, 'asn_owner' => 123456 ],
            [ 'prefix' => '9.10.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 137409, 'asn_owner' => 123456 ],
            [ 'prefix' => '8.0.0.0/24', 'communities' => [ '7578:199', '137409:1:1' ], 'asn_originator' => 123457, 'asn_owner' => 456 ],                             
            [ 'prefix' => '8.0.0.0/28', 'communities' => [ '7578:199', '137409:1:1' ], 'asn_originator' => 123457, 'asn_owner' => 456 ],
            [ 'prefix' => '11.0.0.0/22', 'communities' => [ '7578:99', '137409:10:1' ], 'asn_originator' => 137410, 'asn_owner' => 137410 ],                            
            [ 'prefix' => '11.2.1.255/22', 'communities' => [ '7578:99', '137409:10:1' ], 'asn_originator' => 137410, 'asn_owner' => 137410 ]                           
        ];

        $this->configuration_detailed_v6_1 = [
            [ 'prefix' => '2001:0004:0112:0000:0000:0000:0000:0000/48', 'communities' => [ '7578:92', '7578:99' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],
            [ 'prefix' => '2001:0500:0003:0000:0000:0000:0000:0000/48', 'communities' => [ '7578:399', '7578:499' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],
            [ 'prefix' => '2001:0500:009e:0000:0000:0000:0000:0000/47', 'communities' => [ '7578:399', '7578:499', '7578:599', '7578:1:2' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],
            [ 'prefix' => '2001:0500:009f:0000:0000:0000:0000:0000/48', 'communities' => [ '7578:499', '7578:1:2' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],
            [ 'prefix' => '2001:0500:009f:0000:0000:0000:0000:0001/48', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 123456, 'asn_owner' => 123456 ],
        ];

        $this->testPrefixListImport();
        $this->artisan('prefix:reassociate');
        $this->artisan('prefix:import', []);
        $this->artisan('prefix:associate', []);

        foreach($criteria_2 as $asn => $prefixes) {
            $asn_id = Asns::where('asn', '=', $asn)->get()->first()->id;

            $this->assertEquals(sizeof($prefixes), Prefixes::where('asn_id', '=', $asn_id)->whereIn(DB::raw('CONCAT(address, \'/\', mask)::cidr'), $prefixes)->count(), 'The number of prefixes for AS' . $asn . ' were inaccurate.');
        }

        // Scenario 3

        $this->configuration_detailed_1 = [
            [ 'prefix' => '103.0.0.0/22', 'communities' => [ '7578:92', '7578:99', '7578:199' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],                 
            [ 'prefix' => '113.0.0.0/24', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],             
            [ 'prefix' => '114.0.0.0/24', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],
            [ 'prefix' => '115.0.0.0/24', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],
            [ 'prefix' => '116.0.0.0/24', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],
            [ 'prefix' => '117.0.0.0/24', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],
            [ 'prefix' => '8.0.0.0/16', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 123456, 'asn_owner' => 456 ],    
            [ 'prefix' => '9.1.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 123456, 'asn_owner' => 123456 ],
            [ 'prefix' => '9.2.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 123456, 'asn_owner' => 123456 ],
            [ 'prefix' => '9.3.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 123456, 'asn_owner' => 123456 ],
            [ 'prefix' => '9.4.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 123456, 'asn_owner' => 456 ],
            [ 'prefix' => '9.5.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 123456, 'asn_owner' => 456 ],
            [ 'prefix' => '9.6.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 137409, 'asn_owner' => 123456 ],
            [ 'prefix' => '9.7.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 137409, 'asn_owner' => 123456 ],
            [ 'prefix' => '9.8.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 137409, 'asn_owner' => 123456 ],
            [ 'prefix' => '9.9.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 137409, 'asn_owner' => 123456 ],
            [ 'prefix' => '9.10.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn_originator' => 137409, 'asn_owner' => 123456 ],
            [ 'prefix' => '8.0.0.0/24', 'communities' => [ '7578:199', '137409:1:1' ], 'asn_originator' => 123457, 'asn_owner' => 456 ],                             
            [ 'prefix' => '8.0.0.0/28', 'communities' => [ '7578:199', '137409:1:1' ], 'asn_originator' => 123457, 'asn_owner' => 456 ],
            [ 'prefix' => '11.0.0.0/22', 'communities' => [ '7578:99', '137409:10:1' ], 'asn_originator' => 137410, 'asn_owner' => 137410 ],                            
            [ 'prefix' => '11.2.1.255/22', 'communities' => [ '7578:99', '137409:10:1' ], 'asn_originator' => 137410, 'asn_owner' => 137410 ]                           
        ];

        $this->configuration_detailed_v6_1 = [
            [ 'prefix' => '2001:0004:0112:0000:0000:0000:0000:0000/48', 'communities' => [ '7578:92', '7578:99' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],
            [ 'prefix' => '2001:0500:0003:0000:0000:0000:0000:0000/48', 'communities' => [ '7578:399', '7578:499' ], 'asn_originator' => 137409, 'asn_owner' => 137409 ],
            [ 'prefix' => '2001:0500:009e:0000:0000:0000:0000:0000/47', 'communities' => [ '7578:399', '7578:499', '7578:599', '7578:1:2' ], 'asn_originator' => 123456, 'asn_owner' => 123456 ],
            [ 'prefix' => '2001:0500:009f:0000:0000:0000:0000:0000/48', 'communities' => [ '7578:499', '7578:1:2' ], 'asn_originator' => 123456, 'asn_owner' => 123456 ],
            [ 'prefix' => '2001:0500:009f:0000:0000:0000:0000:0001/48', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn_originator' => 123456, 'asn_owner' => 123456 ],
        ];

        $this->testPrefixListImport();
        $this->artisan('prefix:reassociate');
        $this->artisan('prefix:import', []);
        $this->artisan('prefix:associate', []);

        foreach($criteria_3 as $asn => $prefixes) {
            $asn = Asns::where('asn', '=', $asn)->get()->first()->id;

            $this->assertEquals(sizeof($prefixes), Prefixes::where('asn_id', '=', $asn)->whereIn(DB::raw('CONCAT(address, \'/\', mask)::cidr'), $prefixes)->count(), 'The number of prefixes for this ASN were inaccurate.');
        }

        // Scenario 4
        $this->configuration_detailed_1 = $original_configuration;

        $this->testPrefixListImport();
        $this->artisan('prefix:reassociate');
        $this->artisan('prefix:import', []);
        $this->artisan('prefix:associate', []);

        foreach($criteria_4 as $asn => $prefixes) {
            $asn = Asns::where('asn', '=', $asn)->get()->first()->id;

            $this->assertEquals(sizeof($prefixes), Prefixes::where('asn_id', '=', $asn)->whereIn(DB::raw('CONCAT(address, \'/\', mask)::cidr'), $prefixes)->count(), 'The number of prefixes for this ASN were inaccurate.');
        }

    }
    
    public function testPrefixDelete(): void
    {

        // This involves three components.
        // Firstly, establish that any prefix older than fourteen days is deleted.
        // Secondly, establish that any prefix older than ninety days is purged.
        // Lastly, establish that any undeleted prefix has setup set to 0.

        // Add some prefixes that should be deleted - Will produce 1 supernet
        $prefix_a_1 = Prefixes::factory()->address('111.0.0.0', 24)
            ->ownership($this->organisation['a'], $this->asn['a'])
            ->inline(false)
            ->flags(3, false)
            ->firewall(null)
            ->notAnnounced()
            ->loose(true)
            ->community('111:111', false)
            ->subtenant($this->organisation['d'])
            ->seen(Carbon::now()->subDays(16))
            ->create();

        $prefix_a_2 = Prefixes::factory()->address('112.0.0.0', 24)
            ->ownership($this->organisation['a'], $this->asn['a'])
            ->inline(false)
            ->flags(3, false)
            ->firewall(null)
            ->notAnnounced()
            ->loose(true)
            ->community('111:111', false)
            ->subtenant($this->organisation['d'])
            ->seen(Carbon::now()->subDays(16))
            ->create();

        $prefix_a_3 = Prefixes::factory()->address('112.0.0.0', 25)
            ->ownership($this->organisation['a'], $this->asn['a'])
            ->inline(false)
            ->flags(3, false)
            ->firewall(null)
            ->notAnnounced()
            ->loose(true)
            ->community('111:111', false)
            ->subtenant($this->organisation['d'])
            ->seen(Carbon::now()->subDays(16))
            ->parent($prefix_a_2)
            ->create();

        $prefix_a_4 = Prefixes::factory()->address('2001:0500:0112:0000:0000:0000:0000:0000', 48)
            ->ownership($this->organisation['a'], $this->asn['a'])
            ->inline(false)
            ->flags(3, false)
            ->firewall(null)
            ->notAnnounced()
            ->loose(true)
            ->community('111:111', false)
            ->subtenant($this->organisation['d'])
            ->seen(Carbon::now()->subDays(16))
            ->parent($prefix_a_2)
            ->create();

        // Add some prefixes that should be purged - Will produce 1 supernet
        $prefix_b_1 = Prefixes::factory()->address('211.0.0.0', 24)
            ->ownership($this->organisation['a'], $this->asn['a'])
            ->inline(false)
            ->flags(3, false)
            ->firewall(null)
            ->notAnnounced()
            ->loose(true)
            ->community('211:111', false)
            ->subtenant($this->organisation['d'])
            ->seen(Carbon::now()->subDays(95))
            ->deleted(Carbon::now()->subDays(95))
            ->create();

        $prefix_b_2 = Prefixes::factory()->address('212.0.0.0', 24)
            ->ownership($this->organisation['a'], $this->asn['a'])
            ->inline(false)
            ->flags(3, false)
            ->firewall(null)
            ->notAnnounced()
            ->loose(true)
            ->community('211:111', false)
            ->subtenant($this->organisation['d'])
            ->seen(Carbon::now()->subDays(95))
            ->deleted(Carbon::now()->subDays(95))
            ->create();

        $prefix_b_3 = Prefixes::factory()->address('212.0.0.0', 25)
            ->ownership($this->organisation['a'], $this->asn['a'])
            ->inline(false)
            ->flags(3, false)
            ->firewall(null)
            ->notAnnounced()
            ->loose(true)
            ->community('211:111', false)
            ->subtenant($this->organisation['d'])
            ->seen(Carbon::now()->subDays(95))
            ->deleted(Carbon::now()->subDays(95))
            ->parent($prefix_b_2)
            ->create();

        // Add some regulars - Will produce 4 supernets, one of which is independent
        $prefix_c_1 = Prefixes::factory()->address('111.0.0.0', 26)
            ->ownership($this->organisation['a'], $this->asn['a'])
            ->inline(false)
            ->flags(3, false)
            ->firewall(null)
            ->announced()
            ->loose(true)
            ->community('311:111', false)
            ->subtenant($this->organisation['d'])
            ->seen(Carbon::now()->subDays(1))
            ->parent($prefix_a_1)
            ->create();

        $prefix_c_2 = Prefixes::factory()->address('112.0.0.0', 26)
            ->ownership($this->organisation['a'], $this->asn['a'])
            ->inline(false)
            ->flags(3, false)
            ->firewall(null)
            ->announced()
            ->loose(true)
            ->community('311:111', false)
            ->subtenant($this->organisation['d'])
            ->seen(Carbon::now()->subDays(1))
            ->parent($prefix_a_2)
            ->parent($prefix_a_3)
            ->create();

        $prefix_c_3 = Prefixes::factory()->address('112.0.0.0', 28)
            ->ownership($this->organisation['a'], $this->asn['a'])
            ->inline(false)
            ->flags(3, false)
            ->firewall(null)
            ->announced()
            ->loose(true)
            ->community('311:111', false)
            ->subtenant($this->organisation['d'])
            ->seen(Carbon::now()->subDays(1))
            ->parent($prefix_a_3)
            ->parent($prefix_c_2)
            ->create();


        $this->artisan('prefix:delete', []);

        // Assert we have the correct amount of prefixes.
        $this->assertEquals(4, Prefixes::onlyTrashed()->count(), 'There should be three soft deleted prefixes.');
        $this->assertEquals(3, Prefixes::count(), 'There should be three non-deleted prefixes.');
        $this->assertEquals(7, Prefixes::withTrashed()->count(), 'There should be a total of six prefixes left in either state.');

        // Assert the correct amount of supernets
        $this->assertEquals(10, Supernets::count(), 'There should only be ten supernet entries.');
        $this->assertEquals(5, Supernets::distinct('supernet_id', 'subnet_id')->get()->count(), 'There should only be five supernet entries.');

        // Assert the correct amount of subtenants
        $this->assertEquals(14, PrefixSubtenants::withTrashed()->count(), 'There should only be twelve subtenancy entries.');

        // Assert the correct amount of flags
        $this->assertEquals(7, PrefixGoliathFlag::withTrashed()->count(), 'There should only be six flag entries.');

        // Assert the correct amount of inlines
        $this->assertEquals(7, PrefixGoliathInline::withTrashed()->count(), 'There should only be six flag entries.');

        // Assert the correct amount of firewall entries
        $this->assertEquals(7, PrefixGoliathFirewall::withTrashed()->count(), 'There should only be six flag entries.');

        // Assert the correct amount of loose entries
        $this->assertEquals(7, PrefixGoliathLoose::withTrashed()->count(), 'There should only be six flag entries.');

        // Assert the correct amount of communities
        $this->assertEquals(7, PrefixCommunity::count(), 'There should only be six community entries.');

        // Nothing new should happen - Determine this is stable.

        Prefixes::factory()->address('132.0.0.0', 24)
            ->ownership($this->organisation['d'], $this->asn['d'])
            ->inline(false)
            ->flags(3, false)
            ->firewall(null)
            ->announced()
            ->loose(true)
            ->community('411:111', false)
            ->subtenant($this->organisation['b'])
            ->seen(Carbon::now()->subDays(1))
            ->create();

        Prefixes::factory()->address('142.0.0.0', 24)
            ->ownership($this->organisation['d'], $this->asn['d'])
            ->inline(false)
            ->flags(3, false)
            ->firewall(null)
            ->announced()
            ->loose(false)
            ->community('411:111', false)
            ->community('411:111:123', true)
            ->seen(Carbon::now()->subDays(1))
            ->create();

        $this->artisan('prefix:delete', []);

        $this->assertEquals(4, Prefixes::onlyTrashed()->count(), 'There should be three soft deleted prefixes.');
        $this->assertEquals(5, Prefixes::count(), 'There should be three non-deleted prefixes.');
        $this->assertEquals(9, Prefixes::withTrashed()->count(), 'There should be a total of six prefixes left in either state.');
        $this->assertEquals(10, Supernets::count(), 'There should only be ten supernet entries.');
        $this->assertEquals(5, Supernets::distinct('supernet_id', 'subnet_id')->get()->count(), 'There should only be five supernet entries.');
        $this->assertEquals(16, PrefixSubtenants::withTrashed()->count(), 'There should only be twelve subtenancy entries.');
        $this->assertEquals(9, PrefixGoliathFlag::withTrashed()->count(), 'There should only be six flag entries.');
        $this->assertEquals(9, PrefixGoliathInline::withTrashed()->count(), 'There should only be six flag entries.');
        $this->assertEquals(9, PrefixGoliathFirewall::withTrashed()->count(), 'There should only be six flag entries.');
        $this->assertEquals(9, PrefixGoliathLoose::withTrashed()->count(), 'There should only be six flag entries.');
        $this->assertEquals(10, PrefixCommunity::count(), 'There should only be six flag entries.');

    }

    public function testGoliathLocationSync(): void
    {

        $locationLookup = json_decode(file_get_contents(app_path('Models/CommunityLocationLookup.json')), true);

        $this->testPrefixListImport();
        $this->artisan('prefix:import', []);
        $this->artisan('prefix:associate', []);
        $this->artisan('prefix:announce-v2');
        $this->artisan('prefix:communities-v2');
        
        // Quickly setup all of our prefixes as loose. 
        foreach([...$this->configuration_detailed_1, ...$this->configuration_detailed_v6_1] as $x) {

            $prefix = Prefixes::where(DB::raw('CONCAT(address, \'/\', mask)::cidr'), '=', $x['prefix'])->get()->first() ?? null;

            if($prefix === null) {
                continue;
            }

            PrefixGoliathLoose::insert([[
                'prefix_id'     => $prefix->id,
                'loose'         => 1
            ]]);
        }
        
        $this->artisan('goliath:location-sync');

        // Test our prefixes.
        $this->assertTrue(PrefixGoliathLoose::count() === 24, 'There should only be 24 prefix loose entries.');

        foreach([
            [ 'prefix' => '103.0.0.0/22', 'communities' => [] ], // Should be null with Mercury
            [ 'prefix' => '113.0.0.0/24', 'communities' => [ '7578:399', '7578:499' ] ],
            [ 'prefix' => '114.0.0.0/24', 'communities' => [ '7578:399', '7578:499' ] ],
            [ 'prefix' => '115.0.0.0/24', 'communities' => [ '7578:399', '7578:499' ] ],
            [ 'prefix' => '116.0.0.0/24', 'communities' => [ '7578:399', '7578:499' ] ],
            [ 'prefix' => '117.0.0.0/24', 'communities' => [ '7578:399', '7578:499' ] ],
            [ 'prefix' => '8.0.0.0/16', 'communities' => [ '7578:999' ] ],
            [ 'prefix' => '9.1.0.0/24', 'communities' => [ '7578:999' ] ],
            [ 'prefix' => '9.2.0.0/24', 'communities' => [ '7578:999' ] ],
            [ 'prefix' => '9.3.0.0/24', 'communities' => [ '7578:999' ] ],
            [ 'prefix' => '9.4.0.0/24', 'communities' => [ '7578:999' ] ],
            [ 'prefix' => '9.5.0.0/24', 'communities' => [ '7578:999' ] ],
            [ 'prefix' => '9.6.0.0/24', 'communities' => [ '7578:999' ] ],
            [ 'prefix' => '9.7.0.0/24', 'communities' => [ '7578:999' ] ],
            [ 'prefix' => '9.8.0.0/24', 'communities' => [ '7578:999' ] ],
            [ 'prefix' => '9.9.0.0/24', 'communities' => [ '7578:999' ] ],
            [ 'prefix' => '9.10.0.0/24', 'communities' => [ '7578:999' ] ],
            [ 'prefix' => '8.0.0.0/24', 'communities' => [ '7578:199' ] ],
            [ 'prefix' => '8.0.0.0/28', 'communities' => [ '7578:199' ] ],
            [ 'prefix' => '11.0.0.0/22', 'communities' => [] ],
            [ 'prefix' => '2001:0004:0112:0000:0000:0000:0000:0000/48', 'communities' => [] ],
            [ 'prefix' => '2001:0500:0003:0000:0000:0000:0000:0000/48', 'communities' => [ '7578:399', '7578:499' ] ],
            [ 'prefix' => '2001:0500:009e:0000:0000:0000:0000:0000/47', 'communities' => [ '7578:399', '7578:499' ] ],
            [ 'prefix' => '2001:0500:009f:0000:0000:0000:0000:0000/48', 'communities' => [ '7578:399', '7578:499' ] ],
        ] as $x) {

            $prefix = Prefixes::where(DB::raw('CONCAT(address, \'/\', mask)::cidr'), '=', $x['prefix'])->get()->first();

            $communities = json_decode(PrefixGoliathLoose::where('prefix_id', '=', $prefix->id)->get()->first()->subregion_info, true);
            $region_identity_identifier_lookup = GoliathSubregion::whereIn('id', $communities === null ? [] : array_values($communities))->select('id', 'telemetry_identifier')->get()->keyBy('telemetry_identifier');

            echo json_encode($prefix) . "\n";

            $this->assertTrue((sizeof($x['communities']) === 0 && $communities === null) || sizeof($communities) == sizeof($x['communities']), 'The number of communities does not match for `' . $x['prefix'] . '`.');
            
            foreach($x['communities'] as $y) {
                $this->assertTrue( array_search($y, array_keys($communities)) !== false, "The community `$y` is missing for `" . $x['prefix'] . '` or you have broken the test criteria.' );
                $this->assertTrue( array_search($region_identity_identifier_lookup[$locationLookup[$y]]->id, array_values($communities)) !== false, "The community `$y` for `" . $x['prefix'] . '` is yielding an inappropriate location.' );
            }
        }
    }

    public function testSafeFailure(): void
    {
        /**
         * Test to make sure that core aspects fail if the prefix list is broken or empty. Add any new automation here as needed.
         * 
         * First, generate everything as normal so that way we can measure whether any impacts have occurred. 
         */

        $test_criteria = function() {

            $criteria = [
                [ 'prefix' => '103.0.0.0/22', 'communities' => [ '7578:92', '7578:99', '7578:199' ], 'asn' => 137409, 'flag_heuristics_level' => 3, 'flag_synproxy' => 0, 'forced' => 0  ],
                [ 'prefix' => '113.0.0.0/24', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn' => 137409, 'flag_heuristics_level' => 3, 'flag_synproxy' => 0, 'forced' => 0 ],
                [ 'prefix' => '114.0.0.0/24', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn' => 137409, 'flag_heuristics_level' => 3, 'flag_synproxy' => 0, 'forced' => 0 ],
                [ 'prefix' => '115.0.0.0/24', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn' => 137409, 'flag_heuristics_level' => 3, 'flag_synproxy' => 0, 'forced' => 0 ],
                [ 'prefix' => '116.0.0.0/24', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn' => 137409, 'flag_heuristics_level' => 3, 'flag_synproxy' => 0, 'forced' => 0 ],
                [ 'prefix' => '117.0.0.0/24', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn' => 137409, 'flag_heuristics_level' => 3, 'flag_synproxy' => 0, 'forced' => 0 ],

                [ 'prefix' => '8.0.0.0/16', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn' => 123456, 'flag_heuristics_level' => 3, 'flag_synproxy' => 1, 'forced' => 1 ],
                [ 'prefix' => '9.1.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn' => 123456, 'flag_heuristics_level' => 3, 'flag_synproxy' => 1, 'forced' => 1  ],
                [ 'prefix' => '9.2.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn' => 123456, 'flag_heuristics_level' => 3, 'flag_synproxy' => 1, 'forced' => 1  ],
                [ 'prefix' => '9.3.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn' => 123456, 'flag_heuristics_level' => 3, 'flag_synproxy' => 1, 'forced' => 1  ],
                [ 'prefix' => '9.4.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn' => 123456, 'flag_heuristics_level' => 3, 'flag_synproxy' => 1, 'forced' => 1  ],
                [ 'prefix' => '9.5.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn' => 123456, 'flag_heuristics_level' => 3, 'flag_synproxy' => 1, 'forced' => 1  ],
                [ 'prefix' => '9.6.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn' => 123456, 'flag_heuristics_level' => 3, 'flag_synproxy' => 1, 'forced' => 1  ],
                [ 'prefix' => '9.7.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn' => 123456, 'flag_heuristics_level' => 3, 'flag_synproxy' => 1, 'forced' => 1  ],
                [ 'prefix' => '9.8.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn' => 123456, 'flag_heuristics_level' => 3, 'flag_synproxy' => 1, 'forced' => 1  ],
                [ 'prefix' => '9.9.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn' => 123456, 'flag_heuristics_level' => 3, 'flag_synproxy' => 1, 'forced' => 1  ],
                [ 'prefix' => '9.10.0.0/24', 'communities' => [ '7578:999', '65550:1', '137409:1:0', '137409:1:1' ], 'asn' => 123456, 'flag_heuristics_level' => 3, 'flag_synproxy' => 1, 'forced' => 1  ],
                
                [ 'prefix' => '8.0.0.0/24', 'communities' => [ '7578:199', '137409:1:1' ], 'asn' => 123457, 'flag_heuristics_level' => 3, 'flag_synproxy' => 0, 'forced' => 0 ],
                [ 'prefix' => '8.0.0.0/28', 'communities' => [ '7578:199', '137409:1:1' ], 'asn' => 123457, 'flag_heuristics_level' => 3, 'flag_synproxy' => 0, 'forced' => 0 ],

                [ 'prefix' => '11.0.0.0/22', 'communities' => [ '7578:99', '137409:10:1' ], 'asn' => false ], 

                [ 'prefix' => '2001:4:112::/48', 'communities' => [ '7578:92', '7578:99', '7578:199' ], 'asn' => 137409 ],
                [ 'prefix' => '2001:500:3::/48', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn' => 137409 ],
                [ 'prefix' => '2001:500:9e::/47', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn' => 123456 ],
                [ 'prefix' => '2001:500:9f::/48', 'communities' => [ '7578:399', '7578:499', '7578:1:2' ], 'asn' => 123456 ]
            
            ];

            // Retrieve all prefixes
            $prefixes = Prefixes::leftJoin('prefix_goliath_flags', 'prefix_goliath_flags.prefix_id', '=', 'prefixes.id')->leftJoin('prefix_goliath_inline', 'prefix_goliath_inline.prefix_id', '=', 'prefixes.id')
                ->select('prefixes.id as id', 'address', 'mask', 'asn_id', 'flag_heuristics_level', 'flag_synproxy', 'forced', 'still_announced')
                ->get();

            $this->assertTrue(sizeof($criteria) === sizeof($prefixes), 'The amount of prefixes has changed. Not good.');
            $this->assertTrue(sizeof($criteria) === sizeof($prefixes->where('still_announced', '=', 1)), 'The amount of announced prefixes has changed. This is not good.');

            foreach($criteria as $x) {

                [$address, $mask] = explode('/', $x['prefix']);

                $prefix = $prefixes->where('address', '=', $address)->where('mask', '=', $mask)->first();

                $this->assertTrue($prefix !== null, "There should be a prefix `$address/$mask` - There is not");

                // Check communities
                $this->assertTrue(PrefixCommunity::where('prefix_id', '=', $prefix->id)->count() === sizeof($x['communities']), "The number of communities has changed for `$address/$mask`");

                // Check ASN
                $this->assertTrue(($prefix->asn_id === null && $x['asn'] === false) || $prefix->asn_id === Asns::where('asn', '=', $x['asn'])->get()->first()->id, "An ASN ID has inappropriately changed for `$address/$mask`");

                // Check Goliath

                if(isset($x['flag_heuristics_level'])) {
                    $this->assertTrue(
                        $x['flag_heuristics_level'] == $prefix['flag_heuristics_level']
                        && $x['flag_synproxy'] == $prefix['flag_synproxy']
                        && $x['forced'] == $prefix['forced'],
                        "The Goliath information has changed for `$address/$mask`"  
                    );
                }
            }
        };

        $run_automation = function($_try = false) {
            $this->testPrefixListImport();

            foreach(['prefix:import', 'asn:import', 'prefix:associate', 'asn:reconcile', 'prefix:announce-v2', 'prefix:communities-v2', 'goliath:setup', 'geoip:setup', 'prefix:reassociate', 'prefix:delete'] as $x) {
                try {

                    if($x === 'geoip:setup' && config('app.enterprise')) {
                        $this->addWarning('Test for `geoip:setup` skipped because the `APP_ENTERPRISE` flag is truthy.');
                        continue;
                    }

                    $this->artisan($x, []);
                }
                catch (Exception $ex) {
                    if(!$_try) {
                        throw new Exception($ex);
                    }
                }
            }
        };

        $run_automation(false);
        $test_criteria();

        // Empty configurations
        $this->configuration_detailed_1 = [];
        $this->configuration_detailed_v6_1 = [];

        file_put_contents(storage_path('prefix.detailed.json'), '[]');
        file_put_contents(storage_path('prefix.detailed6.json'), '[]');

        $run_automation(true);
        $test_criteria();

    }

}
