diff --git a/phpunit.xml b/phpunit.xml
index 0949fa0..beaa135 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -4,6 +4,9 @@
src/
+
+ src/EnumServiceProvider.php
+
@@ -15,4 +18,9 @@
./tests/
+
+
+
+
+
diff --git a/src/BaseEnumRoutable.php b/src/BaseEnumRoutable.php
index afb39be..d618b10 100644
--- a/src/BaseEnumRoutable.php
+++ b/src/BaseEnumRoutable.php
@@ -21,7 +21,9 @@ abstract class BaseEnumRoutable extends BaseEnum implements UrlRoutable
*/
public function getRouteKey()
{
+ // @codeCoverageIgnoreStart
throw new NotImplementedException('getRouteKey not implemented, should not be used.');
+ // @codeCoverageIgnoreEnd
}
/**
@@ -31,7 +33,9 @@ abstract class BaseEnumRoutable extends BaseEnum implements UrlRoutable
*/
public function getRouteKeyName()
{
+ // @codeCoverageIgnoreStart
throw new NotImplementedException('getRouteKeyName not implemented, should not be used.');
+ // @codeCoverageIgnoreEnd
}
/**
@@ -72,6 +76,8 @@ abstract class BaseEnumRoutable extends BaseEnum implements UrlRoutable
*/
public function resolveChildRouteBinding($childType, $value, $field)
{
+ // @codeCoverageIgnoreStart
throw new NotImplementedException('resolveChildRouteBinding not implemented, should not be used.');
+ // @codeCoverageIgnoreEnd
}
}
diff --git a/tests/BaseEnumRoutableTest.php b/tests/BaseEnumRoutableTest.php
new file mode 100644
index 0000000..9d353ed
--- /dev/null
+++ b/tests/BaseEnumRoutableTest.php
@@ -0,0 +1,247 @@
+alias(Alarm::class, 'Alarm');
+ $app->alias(Journal::class, 'Journal');
+ $app->alias(Report::class, 'Report');
+ $app->alias(Days::class, 'Days');
+ $app->alias(ContactFormCivility::class, 'ContactFormCivility');
+ $app->alias(ForceStringsFromInteger::class, 'ForceStringsFromInteger');
+ $app->alias(NotRoutableEnum::class, 'NotRoutableEnum');
+ }
+
+ /**
+ * Get package providers.
+ *
+ * @param \Illuminate\Foundation\Application $app
+ *
+ * @return array
+ */
+ // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
+ protected function getPackageProviders($app)
+ {
+ return [
+ 'Kwaadpepper\Enum\EnumServiceProvider',
+ ];
+ }
+
+
+ /**
+ * Defines migrations
+ *
+ * @return void
+ */
+ protected function defineDatabaseMigrations()
+ {
+ Schema::create('reports', function (Blueprint $table) {
+ $table->bigIncrements('id');
+ $table->smallInteger('day');
+ });
+ Schema::create('alarms', function (Blueprint $table) {
+ $table->bigIncrements('id');
+ $table->smallInteger('day');
+ });
+ Schema::create('journals', function (Blueprint $table) {
+ $table->bigIncrements('id');
+ $table->smallInteger('day');
+ });
+ }
+
+ /**
+ * Defines seeders
+ *
+ * @return void
+ */
+ protected function defineDatabaseSeeders()
+ {
+ Report::create(['day' => Days::none()]);
+ Report::create(['day' => Days::mon()]);
+ Report::create(['day' => Days::tue()]);
+ Report::create(['day' => Days::wed()]);
+ Report::create(['day' => Days::thu()]);
+ Report::create(['day' => Days::fri()]);
+ Report::create(['day' => Days::sat()]);
+ Report::create(['day' => Days::sun()]);
+ Alarm::create(['day' => Days::none()]);
+ Alarm::create(['day' => Days::mon()]);
+ Alarm::create(['day' => Days::tue()]);
+ Alarm::create(['day' => Days::wed()]);
+ Alarm::create(['day' => Days::thu()]);
+ Alarm::create(['day' => Days::fri()]);
+ Alarm::create(['day' => Days::sat()]);
+ Alarm::create(['day' => Days::sun()]);
+ Journal::create(['day' => Days::none()]);
+ Journal::create(['day' => Days::mon()]);
+ Journal::create(['day' => Days::tue()]);
+ Journal::create(['day' => Days::wed()]);
+ Journal::create(['day' => Days::thu()]);
+ Journal::create(['day' => Days::fri()]);
+ Journal::create(['day' => Days::sat()]);
+ Journal::create(['day' => Days::sun()]);
+ }
+
+ /**
+ * Define routes setup.
+ *
+ * @param \Illuminate\Routing\Router $router
+ *
+ * @return void
+ */
+ protected function defineRoutes($router)
+ {
+ $router->get('/journals/{journal}', function (Journal $journal) {
+ return response()->json([$journal->day]);
+ })->middleware('bindings');
+ $router->get('/reports/{report}', function (Report $report) {
+ return response()->json([$report->day]);
+ })->middleware('bindings');
+ $router->get('/alarms/{alarm}', function (Alarm $alarm) {
+ return response()->json([$alarm->day]);
+ })->middleware('bindings');
+ $router->get('/days/{day}', function (Days $day) {
+ return response()->json([$day]);
+ })->middleware('bindings');
+ $router->get('/day-validation', function (PassDayRequest $request) {
+ return response()->json([Days::make((int)$request->day)]);
+ })->middleware('bindings');
+ $router->get('/civilities/{civility}', function (ContactFormCivility $civility) {
+ return response()->json([$civility]);
+ })->middleware('bindings');
+ $router->get('/force/{force}', function (ForceStringsFromInteger $force) {
+ return response()->json([$force]);
+ })->middleware('bindings');
+ $router->get('/notroutable/{notroutable}', function (NotRoutableEnum $notroutable) {
+ return response()->json([$notroutable]);
+ })->middleware('bindings');
+ }
+
+ public function testRoutesDays()
+ {
+ $response = $this->call('GET', sprintf('/days/%s', Days::none()->value));
+ $response->assertOk();
+ $response->assertExactJson([Days::none()]);
+ $response = $this->call('GET', sprintf('/days/%s', Days::mon()->value));
+ $response->assertOk();
+ $response->assertExactJson([Days::mon()]);
+ }
+
+ public function testRoutesCivilities()
+ {
+ $response = $this->call('GET', sprintf('/civilities/%s', ContactFormCivility::mme()->value));
+ $response->assertOk();
+ $response->assertExactJson([ContactFormCivility::mme()]);
+ }
+
+ public function testRoutesWithEnumIntString()
+ {
+ $response = $this->call('GET', sprintf('/force/%s', ForceStringsFromInteger::valueB()->value));
+ $response->assertOk();
+ $response->assertExactJson([ForceStringsFromInteger::valueB()]);
+ }
+
+ public function testRoutesWithEnumNotFound()
+ {
+ $response = $this->call('GET', '/force/6');
+ $response->assertNotFound();
+ }
+
+ public function testRoutesWithEnumAsPrimaryKey()
+ {
+ $response = $this->call('GET', sprintf('/reports/%s', Days::none()->value));
+ $response->assertOk();
+ $response = $this->call('GET', sprintf('/reports/%s', Days::mon()->value));
+ $response->assertOk();
+ $response = $this->call('GET', sprintf('/reports/%s', Days::tue()->value));
+ $response->assertOk();
+ $response = $this->call('GET', sprintf('/reports/%s', Days::fri()->value));
+ $response->assertOk();
+ $response = $this->call('GET', sprintf('/reports/%s', 9999));
+ $response->assertNotFound();
+ }
+
+ public function testRoutesWithEnumAsPrimaryKeyWithoutCastEnums()
+ {
+ $response = $this->call('GET', sprintf('/alarms/%s', Days::none()->value));
+ $response->assertOk();
+ $response = $this->call('GET', sprintf('/alarms/%s', Days::mon()->value));
+ $response->assertOk();
+ $response = $this->call('GET', sprintf('/alarms/%s', Days::tue()->value));
+ $response->assertOk();
+ $response = $this->call('GET', sprintf('/alarms/%s', Days::fri()->value));
+ $response->assertOk();
+ $response = $this->call('GET', sprintf('/alarms/%s', 9999));
+ $response->assertNotFound();
+ }
+
+ public function testRoutesWithEnumAsPrimaryKeyWithoutCastEnumsAndCast()
+ {
+ $response = $this->call('GET', sprintf('/journals/%s', Days::none()->value));
+ $response->assertOk();
+ $response = $this->call('GET', sprintf('/journals/%s', Days::mon()->value));
+ $response->assertOk();
+ $response = $this->call('GET', sprintf('/journals/%s', Days::tue()->value));
+ $response->assertOk();
+ $response = $this->call('GET', sprintf('/journals/%s', Days::fri()->value));
+ $response->assertOk();
+ $response = $this->call('GET', sprintf('/journals/%s', 9999));
+ $response->assertNotFound();
+ }
+
+ public function testRules()
+ {
+ $rule = new EnumIsValidRule(Days::class);
+ $this->assertTrue($rule->passes('day', Days::mon()->value));
+ $this->assertFalse($rule->passes('day', 9999));
+ $this->assertFalse($rule->passes('day', Days::mon()));
+ }
+
+ public function testRulesRequest()
+ {
+ $response = $this->call('GET', sprintf('/day-validation?day=%s', Days::fri()->value));
+ $response->assertOk();
+ }
+
+ public function testRulesRequestFail()
+ {
+ $response = $this->json('GET', sprintf('/day-validation?day=%s', '"!:'));
+ $response->assertJsonValidationErrors('day');
+ }
+
+ public function testRulesRequestInvalid()
+ {
+ $this->expectException(UnknownEnumClass::class);
+ new EnumIsValidRule(SplFileObject::class);
+ }
+
+ public function testNotRoutableEnum()
+ {
+ $response = $this->json('GET', sprintf('/notroutable/%s', '1'));
+ $response->assertOk();
+ }
+}
diff --git a/tests/BaseEnumTest.php b/tests/BaseEnumTest.php
new file mode 100644
index 0000000..df3221b
--- /dev/null
+++ b/tests/BaseEnumTest.php
@@ -0,0 +1,175 @@
+assertEquals('Monday', $mon->label, 'Days::mon()->value should be equals to `Monday`');
+ $this->assertEquals(2, $mon->value, 'Days::mon()->value should be equals to `2`');
+ $this->assertClassHasAttribute('value', Days::class, 'Days should have a value property');
+ $this->assertClassHasAttribute('label', Days::class, 'Days should have a label property');
+
+ // undef prop test
+ $exception = null;
+ try {
+ $mon->propthatdontexists;
+ } catch (Exception $e) {
+ $exception = $e;
+ } finally {
+ $this->assertEquals(
+ UnknownEnumProperty::class,
+ \is_object($exception) ? \get_class($exception) : $exception,
+ 'Accessing non existent prop on base enum should throw an UnknownEnumProperty exception'
+ );
+ }
+
+ $this->assertEquals('Days', $mon->getEnumName(), 'getEnumName should return the enum class name');
+ $this->assertEquals('mon', $mon->getDefinition(), 'Days::mon() definition should be `mon`');
+ $this->assertFalse($mon->equals($tue), 'Days::mon() should not be equals to Days::tue()');
+ $this->assertTrue($mon->equals($mon), 'Days::mon() should be equals to Days::mon()');
+ $this->assertIsArray(Days::toArray(), 'toArray method should return an array');
+ $this->assertIsArray(Days::toValues(), 'toValues method should return an array');
+ $this->assertIsArray(Days::toLabels(), 'toLabels method should return an array');
+ $this->assertEquals([
+ 0 => 1,
+ 1 => 2,
+ 2 => 4,
+ 3 => 8,
+ 4 => 16,
+ 5 => 32,
+ 6 => 64,
+ 7 => 128
+ ], Days::toValues(), 'Days::toValues method should return an integer array');
+ $this->assertEquals([
+ 0 => 'None',
+ 1 => 'Monday',
+ 2 => 'Tuesday',
+ 3 => 'Wednesday',
+ 4 => 'Thursday',
+ 5 => 'Friday',
+ 6 => 'Saturday',
+ 7 => 'Sunday'
+ ], Days::toLabels(), 'Days::toLabels method should return a string array');
+ $this->assertEquals([
+ 0 => Days::none(),
+ 1 => Days::mon(),
+ 2 => Days::tue(),
+ 3 => Days::wed(),
+ 4 => Days::thu(),
+ 5 => Days::fri(),
+ 6 => Days::sat(),
+ 7 => Days::sun()
+ ], Days::toArray(), 'Days::toLabels method should return an enum array');
+ $this->assertEquals(
+ (string)ForceStringsFromInteger::valueA()->value,
+ (string)ForceStringsFromInteger::valueA(),
+ 'BaseEnum::__toString should return the value as a string'
+ );
+ }
+
+ public function testCannotAssignAnEnumValue()
+ {
+ $this->expectError();
+ $enum = Days::mon();
+ $enum->value = 'anything';
+ }
+
+ public function testCannotAssignAnEnumLabel()
+ {
+ $this->expectError();
+ $enum = Days::mon();
+ $enum->label = 'anything';
+ }
+
+ /**
+ * This method is here for code coverage
+ *
+ * @return void
+ */
+ public function testHackingEnumValueCouldConfuseCache()
+ {
+ $enum = Days::mon();
+ $refClass = new ReflectionClass($enum);
+ $property = $refClass->getProperty('value');
+ $property->setAccessible(true);
+ $property->setValue($enum, 'anything');
+ $this->assertEquals('anything', $enum->value);
+ $this->assertNull(
+ $enum->getDefinition(),
+ 'BaseEnum::getDefinition should be null when hacking var consing cache'
+ );
+ }
+
+ public function testValidEnumSet()
+ {
+ $this->expectNotToPerformAssertions();
+ $report = new Report();
+ $report->day = Days::mon();
+ $report->day = Days::mon()->value;
+ }
+
+ public function testInvalidEnumSet()
+ {
+ $this->expectException(BadMethodCallException::class);
+ $report = new Report();
+ $report->day = Days::mon()->label;
+ }
+
+ public function testInvalidEnumSetWithFloat()
+ {
+ $this->expectException(TypeError::class);
+ $report = new Report();
+ $report->day = 0.1;
+ }
+
+ public function testInvalidEnumSetNotAuthorizedType()
+ {
+ $this->expectException(TypeError::class);
+ $report = new Report();
+ $report->day = new stdClass();
+ }
+
+ public function testInvalidEnumCanSetNull()
+ {
+ $this->expectNotToPerformAssertions();
+ $report = new Report();
+ $report->day = null;
+ }
+
+ public function testDuplicatedValues()
+ {
+ $this->expectException(DuplicateValuesException::class);
+ DuplicatedValues::one();
+ }
+
+ public function testDuplicatedLabels()
+ {
+ $this->expectException(DuplicateLabelsException::class);
+ DuplicatedLabels::one();
+ }
+}
diff --git a/tests/EnumTest.php b/tests/EnumTest.php
deleted file mode 100644
index f3fcf77..0000000
--- a/tests/EnumTest.php
+++ /dev/null
@@ -1,121 +0,0 @@
-alias(Days::class, 'Days');
- $app->alias(ContactFormCivility::class, 'ContactFormCivility');
- $app->alias(ForceStringsFromInteger::class, 'ForceStringsFromInteger');
- }
-
- /**
- * Define routes setup.
- *
- * @param \Illuminate\Routing\Router $router
- *
- * @return void
- */
- protected function defineRoutes($router)
- {
- $router->get('/days/{day}', function (Days $day) {
- return response()->json([$day]);
- })->middleware('bindings');
- $router->get('/civilities/{civility}', function (ContactFormCivility $civility) {
- return response()->json([$civility]);
- })->middleware('bindings');
- $router->get('/force/{force}', function (ForceStringsFromInteger $force) {
- return response()->json([$force]);
- })->middleware('bindings');
- }
-
-
- public function testEnum()
- {
- $mon = Days::mon();
- $tue = Days::tue();
- Days::toArray();
- Days::toValues();
- Days::toLabels();
- json_encode($tue);
- $mon->getDefinition();
- $this->assertClassHasAttribute('value', Days::class);
- $this->assertClassHasAttribute('label', Days::class);
- $this->assertFalse($mon->equals($tue));
- $this->assertTrue($mon->equals($mon));
- $this->assertIsArray(Days::toArray());
- $this->assertIsArray(Days::toValues());
- $this->assertIsArray(Days::toLabels());
- $this->assertEquals([
- 0 => 0,
- 1 => 1,
- 2 => 2,
- 3 => 4,
- 4 => 8,
- 5 => 16,
- 6 => 32,
- 7 => 64
- ], Days::toValues());
- $this->assertEquals([
- 0 => 'None',
- 1 => 'Monday',
- 2 => 'Tuesday',
- 3 => 'Wednesday',
- 4 => 'Thursday',
- 5 => 'Friday',
- 6 => 'Saturday',
- 7 => 'Sunday'
- ], Days::toLabels());
- $this->assertEquals([
- 0 => Days::none(),
- 1 => Days::mon(),
- 2 => Days::tue(),
- 3 => Days::wed(),
- 4 => Days::thu(),
- 5 => Days::fri(),
- 6 => Days::sat(),
- 7 => Days::sun()
- ], Days::toArray());
- $this->assertEquals('Monday', (string)$mon);
- }
-
- public function testRoutesDays()
- {
- $response = $this->call('GET', '/days/1');
- $response->assertOk();
- $response->assertExactJson([Days::mon()]);
- $response = $this->call('GET', '/days/2');
- $response->assertOk();
- $response->assertExactJson([Days::tue()]);
- }
-
- public function testRoutesCivilities()
- {
- $response = $this->call('GET', '/civilities/mme');
- $response->assertOk();
- $response->assertExactJson([ContactFormCivility::mme()]);
- }
-
- public function testRoutesWithEnumIntString()
- {
- $response = $this->call('GET', '/force/1');
- $response->assertOk();
- $response->assertExactJson([ForceStringsFromInteger::valueB()]);
- }
-
- public function testRules()
- {
- $rule = new EnumIsValidRule(Days::class);
- $this->assertTrue($rule->passes('day', Days::mon()->value));
- $this->assertFalse($rule->passes('day', 9999));
- $this->assertFalse($rule->passes('day', Days::mon()));
- }
-}
diff --git a/tests/Enums/Days.php b/tests/Enums/Days.php
index 0de7126..db83d77 100644
--- a/tests/Enums/Days.php
+++ b/tests/Enums/Days.php
@@ -19,14 +19,14 @@ final class Days extends BaseEnumRoutable
protected static function values(): array
{
return [
- 'none' => 0,
- 'mon' => 1,
- 'tue' => 1 << 1,
- 'wed' => 1 << 2,
- 'thu' => 1 << 3,
- 'fri' => 1 << 4,
- 'sat' => 1 << 5,
- 'sun' => 1 << 6,
+ 'none' => 1,
+ 'mon' => 1 << 1,
+ 'tue' => 1 << 2,
+ 'wed' => 1 << 3,
+ 'thu' => 1 << 4,
+ 'fri' => 1 << 5,
+ 'sat' => 1 << 6,
+ 'sun' => 1 << 7,
];
}
diff --git a/tests/Enums/DuplicatedLabels.php b/tests/Enums/DuplicatedLabels.php
new file mode 100644
index 0000000..0001202
--- /dev/null
+++ b/tests/Enums/DuplicatedLabels.php
@@ -0,0 +1,22 @@
+ 'one',
+ 'two' => 'two',
+ 'three' => 'two'
+ ];
+ }
+}
diff --git a/tests/Enums/DuplicatedValues.php b/tests/Enums/DuplicatedValues.php
new file mode 100644
index 0000000..b7a7df8
--- /dev/null
+++ b/tests/Enums/DuplicatedValues.php
@@ -0,0 +1,22 @@
+ '0',
+ 'two' => '1',
+ 'three' => '1'
+ ];
+ }
+}
diff --git a/tests/Enums/NotRoutableEnum.php b/tests/Enums/NotRoutableEnum.php
new file mode 100644
index 0000000..50ec920
--- /dev/null
+++ b/tests/Enums/NotRoutableEnum.php
@@ -0,0 +1,22 @@
+ '0',
+ 'two' => '1',
+ 'three' => '2'
+ ];
+ }
+}
diff --git a/tests/Models/Alarm.php b/tests/Models/Alarm.php
new file mode 100644
index 0000000..c12a0ec
--- /dev/null
+++ b/tests/Models/Alarm.php
@@ -0,0 +1,23 @@
+ 'int'
+ ];
+
+ protected $enumCasts = [
+ 'day' => Days::class
+ ];
+}
diff --git a/tests/Models/Report.php b/tests/Models/Report.php
new file mode 100644
index 0000000..763d673
--- /dev/null
+++ b/tests/Models/Report.php
@@ -0,0 +1,28 @@
+ Days::class
+ ];
+}
diff --git a/tests/Requests/PassDayRequest.php b/tests/Requests/PassDayRequest.php
new file mode 100644
index 0000000..5e8fcbb
--- /dev/null
+++ b/tests/Requests/PassDayRequest.php
@@ -0,0 +1,32 @@
+ ['required', new EnumIsValidRule(Days::class)],
+ ];
+ }
+}