Skip to content

Instantly share code, notes, and snippets.

@sevein
Created July 23, 2026 07:15
Show Gist options
  • Select an option

  • Save sevein/312c983195e64fd4a13c2cffd3e7682d to your computer and use it in GitHub Desktop.

Select an option

Save sevein/312c983195e64fd4a13c2cffd3e7682d to your computer and use it in GitHub Desktop.
<?php
use PHPUnit\Framework\TestCase;
require_once 'plugins/sfIsadPlugin/lib/sfIsadPlugin.class.php';
/**
* Exercise every create, update, delete, and no-op path in the
* languageNotes setter.
*
* @covers \sfIsadPlugin::__set
*
* @internal
*/
class sfIsadPluginTest extends TestCase
{
/**
* Each row describes the starting notes, submitted value, and expected
* state after the setter runs.
*/
private const SET_LANGUAGE_NOTES_CASES = [
'leave missing note empty' => [
'initialContents' => [],
'value' => '',
'expectedDeleteCalls' => 0,
'expectedContents' => [],
],
'create missing note' => [
'initialContents' => [],
'value' => 'New note',
'expectedDeleteCalls' => 0,
'expectedContents' => ['New note'],
],
'delete existing note' => [
'initialContents' => ['Existing note'],
'value' => '',
'expectedDeleteCalls' => 1,
'expectedContents' => ['Existing note'],
],
'update existing note' => [
'initialContents' => ['Existing note'],
'value' => 'Updated note',
'expectedDeleteCalls' => 0,
'expectedContents' => ['Updated note'],
],
];
/**
* @dataProvider setLanguageNotesProvider
*
* @param string[] $initialContents Contents of notes present before the setter runs
* @param string[] $expectedContents Contents expected after the setter runs
*/
public function testSetLanguageNotes(
array $initialContents,
string $value,
int $expectedDeleteCalls,
array $expectedContents
): void {
$notes = array_map(static function (string $content): sfIsadPluginTestNote {
$note = new sfIsadPluginTestNote();
$note->typeId = QubitTerm::LANGUAGE_NOTE_ID;
$note->content = $content;
return $note;
}, $initialContents);
$resource = new sfIsadPluginTestResource($notes);
$plugin = new sfIsadPlugin($resource);
$plugin->languageNotes = $value;
$actualNotes = iterator_to_array($resource->notes);
$actualContents = array_map(
static fn (QubitNote $note): string => $note->content,
$actualNotes
);
$actualTypeIds = array_map(
static fn (QubitNote $note): int => $note->typeId,
$actualNotes
);
$actualDeleteCalls = array_sum(array_map(
static fn (sfIsadPluginTestNote $note): int => $note->deleteCalls,
$notes
));
$this->assertSame($expectedContents, $actualContents);
$this->assertSame(
array_fill(0, count($expectedContents), QubitTerm::LANGUAGE_NOTE_ID),
$actualTypeIds
);
$this->assertSame($expectedDeleteCalls, $actualDeleteCalls);
}
public function setLanguageNotesProvider(): array
{
return self::SET_LANGUAGE_NOTES_CASES;
}
}
/**
* Minimal fake resource containing only the API needed by sfIsadPlugin.
*
* The production resource normally retrieves notes through the database. This
* fake keeps them in memory so this remains a focused unit test.
*
* @internal
*/
final class sfIsadPluginTestResource
{
/** @var ArrayObject<int, QubitNote> */
public ArrayObject $notes;
/**
* @param QubitNote[] $notes notes available before the setter runs
*/
public function __construct(array $notes)
{
$this->notes = new ArrayObject($notes);
}
/**
* Mirror the production method used by sfIsadPlugin.
*
* The options are accepted for signature compatibility. Filtering is not
* needed because each test constructs exactly the notes it needs.
*
* @return ArrayObject<int, QubitNote>
*/
public function getMemoryNotesByType(array $options = []): ArrayObject
{
return $this->notes;
}
}
/**
* QubitNote spy that records deletion without accessing the database.
*
* A spy is a test double that behaves like the real object while recording an
* interaction for later assertion. All regular note behavior is inherited;
* only delete() is replaced.
*
* @internal
*/
final class sfIsadPluginTestNote extends QubitNote
{
public int $deleteCalls = 0;
/**
* Record the deletion request instead of deleting a database row.
*
* @param null|PropelPDO $connection unused connection argument kept for signature compatibility
*/
public function delete($connection = null)
{
++$this->deleteCalls;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment