hughbris / grav-plugin-pushy

Push with Git to publish changes to your production environment
MIT License
3 stars 1 forks source link

Adding Unit testing using Codeception #26

Open pamtbaau opened 1 year ago

pamtbaau commented 1 year ago

Updated 2023-08-21

The following will add Unit tests using Codeception.

I don't know how you've setup your dev environment. I've separated themes/plugins I develop into folders outside the root of Grav and use symlinks to add the plugin.

use function PHPUnit\Framework\assertEquals; use function PHPUnit\Framework\assertTrue;

class PushyTest extends \Codeception\Test\Unit { public $files = [ '/www/grav/site-pushy/user/pages/03.added/default.md', '/www/grav/site-pushy/user/pages/04.modified/default.md', '/www/grav/site-pushy/user/pages/05.deleted/default.md', '/www/grav/site-pushy/user/pages/06.rename_old/default.md', ];

public $folders = [
    '/www/grav/site-pushy/user/pages/03.added',
    '/www/grav/site-pushy/user/pages/04.modified',
    '/www/grav/site-pushy/user/pages/05.deleted',
    '/www/grav/site-pushy/user/pages/06.rename_old',
];

/**
 * @var \UnitTester
 */
protected $tester;

protected function _before()
{
    $this->setupFiles();
}

protected function _after()
{
}

// tests
public function testReadItems()
{
    $setopt_content = [];

    $setopt_content[] = TRUE;
    $ch = curl_init('http://localhost/grav/site-pushy/admin/publish/pushy:readItems');

    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type:application/json'));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

    $result_data = curl_exec($ch);
    curl_close($ch);

    $result = json_decode($result_data, true);
    codecept_debug($result);

    assertEquals($result[0]['index'], 'A');
    assertEquals($result[0]['path'], 'pages/03.added/default.md');

    assertEquals($result[1]['index'], 'M');
    assertEquals($result[1]['path'], 'pages/04.modified/default.md');

    assertEquals($result[2]['index'], 'D');
    assertEquals($result[2]['path'], 'pages/05.deleted/default.md');

    assertEquals($result[3]['index'], 'R');
    assertEquals($result[3]['path'], 'pages/06.rename_new/default.md');
    assertEquals($result[3]['orig_path'], 'pages/06.rename_old/default.md');
}

public function testPublishItems()
{
    $payload = [
        'items' => [
            [
                'index' => 'A',
                'path' => 'pages/03.added/default.md',
            ],
            [
                'index' => 'M',
                'path' => 'pages/04.modified/default.md',
            ],
            [
                'index' => 'D',
                'path' => 'pages/05.deleted/default.md',
            ],
            [
                'index' => 'R',
                'orig_path' => 'pages/06.rename_old/default.md',
                'path' => 'pages/06.rename_new/default.md',
            ],
        ],
        "message" => "Publish changed pages",
    ];

    $ch = curl_init('http://localhost/grav/site-pushy/admin/publish/pushy:publishItems');

    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type:application/json'));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

    $result_data = curl_exec($ch);
    curl_close($ch);

    $result = json_decode($result_data, true);
    codecept_debug($result);

    assertTrue($result['isSuccess']);
    assertEquals($result['alert'], 'Items have been published.');
}

private function setupFiles()
{
    $this->clearFiles();
    $this->createFiles();
    $this->commitFiles();
    $this->modifyFile();
    $this->deleteFile();
    $this->renameFile();
}

private function clearFiles()
{
    exec('cd /www/grav/site-pushy/user && rm -rf .git');

    foreach ($this->folders as $folder) {
        exec('cd /www/grav/site-pushy/user && rm -rf ' . $folder);
    }

    exec('cd /www/grav/site-pushy/user && rm -rf pages/06.rename_new');

    exec('cd /www/grav/site-pushy/user && git init');
    exec('cd /www/grav/site-pushy/user && git config user.name "unit" && git config user.email "unit@test.com"');
    exec('cd /www/grav/site-pushy/user && git add . && git commit -m "initial commit"');
}

private function createFiles()
{
    foreach ($this->folders as $folder) {
        mkdir($folder, 0777, true);
    }

    file_put_contents(
        '/www/grav/site-pushy/user/pages/03.added/default.md',
        "---\ntitle: Added\n---",
    );
    file_put_contents(
        '/www/grav/site-pushy/user/pages/04.modified/default.md',
        "---\ntitle: To be modified\n---",
    );
    file_put_contents(
        '/www/grav/site-pushy/user/pages/05.deleted/default.md',
        "---\ntitle: To be deleted\n---",
    );
    file_put_contents(
        '/www/grav/site-pushy/user/pages/06.rename_old/default.md',
        "---\ntitle: To be renamed\n---",
    );
}

public function commitFiles()
{
    exec('cd /www/grav/site-pushy/user && git add pages/04.modified/default.md pages/05.deleted/default.md pages/06.rename_old/default.md');
    exec('cd /www/grav/site-pushy/user && git commit -m "Initial commit"');
}

private function modifyFile()
{
    file_put_contents(
        '/www/grav/site-pushy/user/pages/04.modified/default.md',
        "\n# Content title",
        FILE_APPEND,
    );
}

private function deleteFile()
{
    exec('cd /www/grav/site-pushy/user && rm -rf pages/05.deleted');
}

private function renameFile()
{
    exec('cd /www/grav/site-pushy/user && rm -rf pages/06.rename_new');
    exec('cd /www/grav/site-pushy/user && mv -f pages/06.rename_old/ pages/06.rename_new');
}

}


- Update paths in */tests/unit/PushyTests.php* to reflect your setup. E.g.:
  - */www/grav/site-pushy/user/*
  - *http://localhost/grav/site-pushy*

Run the following commands:
- To run all defined unit tests
`$ vendor/bin/codecept run --debug unit`
- To run all test defined in single file
`$ vendor/bin/codecept run --debug unit PushyTest`
- To run a single test method
`$ vendor/bin/codecept run --debug unit PushyTest::testReadItems`
`$ vendor/bin/codecept run --debug unit PushyTest::testPublishItems`
hughbris commented 1 year ago

Thank you, this will be very helpful.

I assume _before() runs before any test, so I'd need to hook this up with a disposable installation because of clearFiles(). Can do.

Because I use Docker, I'll need to persist that unit tests folder so I don't lose PushyTest.php after every container rebuild. That's also not difficult.

I'll have to tweak some of the hardcoded PushyTest.php paths and local URLs too.

I am thinking the simplest way for me to run these development tools in my development installs is to create a development tag on my Docker Grav platform image. The image can include this and #25, and eventually my Xdebug integration (when I get it working!).

Great tools!

I'll let you know when I've run these tests successfully and presumably you can close the issue after that.

pamtbaau commented 1 year ago

assume _before() runs before any test, so I'd need to hook this up with a disposable installation because of clearFiles(). Can do.

That's correct. The _before() is run before every test and is quite destructive.

In my setup, the pushy code is located outside of the pushy website and added to the "/user/plugin" folder using a symbolic link.

/www/grav/
      add-ons/
          plugins/
               contactform/
               pushy/
          themes/
               /base
      site-dev/
      site-pushy/
          user/plugins/pushy  =  symlink to ./www/grav/add-ons/plugins/pushy

This way I can easily clear/setup site-pushy/user/pages folder.

I'll have to tweak some of the hardcoded PushyTest.php paths and local URLs too.

Ideally, the unit tests should be added to pushy itself and added to Git. However, since we have different environment setups, it cannot be hardcoded to pushy (yet). That's why I provided a script.

Yes, all urls/paths need to be adapted to your setup.

I cannot be any help on Docker. I've looked at it a few times, but it's just too complex for me...

hughbris commented 1 year ago

I'm running $ vendor/bin/codecept run --debug unit PushyTest::testReadItems from your top post instructions and see the test run, and this summary:

There was 1 error:

---------
1) PushyTest: Read items
 Test  tests/unit/PushyTest.php:testReadItems

  [TypeError] array_merge(): Argument #1 must be of type array, null given  

#1  /var/www/grav/user/shared/plugins/pushy/vendor/bin/codecept:119

ERRORS!
Tests: 1, Assertions: 0, Errors: 1.

(Initially I ran all unit tests and also saw this message for the Publish Items test.)

I've tweaked your code snippet for working paths. I've confirmed the URL used retrieving /admin/publish/pushy:readItems results in valid JSON when using curl at CLI.

I changed into the plugin directory before running the composer commands.

I suspect I missed or did something wrong setting this up.

pamtbaau commented 1 year ago

I've update the instructions in first post.

hughbris commented 1 year ago

Thanks. I'm still seeing the same error, unfortunately.

I don't know how you've setup your dev environment. I've separated themes/plugins I develop into folders outside the root of Grav and use symlinks to add the plugin.

My setup is very similar. I usually keep plugins/themes, that are shared or I hack on, in their own path and symlink them as required. It's a bit more complicated than that, but effectively that is a working setup. Symlinks generally seem transparent although I have noticed traversing using parent expressions like .. is unreliable. So far this has never been an issue running a Grav install. I did notice the line #119 referenced in the error tries to include a parent-relative file.

Going back a step, I actually finally got to trying out your instructions here because I am building a new plugin and wanted to drive it with tests from the start (TDD!). This plugin currently sits under a site's user/plugins without using a symlink. Sometimes I do that to start off and then move it and symlink it later. (Also because I create it using devtools.)

I think next I will try your instructions in this new plugin with a dummy test and see if I have more luck there. Thanks for your patient explanations!

hughbris commented 1 year ago

I think next I will try your instructions in this new plugin with a dummy test and see if I have more luck there.

I was able to run a dummy unit test, but hit other problems when I tried to assert anything. Examples in Codeception docs are also throwing errors. Seems something is not bootstrapping or loading properly. I need to move onto other tasks. I will contact you offline because it's a bit tedious troubleshooting this here.

pamtbaau commented 1 year ago

When updating the setup script shown above an error was thrown about not existing assertEquals and assertTrue methods. Updating the PHP requirements in composer.json solved the issue.

"require": {
  "php": ">=7.3.6 || ^8.0",

"config": {
  "platform": {
    "php": "7.3.6"
hughbris commented 1 year ago

Updating the PHP requirements in composer.json solved the issue.

I had made that change and it's not solving it for me :/

pamtbaau commented 1 year ago

Just to be sure, a setup with a fresh copy of Grav and fresh plugin...

In my previous tests, an older version of Codeception 4.2 was lingering around while 5.0 is out. I presume your dev environment is PHP 8.x

Setup:

Note, no need to change any PHP version in composer.json inside the plugin.

hughbris commented 1 year ago

Thanks. I finally got to testing this and it worked with the clean install.

I ran the previous tests on "clean" installs, except I reused/persisted user, backups, and logs. That's a simplification because I use docker, but it's effectively the same. My successful test just then also used docker, just without populating those directories. So it's more likely to be something in my user folder than docker that's causing my error, I think.

I think (from faulty memory) I have also tried this with another instance/container and got the same result. Let me try some others and get back to you.