0

I'm attempting to write a unit test, preferably in PEST PHP, to verify the following method:

Data is retrieved from the 'producers' table in the database and fed into the configuration. This configuration includes FTP server access details.

The file is then downloaded and saved locally.

How do you test something like this? Ideally, I would create a producer factory and then check if the file has been downloaded. However, I would like to avoid downloading real data if possible.

This is my simplified Class:

class FtpConnection extends Connection
{
    /**
     * @throws Exception
     */
    public function download()
    {
        // get the content and make sure it's utf-8 encoded
        $file = $this->encode(
            $this->getFile()
        );

        // save content localy
        return Storage::disk($this->disk)
            ->put($this->fileName(), $file);

    }

    /**
     * @throws Exception
     */
    protected function getFile()
    {
        $config = $this->config();

        $storage = Storage::createFtpDriver($config);

        throw_if(!$storage->exists($this->producer->path), new Exception('File not found at path: ' . $this->producer->path));

        return $storage->get($this->producer->path);
    }

    protected function encode($file)
    {
        $currentEncoding = mb_detect_encoding($file, 'UTF-8, ISO-8859-1, GBK');

        if($currentEncoding != 'UTF-8') {
            return mb_convert_encoding($file, 'UTF-8', $currentEncoding);
        } else {
            return $file;
        }
    }

    protected function config(): array
    {
        return [
            'driver' => $this->producer->type,
            'host' => $this->producer->host,
            'port' => $this->producer->port,
            'username' => $this->producer->username,
            'password' => Crypt::decryptString($this->producer->password),
        ];
    }

}

calling the class:

$ftpConnection = new FtpConnection(Producer::find(1))
$file = $ftpConnection->download();
Greg Ostry
  • 1,161
  • 5
  • 27
  • 52
  • First of all, if I am not wrong, you have a `FtpConenction` class that extends `Connection`, but uses none of the parent's methods, so no need to extend `Connection` then if you are overriding everything. Second, could you share what you have tried so far? Third, what I would do is just mock the `Storage` class, and read [this](https://laravel.com/docs/10.x/filesystem#testing), it will tell you how to fake files and downloads. I may be able to help, but I do not know PEST at all, pure PHPUnit in my case – matiaslauriti Jul 24 '23 at 13:51
  • Yes, you're right. Aside from `$this->fileName()` and `$this->producer`, I'm not utilizing anything from the superclass. But there's a reason for this. I brought these methods into this class specifically to illustrate the problem at hand. Here's what I attempted: In the unit test, I set up a real `$producer` with a genuine `$config` for testing purposes, and then invoked `$this->assertTrue($ftpConnection->download())`. This is because the return value of `Storage::disk($this->disk)->put($this->fileName(), $file)` is a boolean. The question now is, how do I simulate a fake download? – Greg Ostry Jul 24 '23 at 19:36

1 Answers1

0

This is my approach to pass this test green.

it('downloads a file from ftp server successfully', function() {
    Storage::fake('ftp');

    $producer = Producer::factory()->make([
        'producer_id' => 'exampleId',
        'file_extension' => 'csv',
        'type' => 'ftp',
        'host' => 'ftp.example.com',
        'port' => 21,
        'username' => 'user',
        'password' => Crypt::encryptString('password'),
    ]);

    (new \Tests\Mock\FakeFtpConnection($producer))->download();

    Storage::disk('ftp')->assertExists('exampleId.csv');
});

GregOstry OP Posted 49 minutes ago Copy it('downloads a file from ftp server successfully', function() { Storage::fake('ftp');

$producer = Producer::factory()->make([
    'producer_id' => 'exampleId',
    'file_extension' => 'csv',
    'type' => 'ftp',
    'host' => 'ftp.example.com',
    'port' => 21,
    'username' => 'user',
    'password' => Crypt::encryptString('password'),
]);

(new \Tests\Mock\FakeFtpConnection($producer))->download();

Storage::disk('ftp')->assertExists('exampleId.csv');

});

I have created a FakeFtpConnection class that extends the FtpConnection class and overrides the download method. Thus, my mock class looks like this:

class FakeFtpConnection extends FtpConnection
{
    public function download(): bool
    {
        Storage::fake('ftp')->put($this->fileName(), 'test');
//        UploadedFile::fake()->create($this->fileName());
        return true;
    }
}
Greg Ostry
  • 1,161
  • 5
  • 27
  • 52
  • If this is not working, this is not a solution, then edit the question with this, do not post as an answer, this is not a forum style page – matiaslauriti Jul 24 '23 at 21:56
  • Chill out, I am telling you that, if this is a working solution, mark your answer as correct, don't leave it posted and that's it, SO does not work like that, your question can get deleted id you do not follow the guidelines – matiaslauriti Jul 25 '23 at 13:32