Add persistence (with local db implementation)

This commit is contained in:
Daniel Siepmann 2020-10-20 22:52:46 +02:00
parent 6abb14b3bd
commit 1a0bbf1099
Signed by: Daniel Siepmann
GPG key ID: 33D6629915560EF4
19 changed files with 441 additions and 29 deletions

2
.env
View file

@ -29,5 +29,5 @@ APP_SECRET=5ea76603566babff85ae4fa3e7d44071
# For an SQLite database, use: "sqlite:///%kernel.project_dir%/var/data.db" # For an SQLite database, use: "sqlite:///%kernel.project_dir%/var/data.db"
# For a PostgreSQL database, use: "postgresql://db_user:db_password@127.0.0.1:5432/db_name?serverVersion=11&charset=utf8" # For a PostgreSQL database, use: "postgresql://db_user:db_password@127.0.0.1:5432/db_name?serverVersion=11&charset=utf8"
# IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml # IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml
DATABASE_URL=mysql://db_user:db_password@127.0.0.1:3306/db_name?serverVersion=5.7 DATABASE_URL=sqlite:///%kernel.project_dir%/var/data.db
###< doctrine/doctrine-bundle ### ###< doctrine/doctrine-bundle ###

View file

@ -3,3 +3,4 @@ KERNEL_CLASS='App\Kernel'
APP_SECRET='$ecretf0rt3st' APP_SECRET='$ecretf0rt3st'
SYMFONY_DEPRECATIONS_HELPER=999999 SYMFONY_DEPRECATIONS_HELPER=999999
PANTHER_APP_ENV=panther PANTHER_APP_ENV=panther
DATABASE_URL=sqlite:///%kernel.project_dir%/var/test.db

View file

@ -42,6 +42,7 @@
"twig/twig": "^2.12|^3.0" "twig/twig": "^2.12|^3.0"
}, },
"require-dev": { "require-dev": {
"dama/doctrine-test-bundle": "^6.3",
"symfony/browser-kit": "^5.1", "symfony/browser-kit": "^5.1",
"symfony/css-selector": "^5.1", "symfony/css-selector": "^5.1",
"symfony/debug-bundle": "^5.1", "symfony/debug-bundle": "^5.1",

59
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "42c5e66445821e40a4194fe5a0a8a7b7", "content-hash": "a571222fe89b2d15a25ad09857c44cc6",
"packages": [ "packages": [
{ {
"name": "composer/package-versions-deprecated", "name": "composer/package-versions-deprecated",
@ -7256,6 +7256,63 @@
} }
], ],
"packages-dev": [ "packages-dev": [
{
"name": "dama/doctrine-test-bundle",
"version": "v6.3.3",
"source": {
"type": "git",
"url": "https://github.com/dmaicher/doctrine-test-bundle.git",
"reference": "a364cfee35acb7d37698c4749f7dd34d04646535"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dmaicher/doctrine-test-bundle/zipball/a364cfee35acb7d37698c4749f7dd34d04646535",
"reference": "a364cfee35acb7d37698c4749f7dd34d04646535",
"shasum": ""
},
"require": {
"doctrine/dbal": "^2.9,>=2.9.3",
"doctrine/doctrine-bundle": "^1.11 || ^2.0",
"php": "^7.1",
"symfony/framework-bundle": "^3.4 || ^4.3 || ^5.0"
},
"require-dev": {
"phpstan/phpstan": "^0.12",
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
"symfony/phpunit-bridge": "^4.3 || ^5.0",
"symfony/yaml": "^3.4 || ^4.3 || ^5.0"
},
"type": "symfony-bundle",
"extra": {
"branch-alias": {
"dev-master": "7.0.x-dev"
}
},
"autoload": {
"psr-4": {
"DAMA\\DoctrineTestBundle\\": "src/DAMA/DoctrineTestBundle"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "David Maicher",
"email": "mail@dmaicher.de"
}
],
"description": "Symfony bundle to isolate doctrine database tests and improve test performance",
"keywords": [
"doctrine",
"isolation",
"performance",
"symfony",
"tests"
],
"time": "2020-09-04T05:50:21+00:00"
},
{ {
"name": "nikic/php-parser", "name": "nikic/php-parser",
"version": "v4.10.2", "version": "v4.10.2",

View file

@ -12,4 +12,5 @@ return [
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true], Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true, 'test' => true], Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true, 'test' => true],
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
DAMA\DoctrineTestBundle\DAMADoctrineTestBundle::class => ['test' => true],
]; ];

View file

@ -0,0 +1,4 @@
dama_doctrine_test:
enable_static_connection: true
enable_static_meta_data_cache: true
enable_static_query_cache: true

View file

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20201020204121 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE TABLE entry (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, title VARCHAR(255) NOT NULL, start DATETIME NOT NULL, "end" DATETIME NOT NULL)');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('DROP TABLE entry');
}
}

View file

@ -33,4 +33,8 @@
<listeners> <listeners>
<listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener" /> <listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener" />
</listeners> </listeners>
<extensions>
<extension class="DAMA\DoctrineTestBundle\PHPUnit\PHPUnitExtension"/>
</extensions>
</phpunit> </phpunit>

View file

@ -21,6 +21,7 @@ namespace App\Controller;
* 02110-1301, USA. * 02110-1301, USA.
*/ */
use App\Persistence\Entries;
use App\Service\ActiveEntry; use App\Service\ActiveEntry;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
@ -33,15 +34,24 @@ class TimeController extends AbstractController
*/ */
private $activeEntry; private $activeEntry;
public function __construct(ActiveEntry $activeEntry) /**
{ * @var Entries
*/
private $entries;
public function __construct(
ActiveEntry $activeEntry,
Entries $entries
) {
$this->activeEntry = $activeEntry; $this->activeEntry = $activeEntry;
$this->entries = $entries;
} }
public function index(): Response public function index(): Response
{ {
return $this->render('time/index.html.twig', [ return $this->render('time/index.html.twig', [
'entry' => $this->activeEntry->get(), 'entry' => $this->activeEntry->get(),
'entries' => $this->entries->get(10),
]); ]);
} }

View file

@ -21,22 +21,37 @@ namespace App\Entity;
* 02110-1301, USA. * 02110-1301, USA.
*/ */
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity()
*/
class Entry class Entry
{ {
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/** /**
* @var string * @var string
* @ORM\Column(type="string", length=255)
*/ */
private $title = ''; private $title = '';
/** /**
* @var \DateTimeImmutable|null * @var \DateTimeImmutable|null
* @ORM\Column(type="datetime")
*/ */
private $start; private $start;
/** /**
* @var \DateTimeImmutable|null * @var \DateTimeImmutable|null
* @ORM\Column(type="datetime")
*/ */
private $stop; private $end;
/** /**
* @var bool * @var bool
@ -49,6 +64,20 @@ class Entry
$this->title = $title; $this->title = $title;
} }
public static function fromPersistence(
string $title,
\DateTimeImmutable $start,
\DateTimeImmutable $end
): self {
$entry = new static();
$entry->title = $title;
$entry->start = $start;
$entry->end = $end;
return $entry;
}
public function start(): void public function start(): void
{ {
$this->start = new \DateTimeImmutable(); $this->start = new \DateTimeImmutable();
@ -57,7 +86,7 @@ class Entry
public function stop(): void public function stop(): void
{ {
$this->stop = new \DateTimeImmutable(); $this->end = new \DateTimeImmutable();
$this->running = false; $this->running = false;
} }
@ -70,4 +99,19 @@ class Entry
{ {
return $this->title; return $this->title;
} }
public function getBegan(): ?\DateTimeImmutable
{
return $this->start;
}
public function getEnded(): ?\DateTimeImmutable
{
return $this->end;
}
public function getDuration(): \DateInterval
{
return $this->start->diff($this->end);
}
} }

View file

@ -0,0 +1,40 @@
<?php
namespace App\Persistence;
/*
* Copyright (C) 2020 Daniel Siepmann <coding@daniel-siepmann.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
use App\Entity\Entry;
interface Entries
{
/**
* Adds the given entry to the persistence.
* Those entries don't have identifiers and are always new.
*/
public function add(Entry $entry): void;
/**
* Get amount of recent entries from persistence.
*
* @return array<Entry>
*/
public function get(int $limit): array;
}

View file

@ -0,0 +1,71 @@
<?php
namespace App\Persistence;
/*
* Copyright (C) 2020 Daniel Siepmann <coding@daniel-siepmann.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
use App\Entity\Entry;
use Doctrine\DBAL\Connection;
class LocalDatabase implements Entries
{
/**
* @var Connection
*/
private $connection;
public function __construct(
Connection $connection
) {
$this->connection = $connection;
}
public function add(Entry $entry): void
{
$this->connection->insert('entry', [
'title' => $entry->getTitle(),
'start' => $entry->getBegan()->format('U'),
'end' => $entry->getEnded()->format('U'),
]);
}
public function get(int $limit): array
{
$queryBuilder = $this->connection->createQueryBuilder();
$queryBuilder->select('*');
$queryBuilder->from('entry');
$queryBuilder->setMaxResults($limit);
$queryBuilder->orderBy('id', 'desc');
$entries = [];
$result = $queryBuilder->execute();
while ($entry = $result->fetch()) {
$entries[] = Entry::fromPersistence(
$entry['title'],
new \DateTimeImmutable('@' . $entry['start']),
new \DateTimeImmutable('@' . $entry['end'])
);
}
return $entries;
}
}

View file

@ -22,6 +22,7 @@ namespace App\Service;
*/ */
use App\Entity\Entry; use App\Entity\Entry;
use App\Persistence\Entries;
use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Component\HttpFoundation\Session\SessionInterface;
class ActiveEntry class ActiveEntry
@ -31,9 +32,17 @@ class ActiveEntry
*/ */
private $session; private $session;
public function __construct(SessionInterface $session) /**
{ * @var Entries
*/
private $persistence;
public function __construct(
SessionInterface $session,
Entries $persistence
) {
$this->session = $session; $this->session = $session;
$this->persistence = $persistence;
} }
public function startNew( public function startNew(
@ -51,7 +60,12 @@ class ActiveEntry
public function stopRunning(): void public function stopRunning(): void
{ {
$this->get()->stop(); $entry = $this->get();
$entry->stop();
$this->persistence->add($entry);
$this->session->remove('runningEntry'); $this->session->remove('runningEntry');
} }

View file

@ -2,6 +2,18 @@
"composer/package-versions-deprecated": { "composer/package-versions-deprecated": {
"version": "1.11.99" "version": "1.11.99"
}, },
"dama/doctrine-test-bundle": {
"version": "4.0",
"recipe": {
"repo": "github.com/symfony/recipes-contrib",
"branch": "master",
"version": "4.0",
"ref": "56eaa387b5e48ebcc7c95a893b47dfa1ad51449c"
},
"files": [
"config/packages/test/dama_doctrine_test_bundle.yaml"
]
},
"doctrine/annotations": { "doctrine/annotations": {
"version": "1.0", "version": "1.0",
"recipe": { "recipe": {

View file

@ -0,0 +1,12 @@
<section>
{% for entry in entries %}
<article>
<header>{{ entry.title }}</header>
<time>{{ entry.began|date('d.m.Y') }}</time>
<time>{{ entry.ended|date('H:i:s') }}</time>
-
<time>{{ entry.ended|date('H:i:s') }}</time>
<!-- // TODO: Add duration, e.g. by using a view model -->
</article>
{% endfor %}
</section>

View file

@ -4,4 +4,5 @@
<h1>Time entries</h1> <h1>Time entries</h1>
{{ include('time/timer.html.twig') }} {{ include('time/timer.html.twig') }}
{{ include('time/entries.html.twig') }}
{% endblock %} {% endblock %}

View file

@ -25,6 +25,7 @@ use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
/** /**
* @covers App\Controller\TimeController * @covers App\Controller\TimeController
* @covers App\Persistence\LocalDatabase
* @uses App\Entity\Entry * @uses App\Entity\Entry
* @uses App\Service\ActiveEntry * @uses App\Service\ActiveEntry
*/ */
@ -33,7 +34,7 @@ class TimeControllerTest extends WebTestCase
/** /**
* @test * @test
*/ */
public function rootCanBeOpened() public function rootCanBeOpened(): void
{ {
$client = static::createClient(); $client = static::createClient();
$crawler = $client->request('GET', '/'); $crawler = $client->request('GET', '/');
@ -45,7 +46,7 @@ class TimeControllerTest extends WebTestCase
/** /**
* @test * @test
*/ */
public function newEntryCanBeStarted() public function newEntryCanBeStarted(): void
{ {
$client = static::createClient(); $client = static::createClient();
@ -62,7 +63,7 @@ class TimeControllerTest extends WebTestCase
/** /**
* @test * @test
*/ */
public function runningEntryCanBeStopped() public function runningEntryCanBeStopped(): void
{ {
$client = static::createClient(); $client = static::createClient();
@ -83,7 +84,7 @@ class TimeControllerTest extends WebTestCase
/** /**
* @test * @test
*/ */
public function noneRunningEntryCanBeStopped() public function noneRunningEntryCanBeStopped(): void
{ {
$client = static::createClient(); $client = static::createClient();
@ -95,4 +96,26 @@ class TimeControllerTest extends WebTestCase
$this->assertInputValueSame('title', ''); $this->assertInputValueSame('title', '');
$this->assertSelectorTextSame('button', 'Start'); $this->assertSelectorTextSame('button', 'Start');
} }
/**
* @test
*/
public function previousEntriesAreShownOnIndex(): void
{
$client = static::createClient();
$client->request('GET', '/');
$client->submitForm('Start', ['title' => 'Test Entry 1']);
$client->request('GET', '/');
$client->submitForm('Stop');
$client->request('GET', '/');
$client->submitForm('Start', ['title' => 'Test Entry 2']);
$client->request('GET', '/');
$client->submitForm('Stop');
$crawler = $client->request('GET', '/');
$this->assertSelectorTextContains('article:nth-of-type(1)', 'Test Entry 2');
$this->assertSelectorTextContains('article:nth-of-type(2)', 'Test Entry 1');
}
} }

View file

@ -32,7 +32,7 @@ class EntryTest extends TestCase
/** /**
* @test * @test
*/ */
public function canBeCreated() public function canBeCreatedViaConstructor(): void
{ {
$subject = new Entry(); $subject = new Entry();
@ -42,7 +42,25 @@ class EntryTest extends TestCase
/** /**
* @test * @test
*/ */
public function canBeCreatedWithTitle() public function canBeCreatedFromPersistence(): void
{
$subject = Entry::fromPersistence(
'Example Title',
new \DateTimeImmutable('2020-10-29 17:10:05'),
new \DateTimeImmutable('2020-10-29 18:12:15')
);
static::assertInstanceOf(Entry::class, $subject);
static::assertSame('Example Title', $subject->getTitle());
static::assertSame('2020-10-29 17:10:05', $subject->getBegan()->format('Y-m-d H:i:s'));
static::assertSame('2020-10-29 18:12:15', $subject->getEnded()->format('Y-m-d H:i:s'));
static::assertSame('01:02:10', $subject->getDuration()->format('%r%H:%I:%S'));
}
/**
* @test
*/
public function canBeCreatedWithTitle(): void
{ {
$subject = new Entry('Example title'); $subject = new Entry('Example title');
@ -52,7 +70,7 @@ class EntryTest extends TestCase
/** /**
* @test * @test
*/ */
public function returnsProvidedTitle() public function returnsProvidedTitle(): void
{ {
$subject = new Entry('Example title'); $subject = new Entry('Example title');
@ -62,7 +80,7 @@ class EntryTest extends TestCase
/** /**
* @test * @test
*/ */
public function canBeStarted() public function canBeStarted(): void
{ {
$subject = new Entry(); $subject = new Entry();
@ -74,7 +92,7 @@ class EntryTest extends TestCase
/** /**
* @test * @test
*/ */
public function canBeStopped() public function canBeStopped(): void
{ {
$subject = new Entry(); $subject = new Entry();
@ -87,7 +105,7 @@ class EntryTest extends TestCase
/** /**
* @test * @test
*/ */
public function canBeStoppedEvenIfNotRunning() public function canBeStoppedEvenIfNotRunning(): void
{ {
$subject = new Entry(); $subject = new Entry();
@ -95,4 +113,59 @@ class EntryTest extends TestCase
static::assertFalse($subject->isRunning()); static::assertFalse($subject->isRunning());
} }
/**
* @test
*/
public function returnsNullForBeganBeforeStarted(): void
{
$subject = new Entry();
static::assertNull($subject->getBegan());
}
/**
* @test
*/
public function returnsBeganAfterStarted(): void
{
$subject = new Entry();
$subject->start();
static::assertInstanceOf(\DateTimeImmutable::class, $subject->getBegan());
}
/**
* @test
*/
public function returnsNullForEndedBeforeStarted(): void
{
$subject = new Entry();
static::assertNull($subject->getEnded());
}
/**
* @test
*/
public function returnsEndedAfterStoped(): void
{
$subject = new Entry();
$subject->start();
$subject->stop();
static::assertInstanceOf(\DateTimeImmutable::class, $subject->getEnded());
}
/**
* @test
*/
public function returnsDurationAfterStopped(): void
{
$subject = new Entry();
$subject->start();
$subject->stop();
static::assertInstanceOf(\DateInterval::class, $subject->getDuration());
}
} }

View file

@ -22,12 +22,14 @@ namespace App\Tests\Service;
*/ */
use App\Entity\Entry; use App\Entity\Entry;
use App\Persistence\Entries;
use App\Service\ActiveEntry; use App\Service\ActiveEntry;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Prophecy\Argument; use Prophecy\Argument;
use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Component\HttpFoundation\Session\SessionInterface;
/** /**
* @covers App\Service\ActiveEntry * @covers App\Service\ActiveEntry
*/ */
@ -36,11 +38,13 @@ class ActiveEntryTest extends TestCase
/** /**
* @test * @test
*/ */
public function canBeCreated() public function canBeCreated(): void
{ {
$session = $this->prophesize(SessionInterface::class); $session = $this->prophesize(SessionInterface::class);
$persistence = $this->prophesize(Entries::class);
$subject = new ActiveEntry( $subject = new ActiveEntry(
$session->reveal() $session->reveal(),
$persistence->reveal()
); );
static::assertInstanceOf(ActiveEntry::class, $subject); static::assertInstanceOf(ActiveEntry::class, $subject);
@ -49,11 +53,13 @@ class ActiveEntryTest extends TestCase
/** /**
* @test * @test
*/ */
public function returnsEmptyEntryAsDefault() public function returnsEmptyEntryAsDefault(): void
{ {
$session = $this->prophesize(SessionInterface::class); $session = $this->prophesize(SessionInterface::class);
$persistence = $this->prophesize(Entries::class);
$subject = new ActiveEntry( $subject = new ActiveEntry(
$session->reveal() $session->reveal(),
$persistence->reveal()
); );
$entry = $subject->get(); $entry = $subject->get();
@ -66,11 +72,13 @@ class ActiveEntryTest extends TestCase
/** /**
* @test * @test
*/ */
public function canStartANewEntry() public function canStartANewEntry(): void
{ {
$session = $this->prophesize(SessionInterface::class); $session = $this->prophesize(SessionInterface::class);
$persistence = $this->prophesize(Entries::class);
$subject = new ActiveEntry( $subject = new ActiveEntry(
$session->reveal() $session->reveal(),
$persistence->reveal()
); );
$subject->startNew('Example title'); $subject->startNew('Example title');
@ -85,12 +93,14 @@ class ActiveEntryTest extends TestCase
/** /**
* @test * @test
*/ */
public function returnsStartedEntry() public function returnsStartedEntry(): void
{ {
$session = $this->prophesize(SessionInterface::class); $session = $this->prophesize(SessionInterface::class);
$persistence = $this->prophesize(Entries::class);
$entry = $this->prophesize(Entry::class); $entry = $this->prophesize(Entry::class);
$subject = new ActiveEntry( $subject = new ActiveEntry(
$session->reveal() $session->reveal(),
$persistence->reveal()
); );
$session->set('runningEntry', Argument::type(Entry::class))->shouldBeCalled(); $session->set('runningEntry', Argument::type(Entry::class))->shouldBeCalled();
@ -104,12 +114,14 @@ class ActiveEntryTest extends TestCase
/** /**
* @test * @test
*/ */
public function stopsRunningEntry() public function stopsRunningEntry(): void
{ {
$session = $this->prophesize(SessionInterface::class); $session = $this->prophesize(SessionInterface::class);
$persistence = $this->prophesize(Entries::class);
$entry = $this->prophesize(Entry::class); $entry = $this->prophesize(Entry::class);
$subject = new ActiveEntry( $subject = new ActiveEntry(
$session->reveal() $session->reveal(),
$persistence->reveal()
); );
$session->set('runningEntry', Argument::type(Entry::class))->shouldBeCalled(); $session->set('runningEntry', Argument::type(Entry::class))->shouldBeCalled();
@ -119,5 +131,6 @@ class ActiveEntryTest extends TestCase
$subject->stopRunning(); $subject->stopRunning();
$session->remove('runningEntry')->shouldBeCalled(); $session->remove('runningEntry')->shouldBeCalled();
$persistence->add($entry->reveal())->shouldBeCalled();
} }
} }