<commit_before><?php

namespace Mongrate\Command;

use Mongrate\Exception\InvalidFixturesException;
use Mongrate\Exception\MigrationDoesntExist;
use Mongrate\Model\Direction;
use Mongrate\Model\Name;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Yaml\Parser;

class TestMigrationCommand extends BaseCommand
{
    private $input;

    private $output;

    private static $matchNativeMongoClass = '/^Mongo(Id|Code|Date|Regex|BinData|Int32|Int64|DBRef|MinKey|MaxKey|Timestamp)\((.*)\)$/';

    protected function configure()
    {
        $this->setName('test')
            ->setDescription('Test a migration up and down.')
            ->addArgument('name', InputArgument::REQUIRED, 'The class name, formatted like "UpdateAddressStructure_20140523".')
            ->addArgument('direction', InputArgument::OPTIONAL, 'Whether to test going up or down. If left blank, both are tested.')
            ->addOption('pretty', null, InputArgument::OPTIONAL, 'Whether to pretty-print the output if there is an error.', false)
        ;

        parent::configure();
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $this->input = $input;
        $this->output = $output;

        $name = new Name($input->getArgument('name'));
        $direction = $input->getArgument('direction')
            ? new Direction($input->getArgument('direction'))
            : null;

        $classFile = $this->service->getMigrationClassFileFromName($name);
        if (file_exists($classFile)) {
            require_once $classFile;
        } else {
            throw new MigrationDoesntExist($name, $classFile);
        }

        $this->service->switchToDatabase('mongrate_test_' . $name);

        if ($direction) {
            $this->test($name, $direction);
        } else {
            $this->test($name, Direction::up());
            $this->test($name, Direction::down());
        }
    }

    private function test(Name $name, Direction $direction)
    {
        $testsDirectory = $this->service->getConfiguration()->getMigrationsDirectory()
            . '/'
            . $name
            . '/';
        $inputFile = $testsDirectory . $direction . '-input.yml';
        $verifierFile = $testsDirectory . $direction . '-verifier.yml';

        $this->addFixturesToDatabaseFromYamlFile($inputFile);
        $this->applyMigration($name, $direction);
        $this->verifyDatabaseAgainstYamlFile($verifierFile);
    }

    private function addFixturesToDatabaseFromYamlFile($fixturesFile)
    {
        $fixtures = $this->getFixturesFromYamlFile($fixturesFile);

        foreach ($fixtures as $collectionName => $collectionFixtures) {
            $collection = $this->service->selectCollection($collectionName);

            // Start off with an empty collection by removing all rows with an empty query.
            $collection->remove([]);

            foreach ($collectionFixtures as $i => $collectionFixture) {
                $collectionFixture = array_map([$this, 'convertYmlStringToNativeMongoObjects'], $collectionFixture);
                $collectionFixture['_orderInTestYamlFile'] = $i;
                $collection->insert($collectionFixture);
            }
        }
    }

    private function applyMigration(Name $name, Direction $direction)
    {
        $migration = $this->service->createMigrationInstance($name, $this->output);

        if ($direction->isUp()) {
            $this->output->writeln('<info>Testing ' . $name . ' going up.</info>');
            $migration->up($this->service->getDatabase());
        } elseif ($direction->isDown()) {
            $this->output->writeln('<info>Testing ' . $name . ' going down.</info>');
            $migration->down($this->service->getDatabase());
        }
    }

    private function verifyDatabaseAgainstYamlFile($verifierFile)
    {<commit_msg>Ensure we start tests with a clean collection without indexes.<commit_after><?php

namespace Mongrate\Command;

use Mongrate\Exception\InvalidFixturesException;
use Mongrate\Exception\MigrationDoesntExist;
use Mongrate\Model\Direction;
use Mongrate\Model\Name;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Yaml\Parser;

class TestMigrationCommand extends BaseCommand
{
    private $input;

    private $output;

    private static $matchNativeMongoClass = '/^Mongo(Id|Code|Date|Regex|BinData|Int32|Int64|DBRef|MinKey|MaxKey|Timestamp)\((.*)\)$/';

    protected function configure()
    {
        $this->setName('test')
            ->setDescription('Test a migration up and down.')
            ->addArgument('name', InputArgument::REQUIRED, 'The class name, formatted like "UpdateAddressStructure_20140523".')
            ->addArgument('direction', InputArgument::OPTIONAL, 'Whether to test going up or down. If left blank, both are tested.')
            ->addOption('pretty', null, InputArgument::OPTIONAL, 'Whether to pretty-print the output if there is an error.', false)
        ;

        parent::configure();
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $this->input = $input;
        $this->output = $output;

        $name = new Name($input->getArgument('name'));
        $direction = $input->getArgument('direction')
            ? new Direction($input->getArgument('direction'))
            : null;

        $classFile = $this->service->getMigrationClassFileFromName($name);
        if (file_exists($classFile)) {
            require_once $classFile;
        } else {
            throw new MigrationDoesntExist($name, $classFile);
        }

        $this->service->switchToDatabase('mongrate_test_' . $name);

        if ($direction) {
            $this->test($name, $direction);
        } else {
            $this->test($name, Direction::up());
            $this->test($name, Direction::down());
        }
    }

    private function test(Name $name, Direction $direction)
    {
        $testsDirectory = $this->service->getConfiguration()->getMigrationsDirectory()
            . '/'
            . $name
            . '/';
        $inputFile = $testsDirectory . $direction . '-input.yml';
        $verifierFile = $testsDirectory . $direction . '-verifier.yml';

        $this->addFixturesToDatabaseFromYamlFile($inputFile);
        $this->applyMigration($name, $direction);
        $this->verifyDatabaseAgainstYamlFile($verifierFile);
    }

    private function addFixturesToDatabaseFromYamlFile($fixturesFile)
    {
        $fixtures = $this->getFixturesFromYamlFile($fixturesFile);

        foreach ($fixtures as $collectionName => $collectionFixtures) {
            $collection = $this->service->selectCollection($collectionName);

            // Start off with an empty collection by removing all documents with an empty query.
            $collection->remove([]);

            // Ensure there are no indexes that can prevent test input documents being inserted,
            // e.g. there may be a uniqueness index that is being added in the migration - so
            // we need to clean up the unique index that was added the last time the test migration
            // command was run.
            $collection->deleteIndexes();

            foreach ($collectionFixtures as $i => $collectionFixture) {
                $collectionFixture = array_map([$this, 'convertYmlStringToNativeMongoObjects'], $collectionFixture);
                $collectionFixture['_orderInTestYamlFile'] = $i;
                $collection->insert($collectionFixture);
            }
        }
    }

    private function applyMigration(Name $name, Direction $direction)
    {
        $migration = $this->service->createMigrationInstance($name, $this->output);

        if ($direction->isUp()) {
            $this->output->writeln('<info>Testing ' . $name . ' going up.</info>');
            $migration->up($this->service->getDatabase());
        } elseif ($direction->isDown()) {
            $this->output->writeln('<info>Testing ' . $name . ' going down.</info>');
            $migration->down($this->service->getDatabase());
        }
    }

    private function verifyDatabaseAgainstYamlFile($verifierFile)
    {
{'fraction_non_alphanumeric': 0.13264818355640534, 'fraction_numerical': 0.0028680688336520078, 'mean_word_length': 2.8337150710032066, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 102, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '925151', 'n_tokens_mistral': 2274, 'n_tokens_neox': 2219, 'n_words': 468}