Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions lib/GaletteObjectsLend/Fixtures.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

/**
* This file is part of Galette Objects Lend plugin (https://galette.eu).
* SPDX-FileCopyrightText: Copyright © 2013-2026 The Galette Team
* SPDX-License-Identifier: GPL-3.0-or-later
*/

declare(strict_types=1);

namespace GaletteObjectsLend;

use Galette\Core\Plugins\FixturesContext;
use Galette\Core\Plugins\FixturesProviderInterface;

/**
* Sample data for fixture members, run by galette:seed-fixtures
*
* @author Johan Cwiklinski <johan@x-tnd.be>
*/
class Fixtures implements FixturesProviderInterface
{
/**
* Load sample data, rents given to fixture members
*
* @param FixturesContext $context Fixtures context
*/
public function seedFixtures(FixturesContext $context): string
{
$created = (new SampleData($context->zdb))->load(array_values($context->members));
return sprintf(
'Created %d categories, %d statuses, %d objects and %d rents',
$created['categories'],
$created['statuses'],
$created['objects'],
$created['rents']
);
}

/**
* Remove sample data
*
* @param FixturesContext $context Fixtures context
*/
public function cleanFixtures(FixturesContext $context): void
{
(new SampleData($context->zdb))->remove();
}
}
102 changes: 102 additions & 0 deletions lib/GaletteObjectsLend/SampleData.php
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,108 @@ public function load(array $members): array
return $created;
}

/**
* Remove sample data, in a single transaction
*
* Objects are found by their serial number, with their rents. Categories
* and statuses are found by name, and only removed when nothing else uses
* them.
*
* @return array{categories: int, statuses: int, objects: int, rents: int} What was removed
*/
public function remove(): array
{
$data = $this->read();
$removed = ['categories' => 0, 'statuses' => 0, 'objects' => 0, 'rents' => 0];

$need_transaction = !$this->zdb->inTransaction();
try {
if ($need_transaction) {
$this->zdb->beginTransaction();
}

$select = $this->zdb->select(LEND_PREFIX . LendObject::TABLE)
->columns([LendObject::PK]);
$select->where->in('serial_number', array_column($data['objects'], 'serial'));
$objects = [];
foreach ($this->zdb->execute($select) as $row) {
$objects[] = (int)$row->{LendObject::PK};
}

if ($objects !== []) {
//objects and rents reference each other: drop current rent first
$update = $this->zdb->update(LEND_PREFIX . LendObject::TABLE)
->set([LendRent::PK => null]);
$update->where->in(LendObject::PK, $objects);
$this->zdb->execute($update);

$delete = $this->zdb->delete(LEND_PREFIX . LendRent::TABLE);
$delete->where->in(LendObject::PK, $objects);
$removed['rents'] = $this->zdb->execute($delete)->getAffectedRows();

$delete = $this->zdb->delete(LEND_PREFIX . LendObject::TABLE);
$delete->where->in(LendObject::PK, $objects);
$removed['objects'] = $this->zdb->execute($delete)->getAffectedRows();
}

$removed['categories'] = $this->removeUnused(
LEND_PREFIX . LendCategory::TABLE,
LendCategory::PK,
'name',
array_column($data['categories'], 'name'),
LEND_PREFIX . LendObject::TABLE
);
$removed['statuses'] = $this->removeUnused(
LEND_PREFIX . LendStatus::TABLE,
LendStatus::PK,
'status_text',
array_column($data['statuses'], 'text'),
LEND_PREFIX . LendRent::TABLE
);

if ($need_transaction) {
$this->zdb->commit();
}
} catch (Throwable $e) {
if ($need_transaction) {
$this->zdb->rollback();
}
throw $e;
}

return $removed;
}

/**
* Remove named rows nothing references anymore
*
* @param string $table Table name
* @param string $pk Primary key, also the referencing column
* @param string $column Name column
* @param list<string> $names Names to remove
* @param string $referencer Table referencing rows
*
* @return int Number of removed rows
*/
private function removeUnused(string $table, string $pk, string $column, array $names, string $referencer): int
{
$select = $this->zdb->select($referencer)
->columns([$pk])
->quantifier('DISTINCT');
$select->where->isNotNull($pk);
$used = [];
foreach ($this->zdb->execute($select) as $row) {
$used[] = (int)$row->{$pk};
}

$delete = $this->zdb->delete($table);
$delete->where->in($column, $names);
if ($used !== []) {
$delete->where->notIn($pk, $used);
}
return $this->zdb->execute($delete)->getAffectedRows();
}

/**
* Read and check data file
*
Expand Down
98 changes: 98 additions & 0 deletions tests/GaletteObjectsLend/tests/units/Fixtures.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<?php

/**
* This file is part of Galette Objects Lend plugin (https://galette.eu).
* SPDX-FileCopyrightText: Copyright © 2013-2026 The Galette Team
* SPDX-License-Identifier: GPL-3.0-or-later
*/

declare(strict_types=1);

namespace GaletteObjectsLend\tests\units;

use Galette\Core\Plugins\FixturesContext;
use Galette\Tests\GaletteTestCase;
use GaletteObjectsLend\Entity\LendCategory;
use GaletteObjectsLend\Entity\LendObject;
use GaletteObjectsLend\Entity\LendRent;
use GaletteObjectsLend\Entity\LendStatus;

/**
* Fixtures tests
*
* @author Johan Cwiklinski <johan@x-tnd.be>
*/
class Fixtures extends GaletteTestCase
{
protected int $seed = 20261004130000;
protected bool $load_plugins = true;

/**
* Set up tests: sample data need an empty catalog
*/
public function setUp(): void
{
parent::setUp();
$this->emptyCatalog();
}

/**
* Cleanup after each test method
*/
public function tearDown(): void
{
$this->emptyCatalog();
parent::tearDown();
}

/**
* Remove objects, rents, categories and statuses
*/
private function emptyCatalog(): void
{
$this->zdb->execute($this->zdb->update(LEND_PREFIX . LendObject::TABLE)->set([LendRent::PK => null]));
foreach ([LendRent::TABLE, LendObject::TABLE, LendCategory::TABLE, LendStatus::TABLE] as $table) {
$this->zdb->execute($this->zdb->delete(LEND_PREFIX . $table));
}
}

/**
* Test seeding and cleaning fixtures
*/
public function testFixtures(): void
{
$member_one = $this->getMemberOne();
$member_two = $this->getMemberTwo();
$this->logSuperAdmin();

$context = new FixturesContext(
zdb: $this->zdb,
login: $this->login,
preferences: $this->preferences,
history: $this->history,
plugins: $this->plugins,
members: ['one' => $member_one->id, 'two' => $member_two->id]
);

$fixtures = new \GaletteObjectsLend\Fixtures();
$this->assertMatchesRegularExpression(
'/^Created 5 categories, 6 statuses, \d+ objects and \d+ rents$/',
$fixtures->seedFixtures($context)
);

//rents are given to context members only
$select = $this->zdb->select(LEND_PREFIX . LendRent::TABLE)->columns(['adherent_id'])->quantifier('DISTINCT');
$select->where->isNotNull('adherent_id');
$members = [];
foreach ($this->zdb->execute($select) as $row) {
$members[] = (int)$row->adherent_id;
}
sort($members);
$this->assertSame([$member_one->id, $member_two->id], $members);

$fixtures->cleanFixtures($context);
foreach ([LendRent::TABLE, LendObject::TABLE, LendCategory::TABLE, LendStatus::TABLE] as $table) {
$this->assertSame(0, $this->zdb->execute($this->zdb->select(LEND_PREFIX . $table))->count(), $table);
}
}
}
52 changes: 52 additions & 0 deletions tests/GaletteObjectsLend/tests/units/SampleData.php
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,58 @@ public function testLoad(): void
}
}

/**
* Remove sample data, keeping what is not part of it
*/
public function testRemove(): void
{
$data = json_decode(
(string)file_get_contents(\GaletteObjectsLend\SampleData::FILE),
true,
flags: JSON_THROW_ON_ERROR
);
$this->assertIsArray($data);

$sample = new \GaletteObjectsLend\SampleData($this->zdb);
$created = $sample->load([(int)$this->getMemberOne()->id]);

//an object of the user, in a sample category and rented with a sample status
$select = $this->zdb->select(LEND_PREFIX . LendCategory::TABLE)
->where(['name' => $data['categories'][0]['name']]);
$category_id = (int)$this->zdb->execute($select)->current()->{LendCategory::PK};
$select = $this->zdb->select(LEND_PREFIX . LendStatus::TABLE)
->where(['status_text' => $data['statuses'][0]['text']]);
$status_id = (int)$this->zdb->execute($select)->current()->{LendStatus::PK};

$object = new LendObject($this->zdb);
$object->setName('User object')->setSerialNumber('USER-001')->setCategoryId($category_id);
$object->store();
$rent = new LendRent($this->zdb);
$rent->setObjectId((int)$object->getId())->setStatusId($status_id)->setComments('');
$rent->store();

$removed = $sample->remove();
$this->assertSame(
[
'categories' => $created['categories'] - 1,
'statuses' => $created['statuses'] - 1,
'objects' => $created['objects'],
'rents' => $created['rents']
],
$removed
);
$this->assertSame(1, $this->countRows(LendObject::TABLE));
$this->assertSame(1, $this->countRows(LendRent::TABLE));
$this->assertSame(1, $this->countRows(LendCategory::TABLE));
$this->assertSame(1, $this->countRows(LendStatus::TABLE));

//nothing left to remove
$this->assertSame(
['categories' => 0, 'statuses' => 0, 'objects' => 0, 'rents' => 0],
$sample->remove()
);
}

/**
* Rents are anonymous without members
*/
Expand Down
Loading