<?php

namespace Velis\Test\Cache;

use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Psr\SimpleCache\CacheInterface;
use Velis\Cache\AbstractCache;

/**
 * @author Jan Małysiak <jan.malysiak@velis.pl>
 */
class AbstractCacheTest extends TestCase
{
    /**
     * @var AbstractCache
     */
    private $cache;

    /**
     * @var MockObject
     */
    private $implementationMock;

    protected function setUp(): void
    {
        $this->implementationMock = $this->createMock(CacheInterface::class);

        $arguments = [
            $this->implementationMock,
        ];

        $this->cache = $this->getMockForAbstractClass(AbstractCache::class, $arguments);

        $this->cache->method('get')
            ->willReturnCallback([$this->implementationMock, 'get']);

        $this->cache->method('set')
            ->willReturnCallback([$this->implementationMock, 'set']);
    }

    public function testHas()
    {
        $this->implementationMock->expects($this->once())
            ->method('has')
            ->willReturn(true);

        $result = $this->cache->has('key');

        $this->assertTrue($result);
    }

    public function testGetMultiple()
    {
        $keys = [
            'key1',
            'key2',
            'key3',
        ];

        $this->implementationMock->expects($this->exactly(count($keys)))
            ->method('get');

        $this->cache->getMultiple($keys);
    }

    public function testSetMultiple()
    {
        $values = [
            'key1' => 'value1',
            'key2' => 'value2',
        ];

        $this->implementationMock->expects($this->exactly(count($values)))
            ->method('set');

        $this->cache->setMultiple($values);
    }

    public function testOffsetGet()
    {
        $expectedValue = 'value';

        $this->implementationMock->expects($this->once())
            ->method('get')
            ->willReturn($expectedValue);

        $actualValue = $this->cache['key'];

        $this->assertEquals($expectedValue, $actualValue);
    }

    public function testOffsetSet()
    {
        $expectedKey = 'key';
        $expectedValue = 'value';

        $this->implementationMock->expects($this->once())
            ->method('set')
            ->willReturnCallback(function ($actualKey, $actualValue) use ($expectedKey, $expectedValue) {
                $this->assertEquals($expectedKey, $actualKey);
                $this->assertEquals($expectedValue, $actualValue);
            })
        ;

        $this->cache[$expectedKey] = $expectedValue;
    }

    public function testOffsetExists()
    {
        $this->implementationMock->expects($this->once())
            ->method('has')
            ->willReturn(false);

        $this->assertFalse(isset($this->cache['key']));
    }

    public function testOffsetUnset()
    {
        $this->implementationMock->expects($this->once())
            ->method('delete');

        unset($this->cache['key']);
    }
}
