commit
63797d30aa
|
@ -4,23 +4,6 @@ namespace Illuminate\Cache;
|
|||
|
||||
class ApcWrapper
|
||||
{
|
||||
/**
|
||||
* Indicates if APCu is supported.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $apcu = false;
|
||||
|
||||
/**
|
||||
* Create a new APC wrapper instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->apcu = function_exists('apcu_fetch');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an item from the cache.
|
||||
*
|
||||
|
@ -29,7 +12,7 @@ class ApcWrapper
|
|||
*/
|
||||
public function get($key)
|
||||
{
|
||||
$fetchedValue = $this->apcu ? apcu_fetch($key, $success) : apc_fetch($key, $success);
|
||||
$fetchedValue = apcu_fetch($key, $success);
|
||||
|
||||
return $success ? $fetchedValue : null;
|
||||
}
|
||||
|
@ -44,7 +27,7 @@ class ApcWrapper
|
|||
*/
|
||||
public function put($key, $value, $seconds)
|
||||
{
|
||||
return $this->apcu ? apcu_store($key, $value, $seconds) : apc_store($key, $value, $seconds);
|
||||
return apcu_store($key, $value, $seconds);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -56,7 +39,7 @@ class ApcWrapper
|
|||
*/
|
||||
public function increment($key, $value)
|
||||
{
|
||||
return $this->apcu ? apcu_inc($key, $value) : apc_inc($key, $value);
|
||||
return apcu_inc($key, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -68,7 +51,7 @@ class ApcWrapper
|
|||
*/
|
||||
public function decrement($key, $value)
|
||||
{
|
||||
return $this->apcu ? apcu_dec($key, $value) : apc_dec($key, $value);
|
||||
return apcu_dec($key, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -79,7 +62,7 @@ class ApcWrapper
|
|||
*/
|
||||
public function delete($key)
|
||||
{
|
||||
return $this->apcu ? apcu_delete($key) : apc_delete($key);
|
||||
return apcu_delete($key);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -89,6 +72,6 @@ class ApcWrapper
|
|||
*/
|
||||
public function flush()
|
||||
{
|
||||
return $this->apcu ? apcu_clear_cache() : apc_clear_cache('user');
|
||||
return apcu_clear_cache();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -288,8 +288,10 @@ class CacheManager implements FactoryContract
|
|||
*/
|
||||
public function repository(Store $store, array $config = [])
|
||||
{
|
||||
return tap(new Repository($store, Arr::only($config, ['store'])), function ($repository) {
|
||||
$this->setEventDispatcher($repository);
|
||||
return tap(new Repository($store, Arr::only($config, ['store'])), function ($repository) use ($config) {
|
||||
if ($config['events'] ?? true) {
|
||||
$this->setEventDispatcher($repository);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -51,22 +51,24 @@ class Limit
|
|||
* Create a new rate limit.
|
||||
*
|
||||
* @param int $maxAttempts
|
||||
* @param int $decaySeconds
|
||||
* @return static
|
||||
*/
|
||||
public static function perSecond($maxAttempts)
|
||||
public static function perSecond($maxAttempts, $decaySeconds = 1)
|
||||
{
|
||||
return new static('', $maxAttempts, 1);
|
||||
return new static('', $maxAttempts, $decaySeconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new rate limit.
|
||||
*
|
||||
* @param int $maxAttempts
|
||||
* @param int $decayMinutes
|
||||
* @return static
|
||||
*/
|
||||
public static function perMinute($maxAttempts)
|
||||
public static function perMinute($maxAttempts, $decayMinutes = 1)
|
||||
{
|
||||
return new static('', $maxAttempts, 60);
|
||||
return new static('', $maxAttempts, 60 * $decayMinutes);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1570,6 +1570,28 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Throttle the values, releasing them at most once per the given seconds.
|
||||
*
|
||||
* @return static<TKey, TValue>
|
||||
*/
|
||||
public function throttle(float $seconds)
|
||||
{
|
||||
return new static(function () use ($seconds) {
|
||||
$microseconds = $seconds * 1_000_000;
|
||||
|
||||
foreach ($this as $key => $value) {
|
||||
$fetchedAt = $this->preciseNow();
|
||||
|
||||
yield $key => $value;
|
||||
|
||||
$sleep = $microseconds - ($this->preciseNow() - $fetchedAt);
|
||||
|
||||
$this->usleep((int) $sleep);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Flatten a multi-dimensional associative array with dots.
|
||||
*
|
||||
|
@ -1781,4 +1803,32 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable
|
|||
? Carbon::now()->timestamp
|
||||
: time();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the precise current time.
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
protected function preciseNow()
|
||||
{
|
||||
return class_exists(Carbon::class)
|
||||
? Carbon::now()->getPreciseTimestamp()
|
||||
: microtime(true) * 1_000_000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sleep for the given amount of microseconds.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function usleep(int $microseconds)
|
||||
{
|
||||
if ($microseconds <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
class_exists(Sleep::class)
|
||||
? Sleep::usleep($microseconds)
|
||||
: usleep($microseconds);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Illuminate\Support\Traits;
|
||||
|
||||
use BackedEnum;
|
||||
use CachingIterator;
|
||||
use Closure;
|
||||
use Exception;
|
||||
|
@ -410,6 +411,10 @@ trait EnumeratesValues
|
|||
*/
|
||||
public function mapInto($class)
|
||||
{
|
||||
if (is_subclass_of($class, BackedEnum::class)) {
|
||||
return $this->map(fn ($value, $key) => $class::from($value));
|
||||
}
|
||||
|
||||
return $this->map(fn ($value, $key) => new $class($value, $key));
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ use Laravel\Prompts\Prompt;
|
|||
use Laravel\Prompts\SearchPrompt;
|
||||
use Laravel\Prompts\SelectPrompt;
|
||||
use Laravel\Prompts\SuggestPrompt;
|
||||
use Laravel\Prompts\TextareaPrompt;
|
||||
use Laravel\Prompts\TextPrompt;
|
||||
use stdClass;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
|
@ -39,6 +40,12 @@ trait ConfiguresPrompts
|
|||
$prompt->validate
|
||||
));
|
||||
|
||||
TextareaPrompt::fallbackUsing(fn (TextareaPrompt $prompt) => $this->promptUntilValid(
|
||||
fn () => $this->components->ask($prompt->label, $prompt->default ?: null, multiline: true) ?? '',
|
||||
$prompt->required,
|
||||
$prompt->validate
|
||||
));
|
||||
|
||||
PasswordPrompt::fallbackUsing(fn (PasswordPrompt $prompt) => $this->promptUntilValid(
|
||||
fn () => $this->components->secret($prompt->label) ?? '',
|
||||
$prompt->required,
|
||||
|
@ -52,28 +59,16 @@ trait ConfiguresPrompts
|
|||
));
|
||||
|
||||
SelectPrompt::fallbackUsing(fn (SelectPrompt $prompt) => $this->promptUntilValid(
|
||||
fn () => $this->components->choice($prompt->label, $prompt->options, $prompt->default),
|
||||
fn () => $this->selectFallback($prompt->label, $prompt->options, $prompt->default),
|
||||
false,
|
||||
$prompt->validate
|
||||
));
|
||||
|
||||
MultiSelectPrompt::fallbackUsing(function (MultiSelectPrompt $prompt) {
|
||||
if ($prompt->default !== []) {
|
||||
return $this->promptUntilValid(
|
||||
fn () => $this->components->choice($prompt->label, $prompt->options, implode(',', $prompt->default), multiple: true),
|
||||
$prompt->required,
|
||||
$prompt->validate
|
||||
);
|
||||
}
|
||||
|
||||
return $this->promptUntilValid(
|
||||
fn () => collect($this->components->choice($prompt->label, ['' => 'None', ...$prompt->options], 'None', multiple: true))
|
||||
->reject('')
|
||||
->all(),
|
||||
$prompt->required,
|
||||
$prompt->validate
|
||||
);
|
||||
});
|
||||
MultiSelectPrompt::fallbackUsing(fn (MultiSelectPrompt $prompt) => $this->promptUntilValid(
|
||||
fn () => $this->multiselectFallback($prompt->label, $prompt->options, $prompt->default, $prompt->required),
|
||||
$prompt->required,
|
||||
$prompt->validate
|
||||
));
|
||||
|
||||
SuggestPrompt::fallbackUsing(fn (SuggestPrompt $prompt) => $this->promptUntilValid(
|
||||
fn () => $this->components->askWithCompletion($prompt->label, $prompt->options, $prompt->default ?: null) ?? '',
|
||||
|
@ -87,7 +82,7 @@ trait ConfiguresPrompts
|
|||
|
||||
$options = ($prompt->options)($query);
|
||||
|
||||
return $this->components->choice($prompt->label, $options);
|
||||
return $this->selectFallback($prompt->label, $options);
|
||||
},
|
||||
false,
|
||||
$prompt->validate
|
||||
|
@ -99,21 +94,7 @@ trait ConfiguresPrompts
|
|||
|
||||
$options = ($prompt->options)($query);
|
||||
|
||||
if ($prompt->required === false) {
|
||||
if (array_is_list($options)) {
|
||||
return collect($this->components->choice($prompt->label, ['None', ...$options], 'None', multiple: true))
|
||||
->reject('None')
|
||||
->values()
|
||||
->all();
|
||||
}
|
||||
|
||||
return collect($this->components->choice($prompt->label, ['' => 'None', ...$options], '', multiple: true))
|
||||
->reject('')
|
||||
->values()
|
||||
->all();
|
||||
}
|
||||
|
||||
return $this->components->choice($prompt->label, $options, multiple: true);
|
||||
return $this->multiselectFallback($prompt->label, $options, required: $prompt->required);
|
||||
},
|
||||
$prompt->required,
|
||||
$prompt->validate
|
||||
|
@ -192,7 +173,7 @@ trait ConfiguresPrompts
|
|||
/**
|
||||
* Get the validator instance that should be used to validate prompts.
|
||||
*
|
||||
* @param string $value
|
||||
* @param mixed $field
|
||||
* @param mixed $value
|
||||
* @param mixed $rules
|
||||
* @param array $messages
|
||||
|
@ -238,4 +219,61 @@ trait ConfiguresPrompts
|
|||
{
|
||||
Prompt::setOutput($this->output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Select fallback.
|
||||
*
|
||||
* @param string $label
|
||||
* @param array $options
|
||||
* @param string|int|null $default
|
||||
* @return string|int
|
||||
*/
|
||||
private function selectFallback($label, $options, $default = null)
|
||||
{
|
||||
$answer = $this->components->choice($label, $options, $default);
|
||||
|
||||
if (! array_is_list($options) && $answer === (string) (int) $answer) {
|
||||
return (int) $answer;
|
||||
}
|
||||
|
||||
return $answer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multi-select fallback.
|
||||
*
|
||||
* @param string $label
|
||||
* @param array $options
|
||||
* @param array $default
|
||||
* @param bool|string $required
|
||||
* @return array
|
||||
*/
|
||||
private function multiselectFallback($label, $options, $default = [], $required = false)
|
||||
{
|
||||
$default = $default !== [] ? implode(',', $default) : null;
|
||||
|
||||
if ($required === false && ! $this->laravel->runningUnitTests()) {
|
||||
$options = array_is_list($options)
|
||||
? ['None', ...$options]
|
||||
: ['' => 'None'] + $options;
|
||||
|
||||
if ($default === null) {
|
||||
$default = 'None';
|
||||
}
|
||||
}
|
||||
|
||||
$answers = $this->components->choice($label, $options, $default, null, true);
|
||||
|
||||
if (! array_is_list($options)) {
|
||||
$answers = array_map(fn ($value) => $value === (string) (int) $value ? (int) $value : $value, $answers);
|
||||
}
|
||||
|
||||
if ($required === false) {
|
||||
return array_is_list($options)
|
||||
? array_values(array_filter($answers, fn ($value) => $value !== 'None'))
|
||||
: array_filter($answers, fn ($value) => $value !== '');
|
||||
}
|
||||
|
||||
return $answers;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -154,19 +154,23 @@ class Schedule
|
|||
*/
|
||||
public function job($job, $queue = null, $connection = null)
|
||||
{
|
||||
$instance = is_string($job)
|
||||
? Container::getInstance()->make($job)
|
||||
: $job;
|
||||
$jobName = $job;
|
||||
|
||||
$name = method_exists($instance, 'displayName')
|
||||
? $instance->displayName()
|
||||
: $instance::class;
|
||||
if (! is_string($job)) {
|
||||
$jobName = method_exists($job, 'displayName')
|
||||
? $job->displayName()
|
||||
: $job::class;
|
||||
}
|
||||
|
||||
return $this->call(function () use ($instance, $queue, $connection) {
|
||||
$instance instanceof ShouldQueue
|
||||
? $this->dispatchToQueue($instance, $queue ?? $instance->queue, $connection ?? $instance->connection)
|
||||
: $this->dispatchNow($instance);
|
||||
})->name($name);
|
||||
return $this->call(function () use ($job, $queue, $connection) {
|
||||
$job = is_string($job) ? Container::getInstance()->make($job) : $job;
|
||||
|
||||
if ($job instanceof ShouldQueue) {
|
||||
$this->dispatchToQueue($job, $queue ?? $job->queue, $connection ?? $job->connection);
|
||||
} else {
|
||||
$this->dispatchNow($job);
|
||||
}
|
||||
})->name($jobName);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
namespace Illuminate\Console\View\Components;
|
||||
|
||||
use Symfony\Component\Console\Question\Question;
|
||||
|
||||
class Ask extends Component
|
||||
{
|
||||
/**
|
||||
|
@ -11,8 +13,13 @@ class Ask extends Component
|
|||
* @param string $default
|
||||
* @return mixed
|
||||
*/
|
||||
public function render($question, $default = null)
|
||||
public function render($question, $default = null, $multiline = false)
|
||||
{
|
||||
return $this->usingQuestionHelper(fn () => $this->output->ask($question, $default));
|
||||
return $this->usingQuestionHelper(
|
||||
fn () => $this->output->askQuestion(
|
||||
(new Question($question, $default))
|
||||
->setMultiline($multiline)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,10 +20,29 @@ class Choice extends Component
|
|||
{
|
||||
return $this->usingQuestionHelper(
|
||||
fn () => $this->output->askQuestion(
|
||||
(new ChoiceQuestion($question, $choices, $default))
|
||||
$this->getChoiceQuestion($question, $choices, $default)
|
||||
->setMaxAttempts($attempts)
|
||||
->setMultiselect($multiple)
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a ChoiceQuestion instance that handles array keys like Prompts.
|
||||
*
|
||||
* @param string $question
|
||||
* @param array $choices
|
||||
* @param mixed $default
|
||||
* @return \Symfony\Component\Console\Question\ChoiceQuestion
|
||||
*/
|
||||
protected function getChoiceQuestion($question, $choices, $default)
|
||||
{
|
||||
return new class($question, $choices, $default) extends ChoiceQuestion
|
||||
{
|
||||
protected function isAssoc(array $array): bool
|
||||
{
|
||||
return ! array_is_list($array);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,6 +62,10 @@ class FreshCommand extends Command
|
|||
|
||||
$database = $this->input->getOption('database');
|
||||
|
||||
if (! is_null($database)) {
|
||||
$this->migrator->setConnection($database);
|
||||
}
|
||||
|
||||
if ($this->migrator->repositoryExists()) {
|
||||
$this->newLine();
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ use Illuminate\Database\RecordsNotFoundException;
|
|||
use Illuminate\Database\UniqueConstraintViolationException;
|
||||
use Illuminate\Pagination\Paginator;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Traits\ForwardsCalls;
|
||||
use ReflectionClass;
|
||||
|
@ -137,6 +138,13 @@ class Builder implements BuilderContract
|
|||
*/
|
||||
protected $removedScopes = [];
|
||||
|
||||
/**
|
||||
* The callbacks that should be invoked after retrieving data from the database.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $afterQueryCallbacks = [];
|
||||
|
||||
/**
|
||||
* Create a new Eloquent query builder instance.
|
||||
*
|
||||
|
@ -723,7 +731,9 @@ class Builder implements BuilderContract
|
|||
$models = $builder->eagerLoadRelations($models);
|
||||
}
|
||||
|
||||
return $builder->getModel()->newCollection($models);
|
||||
return $this->applyAfterQueryCallbacks(
|
||||
$builder->getModel()->newCollection($models)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -852,6 +862,34 @@ class Builder implements BuilderContract
|
|||
return str_contains($name, '.') && str_starts_with($name, $relation.'.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a closure to be invoked after the query is executed.
|
||||
*
|
||||
* @param \Closure $callback
|
||||
* @return $this
|
||||
*/
|
||||
public function afterQuery(Closure $callback)
|
||||
{
|
||||
$this->afterQueryCallbacks[] = $callback;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the "after query" modification callbacks.
|
||||
*
|
||||
* @param mixed $result
|
||||
* @return mixed
|
||||
*/
|
||||
public function applyAfterQueryCallbacks($result)
|
||||
{
|
||||
foreach ($this->afterQueryCallbacks as $afterQueryCallback) {
|
||||
$result = $afterQueryCallback($result) ?: $result;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a lazy collection for the given query.
|
||||
*
|
||||
|
@ -860,8 +898,10 @@ class Builder implements BuilderContract
|
|||
public function cursor()
|
||||
{
|
||||
return $this->applyScopes()->query->cursor()->map(function ($record) {
|
||||
return $this->newModelInstance()->newFromBuilder($record);
|
||||
});
|
||||
$model = $this->newModelInstance()->newFromBuilder($record);
|
||||
|
||||
return $this->applyAfterQueryCallbacks($this->newModelInstance()->newCollection([$model]))->first();
|
||||
})->reject(fn ($model) => is_null($model));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -900,9 +940,11 @@ class Builder implements BuilderContract
|
|||
return $results;
|
||||
}
|
||||
|
||||
return $results->map(function ($value) use ($column) {
|
||||
return $this->model->newFromBuilder([$column => $value])->{$column};
|
||||
});
|
||||
return $this->applyAfterQueryCallbacks(
|
||||
$results->map(function ($value) use ($column) {
|
||||
return $this->model->newFromBuilder([$column => $value])->{$column};
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Illuminate\Database\Eloquent\Relations;
|
||||
|
||||
use BackedEnum;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
@ -375,7 +376,9 @@ class BelongsTo extends Relation
|
|||
*/
|
||||
protected function getForeignKeyFrom(Model $model)
|
||||
{
|
||||
return $model->{$this->foreignKey};
|
||||
$foreignKey = $model->{$this->foreignKey};
|
||||
|
||||
return $foreignKey instanceof BackedEnum ? $foreignKey->value : $foreignKey;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -883,7 +883,9 @@ class BelongsToMany extends Relation
|
|||
$models = $builder->eagerLoadRelations($models);
|
||||
}
|
||||
|
||||
return $this->related->newCollection($models);
|
||||
return $this->query->applyAfterQueryCallbacks(
|
||||
$this->related->newCollection($models)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -505,7 +505,9 @@ class HasManyThrough extends Relation
|
|||
$models = $builder->eagerLoadRelations($models);
|
||||
}
|
||||
|
||||
return $this->related->newCollection($models);
|
||||
return $this->query->applyAfterQueryCallbacks(
|
||||
$this->related->newCollection($models)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -208,6 +208,13 @@ class Builder implements BuilderContract
|
|||
*/
|
||||
public $beforeQueryCallbacks = [];
|
||||
|
||||
/**
|
||||
* The callbacks that should be invoked after retrieving data from the database.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $afterQueryCallbacks = [];
|
||||
|
||||
/**
|
||||
* All of the available clause operators.
|
||||
*
|
||||
|
@ -1215,7 +1222,7 @@ class Builder implements BuilderContract
|
|||
$values = Arr::flatten($values);
|
||||
|
||||
foreach ($values as &$value) {
|
||||
$value = (int) $value;
|
||||
$value = (int) ($value instanceof BackedEnum ? $value->value : $value);
|
||||
}
|
||||
|
||||
$this->wheres[] = compact('type', 'column', 'values', 'boolean');
|
||||
|
@ -2770,6 +2777,34 @@ class Builder implements BuilderContract
|
|||
$this->beforeQueryCallbacks = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a closure to be invoked after the query is executed.
|
||||
*
|
||||
* @param \Closure $callback
|
||||
* @return $this
|
||||
*/
|
||||
public function afterQuery(Closure $callback)
|
||||
{
|
||||
$this->afterQueryCallbacks[] = $callback;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the "after query" modification callbacks.
|
||||
*
|
||||
* @param mixed $result
|
||||
* @return mixed
|
||||
*/
|
||||
public function applyAfterQueryCallbacks($result)
|
||||
{
|
||||
foreach ($this->afterQueryCallbacks as $afterQueryCallback) {
|
||||
$result = $afterQueryCallback($result) ?: $result;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the SQL representation of the query.
|
||||
*
|
||||
|
@ -2884,9 +2919,9 @@ class Builder implements BuilderContract
|
|||
return $this->processor->processSelect($this, $this->runSelect());
|
||||
}));
|
||||
|
||||
return isset($this->groupLimit)
|
||||
? $this->withoutGroupLimitKeys($items)
|
||||
: $items;
|
||||
return $this->applyAfterQueryCallbacks(
|
||||
isset($this->groupLimit) ? $this->withoutGroupLimitKeys($items) : $items
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3114,11 +3149,13 @@ class Builder implements BuilderContract
|
|||
$this->columns = ['*'];
|
||||
}
|
||||
|
||||
return new LazyCollection(function () {
|
||||
return (new LazyCollection(function () {
|
||||
yield from $this->connection->cursor(
|
||||
$this->toSql(), $this->getBindings(), ! $this->useWritePdo
|
||||
);
|
||||
});
|
||||
}))->map(function ($item) {
|
||||
return $this->applyAfterQueryCallbacks(collect([$item]))->first();
|
||||
})->reject(fn ($item) => is_null($item));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3167,9 +3204,11 @@ class Builder implements BuilderContract
|
|||
|
||||
$key = $this->stripTableForPluck($key);
|
||||
|
||||
return is_array($queryResult[0])
|
||||
return $this->applyAfterQueryCallbacks(
|
||||
is_array($queryResult[0])
|
||||
? $this->pluckFromArrayColumn($queryResult, $column, $key)
|
||||
: $this->pluckFromObjectColumn($queryResult, $column, $key);
|
||||
: $this->pluckFromObjectColumn($queryResult, $column, $key)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -125,7 +125,7 @@ class SqlServerGrammar extends Grammar
|
|||
.'join sys.schemas as scm on obj.schema_id = scm.schema_id '
|
||||
.'left join sys.default_constraints def on col.default_object_id = def.object_id and col.object_id = def.parent_object_id '
|
||||
."left join sys.extended_properties as prop on obj.object_id = prop.major_id and col.column_id = prop.minor_id and prop.name = 'MS_Description' "
|
||||
.'left join sys.computed_columns as com on col.column_id = com.column_id '
|
||||
.'left join sys.computed_columns as com on col.column_id = com.column_id and col.object_id = com.object_id '
|
||||
."where obj.type in ('U', 'V') and obj.name = %s and scm.name = %s "
|
||||
.'order by col.column_id',
|
||||
$this->quoteString($table),
|
||||
|
|
|
@ -163,10 +163,19 @@ class Encrypter implements EncrypterContract, StringEncrypter
|
|||
$tag = empty($payload['tag']) ? null : base64_decode($payload['tag'])
|
||||
);
|
||||
|
||||
$foundValidMac = false;
|
||||
|
||||
// Here we will decrypt the value. If we are able to successfully decrypt it
|
||||
// we will then unserialize it and return it out to the caller. If we are
|
||||
// unable to decrypt this value we will throw out an exception message.
|
||||
foreach ($this->getAllKeys() as $key) {
|
||||
if (
|
||||
$this->shouldValidateMac() &&
|
||||
! ($foundValidMac = $foundValidMac || $this->validMacForKey($payload, $key))
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$decrypted = \openssl_decrypt(
|
||||
$payload['value'], strtolower($this->cipher), $key, 0, $iv, $tag ?? ''
|
||||
);
|
||||
|
@ -176,7 +185,11 @@ class Encrypter implements EncrypterContract, StringEncrypter
|
|||
}
|
||||
}
|
||||
|
||||
if ($decrypted === false) {
|
||||
if ($this->shouldValidateMac() && ! $foundValidMac) {
|
||||
throw new DecryptException('The MAC is invalid.');
|
||||
}
|
||||
|
||||
if (($decrypted ?? false) === false) {
|
||||
throw new DecryptException('Could not decrypt the data.');
|
||||
}
|
||||
|
||||
|
@ -232,10 +245,6 @@ class Encrypter implements EncrypterContract, StringEncrypter
|
|||
throw new DecryptException('The payload is invalid.');
|
||||
}
|
||||
|
||||
if (! self::$supportedCiphers[strtolower($this->cipher)]['aead'] && ! $this->validMac($payload)) {
|
||||
throw new DecryptException('The MAC is invalid.');
|
||||
}
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
|
@ -265,24 +274,28 @@ class Encrypter implements EncrypterContract, StringEncrypter
|
|||
}
|
||||
|
||||
/**
|
||||
* Determine if the MAC for the given payload is valid.
|
||||
* Determine if the MAC for the given payload is valid for the primary key.
|
||||
*
|
||||
* @param array $payload
|
||||
* @return bool
|
||||
*/
|
||||
protected function validMac(array $payload)
|
||||
{
|
||||
foreach ($this->getAllKeys() as $key) {
|
||||
$valid = hash_equals(
|
||||
$this->hash($payload['iv'], $payload['value'], $key), $payload['mac']
|
||||
);
|
||||
return $this->validMacForKey($payload, $this->key);
|
||||
}
|
||||
|
||||
if ($valid === true) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
/**
|
||||
* Determine if the MAC is valid for the given payload and key.
|
||||
*
|
||||
* @param array $payload
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
protected function validMacForKey($payload, $key)
|
||||
{
|
||||
return hash_equals(
|
||||
$this->hash($payload['iv'], $payload['value'], $key), $payload['mac']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -302,6 +315,16 @@ class Encrypter implements EncrypterContract, StringEncrypter
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if we should validate the MAC while decrypting.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function shouldValidateMac()
|
||||
{
|
||||
return ! self::$supportedCiphers[strtolower($this->cipher)]['aead'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the encryption key that the encrypter is currently using.
|
||||
*
|
||||
|
|
|
@ -108,7 +108,7 @@ class ApplicationBuilder
|
|||
}
|
||||
|
||||
/**
|
||||
* Register the braodcasting services for the application.
|
||||
* Register the broadcasting services for the application.
|
||||
*
|
||||
* @param string $channels
|
||||
* @param array $attributes
|
||||
|
|
|
@ -33,7 +33,7 @@ class EventServiceProvider extends ServiceProvider
|
|||
protected $observers = [];
|
||||
|
||||
/**
|
||||
* Indiates if events should be discovered.
|
||||
* Indicates if events should be discovered.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
|
|
|
@ -4,6 +4,8 @@ namespace Illuminate\Foundation\Testing\Concerns;
|
|||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Debug\ExceptionHandler;
|
||||
use Illuminate\Support\Facades\Exceptions;
|
||||
use Illuminate\Support\Testing\Fakes\ExceptionHandlerFake;
|
||||
use Illuminate\Testing\Assert;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Symfony\Component\Console\Application as ConsoleApplication;
|
||||
|
@ -27,7 +29,11 @@ trait InteractsWithExceptionHandling
|
|||
protected function withExceptionHandling()
|
||||
{
|
||||
if ($this->originalExceptionHandler) {
|
||||
$this->app->instance(ExceptionHandler::class, $this->originalExceptionHandler);
|
||||
$currentExceptionHandler = app(ExceptionHandler::class);
|
||||
|
||||
$currentExceptionHandler instanceof ExceptionHandlerFake
|
||||
? $currentExceptionHandler->setHandler($this->originalExceptionHandler)
|
||||
: $this->app->instance(ExceptionHandler::class, $this->originalExceptionHandler);
|
||||
}
|
||||
|
||||
return $this;
|
||||
|
@ -63,10 +69,14 @@ trait InteractsWithExceptionHandling
|
|||
protected function withoutExceptionHandling(array $except = [])
|
||||
{
|
||||
if ($this->originalExceptionHandler == null) {
|
||||
$this->originalExceptionHandler = app(ExceptionHandler::class);
|
||||
$currentExceptionHandler = app(ExceptionHandler::class);
|
||||
|
||||
$this->originalExceptionHandler = $currentExceptionHandler instanceof ExceptionHandlerFake
|
||||
? $currentExceptionHandler->handler()
|
||||
: $currentExceptionHandler;
|
||||
}
|
||||
|
||||
$this->app->instance(ExceptionHandler::class, new class($this->originalExceptionHandler, $except) implements ExceptionHandler
|
||||
$exceptionHandler = new class($this->originalExceptionHandler, $except) implements ExceptionHandler, WithoutExceptionHandlingHandler
|
||||
{
|
||||
protected $except;
|
||||
protected $originalHandler;
|
||||
|
@ -145,7 +155,13 @@ trait InteractsWithExceptionHandling
|
|||
{
|
||||
(new ConsoleApplication)->renderThrowable($e, $output);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$currentExceptionHandler = app(ExceptionHandler::class);
|
||||
|
||||
$currentExceptionHandler instanceof ExceptionHandlerFake
|
||||
? $currentExceptionHandler->setHandler($exceptionHandler)
|
||||
: $this->app->instance(ExceptionHandler::class, $exceptionHandler);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Testing\Concerns;
|
||||
|
||||
interface WithoutExceptionHandlingHandler
|
||||
{
|
||||
//
|
||||
}
|
|
@ -298,7 +298,7 @@ if (! function_exists('context')) {
|
|||
* @param mixed $default
|
||||
* @return mixed|\Illuminate\Log\Context\Repository
|
||||
*/
|
||||
function context($key, $default = null)
|
||||
function context($key = null, $default = null)
|
||||
{
|
||||
return match (true) {
|
||||
is_null($key) => app(ContextRepository::class),
|
||||
|
|
|
@ -764,6 +764,8 @@ class PendingRequest
|
|||
* @param string $url
|
||||
* @param array|string|null $query
|
||||
* @return \Illuminate\Http\Client\Response
|
||||
*
|
||||
* @throws \Illuminate\Http\Client\ConnectionException
|
||||
*/
|
||||
public function get(string $url, $query = null)
|
||||
{
|
||||
|
@ -778,6 +780,8 @@ class PendingRequest
|
|||
* @param string $url
|
||||
* @param array|string|null $query
|
||||
* @return \Illuminate\Http\Client\Response
|
||||
*
|
||||
* @throws \Illuminate\Http\Client\ConnectionException
|
||||
*/
|
||||
public function head(string $url, $query = null)
|
||||
{
|
||||
|
@ -792,6 +796,8 @@ class PendingRequest
|
|||
* @param string $url
|
||||
* @param array $data
|
||||
* @return \Illuminate\Http\Client\Response
|
||||
*
|
||||
* @throws \Illuminate\Http\Client\ConnectionException
|
||||
*/
|
||||
public function post(string $url, $data = [])
|
||||
{
|
||||
|
@ -806,6 +812,8 @@ class PendingRequest
|
|||
* @param string $url
|
||||
* @param array $data
|
||||
* @return \Illuminate\Http\Client\Response
|
||||
*
|
||||
* @throws \Illuminate\Http\Client\ConnectionException
|
||||
*/
|
||||
public function patch(string $url, $data = [])
|
||||
{
|
||||
|
@ -820,6 +828,8 @@ class PendingRequest
|
|||
* @param string $url
|
||||
* @param array $data
|
||||
* @return \Illuminate\Http\Client\Response
|
||||
*
|
||||
* @throws \Illuminate\Http\Client\ConnectionException
|
||||
*/
|
||||
public function put(string $url, $data = [])
|
||||
{
|
||||
|
@ -834,6 +844,8 @@ class PendingRequest
|
|||
* @param string $url
|
||||
* @param array $data
|
||||
* @return \Illuminate\Http\Client\Response
|
||||
*
|
||||
* @throws \Illuminate\Http\Client\ConnectionException
|
||||
*/
|
||||
public function delete(string $url, $data = [])
|
||||
{
|
||||
|
@ -870,6 +882,7 @@ class PendingRequest
|
|||
* @return \Illuminate\Http\Client\Response
|
||||
*
|
||||
* @throws \Exception
|
||||
* @throws \Illuminate\Http\Client\ConnectionException
|
||||
*/
|
||||
public function send(string $method, string $url, array $options = [])
|
||||
{
|
||||
|
|
|
@ -94,9 +94,9 @@ class SesTransport extends AbstractTransport implements Stringable
|
|||
}
|
||||
|
||||
/**
|
||||
* Extract the SES list managenent options, if applicable.
|
||||
* Extract the SES list management options, if applicable.
|
||||
*
|
||||
* @param \Illuminate\Mail\SentMessage $message
|
||||
* @param \Symfony\Component\Mailer\SentMessage $message
|
||||
* @return array|null
|
||||
*/
|
||||
protected function listManagementOptions(SentMessage $message)
|
||||
|
|
|
@ -14,6 +14,6 @@ class SyncConnector implements ConnectorInterface
|
|||
*/
|
||||
public function connect(array $config)
|
||||
{
|
||||
return new SyncQueue;
|
||||
return new SyncQueue($config['after_commit'] ?? null);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,17 @@ use Throwable;
|
|||
|
||||
class SyncQueue extends Queue implements QueueContract
|
||||
{
|
||||
/**
|
||||
* Create a new sync queue instance.
|
||||
*
|
||||
* @param bool $dispatchAfterCommit
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($dispatchAfterCommit = false)
|
||||
{
|
||||
$this->dispatchAfterCommit = $dispatchAfterCommit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size of the queue.
|
||||
*
|
||||
|
|
|
@ -8,6 +8,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
|||
use Illuminate\Routing\Exceptions\BackedEnumCaseNotFoundException;
|
||||
use Illuminate\Support\Reflector;
|
||||
use Illuminate\Support\Str;
|
||||
use ReflectionEnum;
|
||||
|
||||
class ImplicitRouteBinding
|
||||
{
|
||||
|
@ -74,6 +75,7 @@ class ImplicitRouteBinding
|
|||
* @return \Illuminate\Routing\Route
|
||||
*
|
||||
* @throws \Illuminate\Routing\Exceptions\BackedEnumCaseNotFoundException
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
protected static function resolveBackedEnumsForRoute($route, $parameters)
|
||||
{
|
||||
|
@ -86,7 +88,12 @@ class ImplicitRouteBinding
|
|||
|
||||
$backedEnumClass = $parameter->getType()?->getName();
|
||||
|
||||
$backedEnum = $backedEnumClass::tryFrom((string) $parameterValue);
|
||||
$reflectionEnum = new ReflectionEnum($backedEnumClass);
|
||||
|
||||
match ($reflectionEnum->getBackingType()->getName()) {
|
||||
'int' => $backedEnum = collect($backedEnumClass::cases())->first(fn ($case) => (string) $case->value === (string) $parameterValue),
|
||||
'string' => $backedEnum = $backedEnumClass::tryFrom((string) $parameterValue),
|
||||
};
|
||||
|
||||
if (is_null($backedEnum)) {
|
||||
throw new BackedEnumCaseNotFoundException($backedEnumClass, $parameterValue);
|
||||
|
|
|
@ -28,7 +28,7 @@ class RouteSignatureParameters
|
|||
|
||||
return match (true) {
|
||||
! empty($conditions['subClass']) => array_filter($parameters, fn ($p) => Reflector::isParameterSubclassOf($p, $conditions['subClass'])),
|
||||
! empty($conditions['backedEnum']) => array_filter($parameters, fn ($p) => Reflector::isParameterBackedEnumWithStringBackingType($p)),
|
||||
! empty($conditions['backedEnum']) => array_filter($parameters, fn ($p) => Reflector::isParameterBackedEnumWithValidBackingType($p)),
|
||||
default => $parameters,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
namespace Illuminate\Support\Facades;
|
||||
|
||||
use Illuminate\Contracts\Debug\ExceptionHandler;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Testing\Fakes\ExceptionHandlerFake;
|
||||
|
||||
/**
|
||||
* @method static void register()
|
||||
* @method static \Illuminate\Foundation\Exceptions\ReportableHandler reportable(callable $reportUsing)
|
||||
* @method static \Illuminate\Foundation\Exceptions\Handler renderable(callable $renderUsing)
|
||||
* @method static \Illuminate\Foundation\Exceptions\Handler map(\Closure|string $from, \Closure|string|null $to = null)
|
||||
* @method static \Illuminate\Foundation\Exceptions\Handler dontReport(array|string $exceptions)
|
||||
* @method static \Illuminate\Foundation\Exceptions\Handler ignore(array|string $exceptions)
|
||||
* @method static \Illuminate\Foundation\Exceptions\Handler dontFlash(array|string $attributes)
|
||||
* @method static \Illuminate\Foundation\Exceptions\Handler level(string $type, void $level)
|
||||
* @method static void report(\Throwable $e)
|
||||
* @method static bool shouldReport(\Throwable $e)
|
||||
* @method static \Illuminate\Foundation\Exceptions\Handler throttleUsing(callable $throttleUsing)
|
||||
* @method static \Illuminate\Foundation\Exceptions\Handler stopIgnoring(array|string $exceptions)
|
||||
* @method static \Illuminate\Foundation\Exceptions\Handler buildContextUsing(\Closure $contextCallback)
|
||||
* @method static \Symfony\Component\HttpFoundation\Response render(\Illuminate\Http\Request $request, \Throwable $e)
|
||||
* @method static \Illuminate\Foundation\Exceptions\Handler respondUsing(callable $callback)
|
||||
* @method static \Illuminate\Foundation\Exceptions\Handler shouldRenderJsonWhen(callable $callback)
|
||||
* @method static \Illuminate\Foundation\Exceptions\Handler dontReportDuplicates()
|
||||
* @method static \Illuminate\Contracts\Debug\ExceptionHandler handler()
|
||||
* @method static void assertNothingReported()
|
||||
* @method static void assertReported(\Closure|string $exception)
|
||||
* @method static void assertReportedCount(int $count)
|
||||
* @method static void assertNotReported(\Closure|string $exception)
|
||||
* @method static void renderForConsole(\Symfony\Component\Console\Output\OutputInterface $output, \Throwable $e)
|
||||
* @method static \Illuminate\Support\Testing\Fakes\ExceptionHandlerFake throwFirstReported()
|
||||
* @method static \Illuminate\Support\Testing\Fakes\ExceptionHandlerFake setHandler(\Illuminate\Contracts\Debug\ExceptionHandler $handler)
|
||||
*
|
||||
* @see \Illuminate\Foundation\Exceptions\Handler
|
||||
* @see \Illuminate\Contracts\Debug\ExceptionHandler
|
||||
* @see \Illuminate\Support\Testing\Fakes\ExceptionHandlerFake
|
||||
*/
|
||||
class Exceptions extends Facade
|
||||
{
|
||||
/**
|
||||
* Replace the bound instance with a fake.
|
||||
*
|
||||
* @param array<int, class-string<\Throwable>>|class-string<\Throwable> $exceptions
|
||||
* @return \Illuminate\Support\Testing\Fakes\ExceptionHandlerFake
|
||||
*/
|
||||
public static function fake(array|string $exceptions = [])
|
||||
{
|
||||
$exceptionHandler = static::isFake()
|
||||
? static::getFacadeRoot()->handler()
|
||||
: static::getFacadeRoot();
|
||||
|
||||
return tap(new ExceptionHandlerFake($exceptionHandler, Arr::wrap($exceptions)), function ($fake) {
|
||||
static::swap($fake);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the registered name of the component.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function getFacadeAccessor()
|
||||
{
|
||||
return ExceptionHandler::class;
|
||||
}
|
||||
}
|
|
@ -64,7 +64,7 @@ abstract class MultipleInstanceManager
|
|||
abstract public function getInstanceConfig($name);
|
||||
|
||||
/**
|
||||
* Get an instance instance by name.
|
||||
* Get an instance by name.
|
||||
*
|
||||
* @param string|null $name
|
||||
* @return mixed
|
||||
|
|
|
@ -141,12 +141,12 @@ class Reflector
|
|||
}
|
||||
|
||||
/**
|
||||
* Determine if the parameter's type is a Backed Enum with a string backing type.
|
||||
* Determine if the parameter's type is a Backed Enum with a string or int backing type.
|
||||
*
|
||||
* @param \ReflectionParameter $parameter
|
||||
* @return bool
|
||||
*/
|
||||
public static function isParameterBackedEnumWithStringBackingType($parameter)
|
||||
public static function isParameterBackedEnumWithValidBackingType($parameter)
|
||||
{
|
||||
if (! $parameter->getType() instanceof ReflectionNamedType) {
|
||||
return false;
|
||||
|
@ -162,7 +162,7 @@ class Reflector
|
|||
$reflectionBackedEnum = new ReflectionEnum($backedEnumClass);
|
||||
|
||||
return $reflectionBackedEnum->isBacked()
|
||||
&& $reflectionBackedEnum->getBackingType()->getName() == 'string';
|
||||
&& in_array($reflectionBackedEnum->getBackingType()->getName(), ['int', 'string']);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -0,0 +1,275 @@
|
|||
<?php
|
||||
|
||||
namespace Illuminate\Support\Testing\Fakes;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Debug\ExceptionHandler;
|
||||
use Illuminate\Foundation\Testing\Concerns\WithoutExceptionHandlingHandler;
|
||||
use Illuminate\Support\Traits\ForwardsCalls;
|
||||
use Illuminate\Support\Traits\ReflectsClosures;
|
||||
use Illuminate\Testing\Assert;
|
||||
use PHPUnit\Framework\Assert as PHPUnit;
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* @mixin \Illuminate\Foundation\Exceptions\Handler
|
||||
*/
|
||||
class ExceptionHandlerFake implements ExceptionHandler, Fake
|
||||
{
|
||||
use ForwardsCalls, ReflectsClosures;
|
||||
|
||||
/**
|
||||
* All of the exceptions that have been reported.
|
||||
*
|
||||
* @var array<int, \Throwable>
|
||||
*/
|
||||
protected $reported = [];
|
||||
|
||||
/**
|
||||
* If the fake should throw exceptions when they are reported.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $throwOnReport = false;
|
||||
|
||||
/**
|
||||
* Create a new exception handler fake.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Debug\ExceptionHandler $handler
|
||||
* @param array<int, class-string<\Throwable>> $exceptions
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(
|
||||
protected ExceptionHandler $handler,
|
||||
protected array $exceptions = [],
|
||||
) {
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the underlying handler implementation.
|
||||
*
|
||||
* @return \Illuminate\Contracts\Debug\ExceptionHandler
|
||||
*/
|
||||
public function handler()
|
||||
{
|
||||
return $this->handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert if an exception of the given type has been reported.
|
||||
*
|
||||
* @param \Closure|string $exception
|
||||
* @return void
|
||||
*/
|
||||
public function assertReported(Closure|string $exception)
|
||||
{
|
||||
$message = sprintf(
|
||||
'The expected [%s] exception was not reported.',
|
||||
is_string($exception) ? $exception : $this->firstClosureParameterType($exception)
|
||||
);
|
||||
|
||||
if (is_string($exception)) {
|
||||
Assert::assertTrue(
|
||||
in_array($exception, array_map('get_class', $this->reported), true),
|
||||
$message,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Assert::assertTrue(
|
||||
collect($this->reported)->contains(
|
||||
fn (Throwable $e) => $this->firstClosureParameterType($exception) === get_class($e)
|
||||
&& $exception($e) === true,
|
||||
), $message,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert the number of exceptions that have been reported.
|
||||
*
|
||||
* @param int $count
|
||||
* @return void
|
||||
*/
|
||||
public function assertReportedCount(int $count)
|
||||
{
|
||||
$total = collect($this->reported)->count();
|
||||
|
||||
PHPUnit::assertSame(
|
||||
$count, $total,
|
||||
"The total number of exceptions reported was {$total} instead of {$count}."
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert if an exception of the given type has not been reported.
|
||||
*
|
||||
* @param \Closure|string $exception
|
||||
* @return void
|
||||
*/
|
||||
public function assertNotReported(Closure|string $exception)
|
||||
{
|
||||
try {
|
||||
$this->assertReported($exception);
|
||||
} catch (ExpectationFailedException $e) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new ExpectationFailedException(sprintf(
|
||||
'The expected [%s] exception was not reported.',
|
||||
is_string($exception) ? $exception : $this->firstClosureParameterType($exception)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert nothing has been reported.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function assertNothingReported()
|
||||
{
|
||||
Assert::assertEmpty(
|
||||
$this->reported,
|
||||
sprintf(
|
||||
'The following exceptions were reported: %s.',
|
||||
implode(', ', array_map('get_class', $this->reported)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Report or log an exception.
|
||||
*
|
||||
* @param \Throwable $e
|
||||
* @return void
|
||||
*/
|
||||
public function report($e)
|
||||
{
|
||||
if (! $this->isFakedException($e)) {
|
||||
$this->handler->report($e);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (! $this->shouldReport($e)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->reported[] = $e;
|
||||
|
||||
if ($this->throwOnReport) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given exception is faked.
|
||||
*
|
||||
* @param \Throwable $e
|
||||
* @return bool
|
||||
*/
|
||||
protected function isFakedException(Throwable $e)
|
||||
{
|
||||
return count($this->exceptions) === 0 || in_array(get_class($e), $this->exceptions, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the exception should be reported.
|
||||
*
|
||||
* @param \Throwable $e
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldReport($e)
|
||||
{
|
||||
return $this->runningWithoutExceptionHandling() || $this->handler->shouldReport($e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the handler is running without exception handling.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function runningWithoutExceptionHandling()
|
||||
{
|
||||
return $this->handler instanceof WithoutExceptionHandlingHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render an exception into an HTTP response.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Throwable $e
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function render($request, $e)
|
||||
{
|
||||
return $this->handler->render($request, $e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render an exception to the console.
|
||||
*
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
||||
* @param \Throwable $e
|
||||
* @return void
|
||||
*/
|
||||
public function renderForConsole($output, Throwable $e)
|
||||
{
|
||||
$this->handler->renderForConsole($output, $e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw exceptions when they are reported.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function throwOnReport()
|
||||
{
|
||||
$this->throwOnReport = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw the first reported exception.
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function throwFirstReported()
|
||||
{
|
||||
foreach ($this->reported as $e) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the "original" handler that should be used by the fake.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Debug\ExceptionHandler $handler
|
||||
* @return $this
|
||||
*/
|
||||
public function setHandler(ExceptionHandler $handler)
|
||||
{
|
||||
$this->handler = $handler;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle dynamic method calls to the mailer.
|
||||
*
|
||||
* @param string $method
|
||||
* @param array<string, mixed> $parameters
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call(string $method, array $parameters)
|
||||
{
|
||||
return $this->forwardCallTo($this->handler, $method, $parameters);
|
||||
}
|
||||
}
|
|
@ -137,6 +137,7 @@ return [
|
|||
'required_array_keys' => 'The :attribute field must contain entries for: :values.',
|
||||
'required_if' => 'The :attribute field is required when :other is :value.',
|
||||
'required_if_accepted' => 'The :attribute field is required when :other is accepted.',
|
||||
'required_if_declined' => 'The :attribute field is required when :other is declined.',
|
||||
'required_unless' => 'The :attribute field is required unless :other is in :values.',
|
||||
'required_with' => 'The :attribute field is required when :values is present.',
|
||||
'required_with_all' => 'The :attribute field is required when :values are present.',
|
||||
|
|
|
@ -609,6 +609,22 @@ trait ReplacesAttributes
|
|||
return str_replace([':other'], $parameters, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace all place-holders for the required_if_declined rule.
|
||||
*
|
||||
* @param string $message
|
||||
* @param string $attribute
|
||||
* @param string $rule
|
||||
* @param array<int,string> $parameters
|
||||
* @return string
|
||||
*/
|
||||
public function replaceRequiredIfDeclined($message, $attribute, $rule, $parameters)
|
||||
{
|
||||
$parameters[0] = $this->getDisplayableAttribute($parameters[0]);
|
||||
|
||||
return str_replace([':other'], $parameters, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace all place-holders for the required_unless rule.
|
||||
*
|
||||
|
|
|
@ -1961,6 +1961,25 @@ trait ValidatesAttributes
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that an attribute exists when another attribute was "declined".
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param mixed $parameters
|
||||
* @return bool
|
||||
*/
|
||||
public function validateRequiredIfDeclined($attribute, $value, $parameters)
|
||||
{
|
||||
$this->requireParameterCount(1, $parameters, 'required_if_declined');
|
||||
|
||||
if ($this->validateDeclined($parameters[0], $this->getValue($parameters[0]))) {
|
||||
return $this->validateRequired($attribute, $value);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that an attribute does not exist or is an empty string.
|
||||
*
|
||||
|
|
|
@ -221,6 +221,7 @@ class Validator implements ValidatorContract
|
|||
'Required',
|
||||
'RequiredIf',
|
||||
'RequiredIfAccepted',
|
||||
'RequiredIfDeclined',
|
||||
'RequiredUnless',
|
||||
'RequiredWith',
|
||||
'RequiredWithAll',
|
||||
|
@ -252,6 +253,7 @@ class Validator implements ValidatorContract
|
|||
'DeclinedIf',
|
||||
'RequiredIf',
|
||||
'RequiredIfAccepted',
|
||||
'RequiredIfDeclined',
|
||||
'RequiredUnless',
|
||||
'RequiredWith',
|
||||
'RequiredWithAll',
|
||||
|
|
|
@ -502,8 +502,7 @@ class ComponentAttributeBag implements ArrayAccess, IteratorAggregate, JsonSeria
|
|||
}
|
||||
|
||||
if ($value === true) {
|
||||
// Exception for Alpine...
|
||||
$value = $key === 'x-data' ? '' : $key;
|
||||
$value = $key === 'x-data' || str_starts_with($key, 'wire:') ? '' : $key;
|
||||
}
|
||||
|
||||
$string .= ' '.$key.'="'.str_replace('"', '\\"', trim($value)).'"';
|
||||
|
|
|
@ -88,7 +88,7 @@ class InvokableComponentVariable implements DeferringDisplayableValue, IteratorA
|
|||
/**
|
||||
* Resolve the variable as a string.
|
||||
*
|
||||
* @return mixed
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
|
|
|
@ -277,6 +277,36 @@ class CacheManagerTest extends TestCase
|
|||
$cacheManager->store('alien_store');
|
||||
}
|
||||
|
||||
public function testMakesRepositoryWithoutDispatcherWhenEventsDisabled()
|
||||
{
|
||||
$userConfig = [
|
||||
'cache' => [
|
||||
'stores' => [
|
||||
'my_store' => [
|
||||
'driver' => 'array',
|
||||
],
|
||||
'my_store_without_events' => [
|
||||
'driver' => 'array',
|
||||
'events' => false,
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$app = $this->getApp($userConfig);
|
||||
$app->bind(Dispatcher::class, fn () => new Event);
|
||||
|
||||
$cacheManager = new CacheManager($app);
|
||||
|
||||
// The repository will have an event dispatcher
|
||||
$repo = $cacheManager->store('my_store');
|
||||
$this->assertNotNull($repo->getEventDispatcher());
|
||||
|
||||
// This repository will not have an event dispatcher as 'with_events' is false
|
||||
$repoWithoutEvents = $cacheManager->store('my_store_without_events');
|
||||
$this->assertNull($repoWithoutEvents->getEventDispatcher());
|
||||
}
|
||||
|
||||
protected function getApp(array $userConfig)
|
||||
{
|
||||
$app = new Container;
|
||||
|
|
|
@ -18,10 +18,18 @@ class LimitTest extends TestCase
|
|||
$this->assertSame(1, $limit->decaySeconds);
|
||||
$this->assertSame(3, $limit->maxAttempts);
|
||||
|
||||
$limit = Limit::perSecond(3, 5);
|
||||
$this->assertSame(5, $limit->decaySeconds);
|
||||
$this->assertSame(3, $limit->maxAttempts);
|
||||
|
||||
$limit = Limit::perMinute(3);
|
||||
$this->assertSame(60, $limit->decaySeconds);
|
||||
$this->assertSame(3, $limit->maxAttempts);
|
||||
|
||||
$limit = Limit::perMinute(3, 4);
|
||||
$this->assertSame(240, $limit->decaySeconds);
|
||||
$this->assertSame(3, $limit->maxAttempts);
|
||||
|
||||
$limit = Limit::perMinutes(2, 3);
|
||||
$this->assertSame(120, $limit->decaySeconds);
|
||||
$this->assertSame(3, $limit->maxAttempts);
|
||||
|
@ -30,10 +38,18 @@ class LimitTest extends TestCase
|
|||
$this->assertSame(3600, $limit->decaySeconds);
|
||||
$this->assertSame(3, $limit->maxAttempts);
|
||||
|
||||
$limit = Limit::perHour(3, 2);
|
||||
$this->assertSame(7200, $limit->decaySeconds);
|
||||
$this->assertSame(3, $limit->maxAttempts);
|
||||
|
||||
$limit = Limit::perDay(3);
|
||||
$this->assertSame(86400, $limit->decaySeconds);
|
||||
$this->assertSame(3, $limit->maxAttempts);
|
||||
|
||||
$limit = Limit::perDay(3, 5);
|
||||
$this->assertSame(432000, $limit->decaySeconds);
|
||||
$this->assertSame(3, $limit->maxAttempts);
|
||||
|
||||
$limit = new GlobalLimit(3);
|
||||
$this->assertSame(60, $limit->decaySeconds);
|
||||
$this->assertSame(3, $limit->maxAttempts);
|
||||
|
|
|
@ -56,6 +56,9 @@ class RepositoryTest extends TestCase
|
|||
$this->assertNull(
|
||||
$this->repository->get('a.b.c')
|
||||
);
|
||||
|
||||
$this->assertNull($this->repository->get('x.y.z'));
|
||||
$this->assertNull($this->repository->get('.'));
|
||||
}
|
||||
|
||||
public function testGetBooleanValue()
|
||||
|
@ -178,10 +181,13 @@ class RepositoryTest extends TestCase
|
|||
{
|
||||
$this->assertSame('aaa', $this->repository->get('array.0'));
|
||||
$this->assertSame('zzz', $this->repository->get('array.1'));
|
||||
|
||||
$this->repository->prepend('array', 'xxx');
|
||||
$this->assertSame('xxx', $this->repository->get('array.0'));
|
||||
$this->assertSame('aaa', $this->repository->get('array.1'));
|
||||
$this->assertSame('zzz', $this->repository->get('array.2'));
|
||||
$this->assertNull($this->repository->get('array.3'));
|
||||
$this->assertCount(3, $this->repository->get('array'));
|
||||
}
|
||||
|
||||
public function testPush()
|
||||
|
@ -192,6 +198,8 @@ class RepositoryTest extends TestCase
|
|||
$this->assertSame('aaa', $this->repository->get('array.0'));
|
||||
$this->assertSame('zzz', $this->repository->get('array.1'));
|
||||
$this->assertSame('xxx', $this->repository->get('array.2'));
|
||||
|
||||
$this->assertCount(3, $this->repository->get('array'));
|
||||
}
|
||||
|
||||
public function testPrependWithNewKey()
|
||||
|
@ -213,8 +221,21 @@ class RepositoryTest extends TestCase
|
|||
|
||||
public function testOffsetExists()
|
||||
{
|
||||
$data = [
|
||||
'foo' => 'bar',
|
||||
'null_value' => null,
|
||||
'empty_string' => '',
|
||||
'numeric_value' => 123,
|
||||
];
|
||||
$this->repository->set($data);
|
||||
|
||||
$this->assertTrue(isset($this->repository['foo']));
|
||||
$this->assertFalse(isset($this->repository['not-exist']));
|
||||
$this->assertTrue(isset($this->repository['null_value']));
|
||||
$this->assertTrue(isset($this->repository['empty_string']));
|
||||
$this->assertTrue(isset($this->repository['numeric_value']));
|
||||
$this->assertFalse(isset($this->repository[-1]));
|
||||
$this->assertFalse(isset($this->repository['non_numeric']));
|
||||
}
|
||||
|
||||
public function testOffsetGet()
|
||||
|
@ -234,6 +255,18 @@ class RepositoryTest extends TestCase
|
|||
$this->repository['key'] = 'value';
|
||||
|
||||
$this->assertSame('value', $this->repository['key']);
|
||||
|
||||
$this->repository['key'] = 'new_value';
|
||||
$this->assertSame('new_value', $this->repository['key']);
|
||||
|
||||
$this->repository['new_key'] = null;
|
||||
$this->assertNull($this->repository['new_key']);
|
||||
|
||||
$this->repository[''] = 'value';
|
||||
$this->assertSame('value', $this->repository['']);
|
||||
|
||||
$this->repository[123] = '123';
|
||||
$this->assertSame('123', $this->repository[123]);
|
||||
}
|
||||
|
||||
public function testOffsetUnset()
|
||||
|
@ -259,7 +292,7 @@ class RepositoryTest extends TestCase
|
|||
public function testItGetsAsString(): void
|
||||
{
|
||||
$this->assertSame(
|
||||
$this->repository->string('a.b'), 'c'
|
||||
'c', $this->repository->string('a.b')
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
<?php
|
||||
|
||||
namespace Illuminate\Tests\Console;
|
||||
|
||||
use Illuminate\Console\Application;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Console\OutputStyle;
|
||||
use Illuminate\Console\View\Components\Factory;
|
||||
use Laravel\Prompts\Prompt;
|
||||
use Mockery as m;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Output\NullOutput;
|
||||
|
||||
use function Laravel\Prompts\multiselect;
|
||||
use function Laravel\Prompts\select;
|
||||
|
||||
class ConfiguresPromptsTest extends TestCase
|
||||
{
|
||||
protected function tearDown(): void
|
||||
{
|
||||
m::close();
|
||||
}
|
||||
|
||||
#[DataProvider('selectDataProvider')]
|
||||
public function testSelectFallback($prompt, $expectedOptions, $expectedDefault, $return, $expectedReturn)
|
||||
{
|
||||
Prompt::fallbackWhen(true);
|
||||
|
||||
$command = new class($prompt) extends Command
|
||||
{
|
||||
public $answer;
|
||||
|
||||
public function __construct(protected $prompt)
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$this->answer = ($this->prompt)();
|
||||
}
|
||||
};
|
||||
|
||||
$this->runCommand($command, fn ($components) => $components
|
||||
->expects('choice')
|
||||
->with('Test', $expectedOptions, $expectedDefault)
|
||||
->andReturn($return)
|
||||
);
|
||||
|
||||
$this->assertSame($expectedReturn, $command->answer);
|
||||
}
|
||||
|
||||
public static function selectDataProvider()
|
||||
{
|
||||
return [
|
||||
'list with no default' => [fn () => select('Test', ['a', 'b', 'c']), ['a', 'b', 'c'], null, 'b', 'b'],
|
||||
'numeric keys with no default' => [fn () => select('Test', [1 => 'a', 2 => 'b', 3 => 'c']), [1 => 'a', 2 => 'b', 3 => 'c'], null, '2', 2],
|
||||
'assoc with no default' => [fn () => select('Test', ['a' => 'A', 'b' => 'B', 'c' => 'C']), ['a' => 'A', 'b' => 'B', 'c' => 'C'], null, 'b', 'b'],
|
||||
'list with default' => [fn () => select('Test', ['a', 'b', 'c'], 'b'), ['a', 'b', 'c'], 'b', 'b', 'b'],
|
||||
'numeric keys with default' => [fn () => select('Test', [1 => 'a', 2 => 'b', 3 => 'c'], 2), [1 => 'a', 2 => 'b', 3 => 'c'], 2, '2', 2],
|
||||
'assoc with default' => [fn () => select('Test', ['a' => 'A', 'b' => 'B', 'c' => 'C'], 'b'), ['a' => 'A', 'b' => 'B', 'c' => 'C'], 'b', 'b', 'b'],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('multiselectDataProvider')]
|
||||
public function testMultiselectFallback($prompt, $expectedOptions, $expectedDefault, $return, $expectedReturn)
|
||||
{
|
||||
Prompt::fallbackWhen(true);
|
||||
|
||||
$command = new class($prompt) extends Command
|
||||
{
|
||||
public $answer;
|
||||
|
||||
public function __construct(protected $prompt)
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$this->answer = ($this->prompt)();
|
||||
}
|
||||
};
|
||||
|
||||
$this->runCommand($command, fn ($components) => $components
|
||||
->expects('choice')
|
||||
->with('Test', $expectedOptions, $expectedDefault, null, true)
|
||||
->andReturn($return)
|
||||
);
|
||||
|
||||
$this->assertSame($expectedReturn, $command->answer);
|
||||
}
|
||||
|
||||
public static function multiselectDataProvider()
|
||||
{
|
||||
return [
|
||||
'list with no default' => [fn () => multiselect('Test', ['a', 'b', 'c']), ['None', 'a', 'b', 'c'], 'None', ['None'], []],
|
||||
'numeric keys with no default' => [fn () => multiselect('Test', [1 => 'a', 2 => 'b', 3 => 'c']), ['' => 'None', 1 => 'a', 2 => 'b', 3 => 'c'], 'None', [''], []],
|
||||
'assoc with no default' => [fn () => multiselect('Test', ['a' => 'A', 'b' => 'B', 'c' => 'C']), ['' => 'None', 'a' => 'A', 'b' => 'B', 'c' => 'C'], 'None', [''], []],
|
||||
'list with default' => [fn () => multiselect('Test', ['a', 'b', 'c'], ['b', 'c']), ['None', 'a', 'b', 'c'], 'b,c', ['b', 'c'], ['b', 'c']],
|
||||
'numeric keys with default' => [fn () => multiselect('Test', [1 => 'a', 2 => 'b', 3 => 'c'], [2, 3]), ['' => 'None', 1 => 'a', 2 => 'b', 3 => 'c'], '2,3', ['2', '3'], [2, 3]],
|
||||
'assoc with default' => [fn () => multiselect('Test', ['a' => 'A', 'b' => 'B', 'c' => 'C'], ['b', 'c']), ['' => 'None', 'a' => 'A', 'b' => 'B', 'c' => 'C'], 'b,c', ['b', 'c'], ['b', 'c']],
|
||||
'required list with no default' => [fn () => multiselect('Test', ['a', 'b', 'c'], required: true), ['a', 'b', 'c'], null, ['b', 'c'], ['b', 'c']],
|
||||
'required numeric keys with no default' => [fn () => multiselect('Test', [1 => 'a', 2 => 'b', 3 => 'c'], required: true), [1 => 'a', 2 => 'b', 3 => 'c'], null, ['2', '3'], [2, 3]],
|
||||
'required assoc with no default' => [fn () => multiselect('Test', ['a' => 'A', 'b' => 'B', 'c' => 'C'], required: true), ['a' => 'A', 'b' => 'B', 'c' => 'C'], null, ['b', 'c'], ['b', 'c']],
|
||||
'required list with default' => [fn () => multiselect('Test', ['a', 'b', 'c'], ['b', 'c'], required: true), ['a', 'b', 'c'], 'b,c', ['b', 'c'], ['b', 'c']],
|
||||
'required numeric keys with default' => [fn () => multiselect('Test', [1 => 'a', 2 => 'b', 3 => 'c'], [2, 3], required: true), [1 => 'a', 2 => 'b', 3 => 'c'], '2,3', ['2', '3'], [2, 3]],
|
||||
'required assoc with default' => [fn () => multiselect('Test', ['a' => 'A', 'b' => 'B', 'c' => 'C'], ['b', 'c'], required: true), ['a' => 'A', 'b' => 'B', 'c' => 'C'], 'b,c', ['b', 'c'], ['b', 'c']],
|
||||
];
|
||||
}
|
||||
|
||||
protected function runCommand($command, $expectations)
|
||||
{
|
||||
$command->setLaravel($application = m::mock(Application::class));
|
||||
|
||||
$application->shouldReceive('make')->withArgs(fn ($abstract) => $abstract === OutputStyle::class)->andReturn($outputStyle = m::mock(OutputStyle::class));
|
||||
$application->shouldReceive('make')->withArgs(fn ($abstract) => $abstract === Factory::class)->andReturn($factory = m::mock(Factory::class));
|
||||
$application->shouldReceive('runningUnitTests')->andReturn(false);
|
||||
$application->shouldReceive('call')->with([$command, 'handle'])->andReturnUsing(fn ($callback) => call_user_func($callback));
|
||||
$outputStyle->shouldReceive('newLinesWritten')->andReturn(1);
|
||||
|
||||
$expectations($factory);
|
||||
|
||||
$command->run(new ArrayInput([]), new NullOutput);
|
||||
}
|
||||
}
|
|
@ -8,7 +8,4 @@ use Illuminate\Contracts\Queue\ShouldQueue;
|
|||
|
||||
final class JobToTestWithSchedule implements ShouldQueue
|
||||
{
|
||||
public function __invoke(): void
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,11 +36,12 @@ final class ScheduleTest extends TestCase
|
|||
}
|
||||
|
||||
#[DataProvider('jobHonoursDisplayNameIfMethodExistsProvider')]
|
||||
public function testJobHonoursDisplayNameIfMethodExists(string|object $job, string $jobName): void
|
||||
public function testJobHonoursDisplayNameIfMethodExists(object $job, string $jobName): void
|
||||
{
|
||||
$schedule = new Schedule();
|
||||
$scheduledJob = $schedule->job($job);
|
||||
self::assertSame($jobName, $scheduledJob->description);
|
||||
self::assertFalse($this->container->resolved(JobToTestWithSchedule::class));
|
||||
}
|
||||
|
||||
public static function jobHonoursDisplayNameIfMethodExistsProvider(): array
|
||||
|
@ -54,9 +55,16 @@ final class ScheduleTest extends TestCase
|
|||
};
|
||||
|
||||
return [
|
||||
[JobToTestWithSchedule::class, JobToTestWithSchedule::class],
|
||||
[new JobToTestWithSchedule, JobToTestWithSchedule::class],
|
||||
[$job, 'testJob-123'],
|
||||
];
|
||||
}
|
||||
|
||||
public function testJobIsNotInstantiatedIfSuppliedAsClassname(): void
|
||||
{
|
||||
$schedule = new Schedule();
|
||||
$scheduledJob = $schedule->job(JobToTestWithSchedule::class);
|
||||
self::assertSame(JobToTestWithSchedule::class, $scheduledJob->description);
|
||||
self::assertFalse($this->container->resolved(JobToTestWithSchedule::class));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ use Illuminate\Database\Eloquent\Builder;
|
|||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Tests\Database\Fixtures\Enums\Bar;
|
||||
use Mockery as m;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
|
@ -85,6 +86,16 @@ class DatabaseEloquentBelongsToTest extends TestCase
|
|||
$relation->addEagerConstraints($models);
|
||||
}
|
||||
|
||||
public function testIdsInEagerConstraintsCanBeBackedEnum()
|
||||
{
|
||||
$relation = $this->getRelation();
|
||||
$relation->getRelated()->shouldReceive('getKeyName')->andReturn('id');
|
||||
$relation->getRelated()->shouldReceive('getKeyType')->andReturn('int');
|
||||
$relation->getQuery()->shouldReceive('whereIntegerInRaw')->once()->with('relation.id', [5, 'foreign.value']);
|
||||
$models = [new EloquentBelongsToModelStub, new EloquentBelongsToModelStubWithBackedEnumCast];
|
||||
$relation->addEagerConstraints($models);
|
||||
}
|
||||
|
||||
public function testRelationIsProperlyInitialized()
|
||||
{
|
||||
$relation = $this->getRelation();
|
||||
|
@ -119,6 +130,15 @@ class DatabaseEloquentBelongsToTest extends TestCase
|
|||
}
|
||||
};
|
||||
|
||||
$result4 = new class extends Model
|
||||
{
|
||||
protected $casts = [
|
||||
'id' => Bar::class,
|
||||
];
|
||||
|
||||
protected $attributes = ['id' => 5];
|
||||
};
|
||||
|
||||
$model1 = new EloquentBelongsToModelStub;
|
||||
$model1->foreign_key = 1;
|
||||
$model2 = new EloquentBelongsToModelStub;
|
||||
|
@ -131,11 +151,18 @@ class DatabaseEloquentBelongsToTest extends TestCase
|
|||
return '3';
|
||||
}
|
||||
};
|
||||
$models = $relation->match([$model1, $model2, $model3], new Collection([$result1, $result2, $result3]), 'foo');
|
||||
$model4 = new EloquentBelongsToModelStub;
|
||||
$model4->foreign_key = 5;
|
||||
$models = $relation->match(
|
||||
[$model1, $model2, $model3, $model4],
|
||||
new Collection([$result1, $result2, $result3, $result4]),
|
||||
'foo'
|
||||
);
|
||||
|
||||
$this->assertEquals(1, $models[0]->foo->getAttribute('id'));
|
||||
$this->assertEquals(2, $models[1]->foo->getAttribute('id'));
|
||||
$this->assertSame('3', (string) $models[2]->foo->getAttribute('id'));
|
||||
$this->assertEquals(5, $models[3]->foo->getAttribute('id')->value);
|
||||
}
|
||||
|
||||
public function testAssociateMethodSetsForeignKeyOnModel()
|
||||
|
@ -403,3 +430,14 @@ class MissingEloquentBelongsToModelStub extends Model
|
|||
{
|
||||
public $foreign_key;
|
||||
}
|
||||
|
||||
class EloquentBelongsToModelStubWithBackedEnumCast extends Model
|
||||
{
|
||||
protected $casts = [
|
||||
'foreign_key' => Bar::class,
|
||||
];
|
||||
|
||||
public $attributes = [
|
||||
'foreign_key' => 5,
|
||||
];
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ use Illuminate\Pagination\AbstractPaginator as Paginator;
|
|||
use Illuminate\Pagination\Cursor;
|
||||
use Illuminate\Pagination\CursorPaginator;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Tests\Database\Fixtures\Enums\Bar;
|
||||
use InvalidArgumentException;
|
||||
use Mockery as m;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
@ -1042,8 +1043,10 @@ class DatabaseQueryBuilderTest extends TestCase
|
|||
public function testWhereIntegerInRaw()
|
||||
{
|
||||
$builder = $this->getBuilder();
|
||||
$builder->select('*')->from('users')->whereIntegerInRaw('id', ['1a', 2]);
|
||||
$this->assertSame('select * from "users" where "id" in (1, 2)', $builder->toSql());
|
||||
$builder->select('*')->from('users')->whereIntegerInRaw('id', [
|
||||
'1a', 2, Bar::FOO,
|
||||
]);
|
||||
$this->assertSame('select * from "users" where "id" in (1, 2, 5)', $builder->toSql());
|
||||
$this->assertEquals([], $builder->getBindings());
|
||||
|
||||
$builder = $this->getBuilder();
|
||||
|
@ -1051,8 +1054,9 @@ class DatabaseQueryBuilderTest extends TestCase
|
|||
['id' => '1a'],
|
||||
['id' => 2],
|
||||
['any' => '3'],
|
||||
['id' => Bar::FOO],
|
||||
]);
|
||||
$this->assertSame('select * from "users" where "id" in (1, 2, 3)', $builder->toSql());
|
||||
$this->assertSame('select * from "users" where "id" in (1, 2, 3, 5)', $builder->toSql());
|
||||
$this->assertEquals([], $builder->getBindings());
|
||||
}
|
||||
|
||||
|
@ -2479,19 +2483,19 @@ class DatabaseQueryBuilderTest extends TestCase
|
|||
public function testJoinsWithMultipleNestedJoins()
|
||||
{
|
||||
$builder = $this->getBuilder();
|
||||
$builder->select('users.id', 'contacts.id', 'contact_types.id', 'countrys.id', 'planets.id')->from('users')->leftJoin('contacts', function ($j) {
|
||||
$builder->select('users.id', 'contacts.id', 'contact_types.id', 'countries.id', 'planets.id')->from('users')->leftJoin('contacts', function ($j) {
|
||||
$j->on('users.id', 'contacts.id')
|
||||
->join('contact_types', 'contacts.contact_type_id', '=', 'contact_types.id')
|
||||
->leftJoin('countrys', function ($q) {
|
||||
$q->on('contacts.country', '=', 'countrys.country')
|
||||
->leftJoin('countries', function ($q) {
|
||||
$q->on('contacts.country', '=', 'countries.country')
|
||||
->join('planets', function ($q) {
|
||||
$q->on('countrys.planet_id', '=', 'planet.id')
|
||||
$q->on('countries.planet_id', '=', 'planet.id')
|
||||
->where('planet.is_settled', '=', 1)
|
||||
->where('planet.population', '>=', 10000);
|
||||
});
|
||||
});
|
||||
});
|
||||
$this->assertSame('select "users"."id", "contacts"."id", "contact_types"."id", "countrys"."id", "planets"."id" from "users" left join ("contacts" inner join "contact_types" on "contacts"."contact_type_id" = "contact_types"."id" left join ("countrys" inner join "planets" on "countrys"."planet_id" = "planet"."id" and "planet"."is_settled" = ? and "planet"."population" >= ?) on "contacts"."country" = "countrys"."country") on "users"."id" = "contacts"."id"', $builder->toSql());
|
||||
$this->assertSame('select "users"."id", "contacts"."id", "contact_types"."id", "countries"."id", "planets"."id" from "users" left join ("contacts" inner join "contact_types" on "contacts"."contact_type_id" = "contact_types"."id" left join ("countries" inner join "planets" on "countries"."planet_id" = "planet"."id" and "planet"."is_settled" = ? and "planet"."population" >= ?) on "contacts"."country" = "countries"."country") on "users"."id" = "contacts"."id"', $builder->toSql());
|
||||
$this->assertEquals(['1', 10000], $builder->getBindings());
|
||||
}
|
||||
|
||||
|
@ -2502,16 +2506,16 @@ class DatabaseQueryBuilderTest extends TestCase
|
|||
$j->on('users.id', 'contacts.id')
|
||||
->join('contact_types', 'contacts.contact_type_id', '=', 'contact_types.id')
|
||||
->whereExists(function ($q) {
|
||||
$q->select('*')->from('countrys')
|
||||
->whereColumn('contacts.country', '=', 'countrys.country')
|
||||
$q->select('*')->from('countries')
|
||||
->whereColumn('contacts.country', '=', 'countries.country')
|
||||
->join('planets', function ($q) {
|
||||
$q->on('countrys.planet_id', '=', 'planet.id')
|
||||
$q->on('countries.planet_id', '=', 'planet.id')
|
||||
->where('planet.is_settled', '=', 1);
|
||||
})
|
||||
->where('planet.population', '>=', 10000);
|
||||
});
|
||||
});
|
||||
$this->assertSame('select "users"."id", "contacts"."id", "contact_types"."id" from "users" left join ("contacts" inner join "contact_types" on "contacts"."contact_type_id" = "contact_types"."id") on "users"."id" = "contacts"."id" and exists (select * from "countrys" inner join "planets" on "countrys"."planet_id" = "planet"."id" and "planet"."is_settled" = ? where "contacts"."country" = "countrys"."country" and "planet"."population" >= ?)', $builder->toSql());
|
||||
$this->assertSame('select "users"."id", "contacts"."id", "contact_types"."id" from "users" left join ("contacts" inner join "contact_types" on "contacts"."contact_type_id" = "contact_types"."id") on "users"."id" = "contacts"."id" and exists (select * from "countries" inner join "planets" on "countries"."planet_id" = "planet"."id" and "planet"."is_settled" = ? where "contacts"."country" = "countries"."country" and "planet"."population" >= ?)', $builder->toSql());
|
||||
$this->assertEquals(['1', 10000], $builder->getBindings());
|
||||
}
|
||||
|
||||
|
|
|
@ -74,8 +74,8 @@ class DatabaseTransactionsManagerTest extends TestCase
|
|||
$executedAdminTransactions = $manager->commit('admin', 2, 1);
|
||||
|
||||
$this->assertCount(1, $manager->getPendingTransactions()); // One pending "admin" transaction left...
|
||||
$this->assertCount(2, $executedTransactions); // Two committed tranasctions on "default"
|
||||
$this->assertCount(0, $executedAdminTransactions); // Zero executed committed tranasctions on "default"
|
||||
$this->assertCount(2, $executedTransactions); // Two committed transactions on "default"
|
||||
$this->assertCount(0, $executedAdminTransactions); // Zero executed committed transactions on "default"
|
||||
|
||||
// Level 2 "admin" callback has been staged...
|
||||
$this->assertSame('admin', $manager->getCommittedTransactions()[0]->connection);
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace Illuminate\Tests\Database\Fixtures\Enums;
|
||||
|
||||
enum Bar: int
|
||||
{
|
||||
case FOO = 5;
|
||||
}
|
|
@ -6,11 +6,24 @@ use Carbon\CarbonInterval;
|
|||
use Illuminate\Database\Connection;
|
||||
use Illuminate\Events\Dispatcher;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Carbon;
|
||||
use PDO;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class QueryDurationThresholdTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var \Illuminate\Support\Carbon
|
||||
*/
|
||||
protected $now;
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
Carbon::setTestNow(null);
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testItCanHandleReachingADurationThresholdInTheDb()
|
||||
{
|
||||
$connection = new Connection(new PDO('sqlite::memory:'));
|
||||
|
@ -46,10 +59,12 @@ class QueryDurationThresholdTest extends TestCase
|
|||
|
||||
public function testItIsOnlyCalledOnceWhenGivenDateTime()
|
||||
{
|
||||
Carbon::setTestNow($this->now = Carbon::create(2017, 6, 27, 13, 14, 15, 'UTC'));
|
||||
|
||||
$connection = new Connection(new PDO('sqlite::memory:'));
|
||||
$connection->setEventDispatcher(new Dispatcher());
|
||||
$called = 0;
|
||||
$connection->whenQueryingForLongerThan(now()->addMilliseconds(1), function () use (&$called) {
|
||||
$connection->whenQueryingForLongerThan($this->now->addMilliseconds(1), function () use (&$called) {
|
||||
$called++;
|
||||
});
|
||||
|
||||
|
|
|
@ -38,6 +38,18 @@ class EncrypterTest extends TestCase
|
|||
$this->assertSame('foo', $decrypted);
|
||||
}
|
||||
|
||||
public function testItValidatesMacOnPerKeyBasis()
|
||||
{
|
||||
// Payload created with (key: str_repeat('b', 16)) but will
|
||||
// "successfully" decrypt with (key: str_repeat('a', 16)), however it
|
||||
// outputs a random binary string as it is not the correct key.
|
||||
$encrypted = 'eyJpdiI6Ilg0dFM5TVRibEFqZW54c3lQdWJoVVE9PSIsInZhbHVlIjoiRGJpa2p2ZHI3eUs0dUtRakJneUhUUT09IiwibWFjIjoiMjBjZWYxODdhNThhOTk4MTk1NTc0YTE1MDgzODU1OWE0ZmQ4MDc5ZjMxYThkOGM1ZmM1MzlmYzBkYTBjMWI1ZiIsInRhZyI6IiJ9';
|
||||
|
||||
$new = new Encrypter(str_repeat('a', 16));
|
||||
$new->previousKeys([str_repeat('b', 16)]);
|
||||
$this->assertSame('foo', $new->decryptString($encrypted));
|
||||
}
|
||||
|
||||
public function testEncryptionUsingBase64EncodedKey()
|
||||
{
|
||||
$e = new Encrypter(random_bytes(16));
|
||||
|
|
|
@ -180,7 +180,7 @@ class FoundationDocsCommandTest extends TestCase
|
|||
$this->artisan('docs');
|
||||
}
|
||||
|
||||
public function testItBubblesUpNonProcessInterruptExceptionsInAskStratgies()
|
||||
public function testItBubblesUpNonProcessInterruptExceptionsInAskStrategies()
|
||||
{
|
||||
putenv('ARTISAN_DOCS_ASK_STRATEGY='.__DIR__.'/fixtures/process-failure-strategy.php');
|
||||
|
||||
|
|
|
@ -0,0 +1,199 @@
|
|||
<?php
|
||||
|
||||
namespace Illuminate\Tests\Integration\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Contracts\Console\Kernel;
|
||||
use Orchestra\Testbench\TestCase;
|
||||
|
||||
use function Laravel\Prompts\confirm;
|
||||
use function Laravel\Prompts\multiselect;
|
||||
use function Laravel\Prompts\password;
|
||||
use function Laravel\Prompts\select;
|
||||
use function Laravel\Prompts\text;
|
||||
use function Laravel\Prompts\textarea;
|
||||
|
||||
class PromptsAssertionTest extends TestCase
|
||||
{
|
||||
public function testAssertionForTextPrompt()
|
||||
{
|
||||
$this->app[Kernel::class]->registerCommand(
|
||||
new class extends Command
|
||||
{
|
||||
protected $signature = 'test:text';
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$name = text('What is your name?', 'John');
|
||||
|
||||
$this->line($name);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
$this
|
||||
->artisan('test:text')
|
||||
->expectsQuestion('What is your name?', 'Jane')
|
||||
->expectsOutput('Jane');
|
||||
}
|
||||
|
||||
public function testAssertionForTextareaPrompt()
|
||||
{
|
||||
$this->app[Kernel::class]->registerCommand(
|
||||
new class extends Command
|
||||
{
|
||||
protected $signature = 'test:textarea';
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$name = textarea('What is your name?', 'John');
|
||||
|
||||
$this->line($name);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
$this
|
||||
->artisan('test:textarea')
|
||||
->expectsQuestion('What is your name?', 'Jane')
|
||||
->expectsOutput('Jane');
|
||||
}
|
||||
|
||||
public function testAssertionForPasswordPrompt()
|
||||
{
|
||||
$this->app[Kernel::class]->registerCommand(
|
||||
new class extends Command
|
||||
{
|
||||
protected $signature = 'test:password';
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$name = password('What is your password?');
|
||||
|
||||
$this->line($name);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
$this
|
||||
->artisan('test:password')
|
||||
->expectsQuestion('What is your password?', 'secret')
|
||||
->expectsOutput('secret');
|
||||
}
|
||||
|
||||
public function testAssertionForConfirmPrompt()
|
||||
{
|
||||
$this->app[Kernel::class]->registerCommand(
|
||||
new class extends Command
|
||||
{
|
||||
protected $signature = 'test:confirm';
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$confirmed = confirm('Is your name John?');
|
||||
|
||||
if ($confirmed) {
|
||||
$this->line('Your name is John.');
|
||||
} else {
|
||||
$this->line('Your name is not John.');
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
$this
|
||||
->artisan('test:confirm')
|
||||
->expectsConfirmation('Is your name John?', 'no')
|
||||
->expectsOutput('Your name is not John.');
|
||||
|
||||
$this
|
||||
->artisan('test:confirm')
|
||||
->expectsConfirmation('Is your name John?', 'yes')
|
||||
->expectsOutput('Your name is John.');
|
||||
}
|
||||
|
||||
public function testAssertionForSelectPrompt()
|
||||
{
|
||||
$this->app[Kernel::class]->registerCommand(
|
||||
new class extends Command
|
||||
{
|
||||
protected $signature = 'test:select';
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$name = select(
|
||||
label: 'What is your name?',
|
||||
options: ['John', 'Jane']
|
||||
);
|
||||
|
||||
$this->line("Your name is $name.");
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
$this
|
||||
->artisan('test:select')
|
||||
->expectsChoice('What is your name?', 'Jane', ['John', 'Jane'])
|
||||
->expectsOutput('Your name is Jane.');
|
||||
}
|
||||
|
||||
public function testAssertionForRequiredMultiselectPrompt()
|
||||
{
|
||||
$this->app[Kernel::class]->registerCommand(
|
||||
new class extends Command
|
||||
{
|
||||
protected $signature = 'test:multiselect';
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$names = multiselect(
|
||||
label: 'Which names do you like?',
|
||||
options: ['John', 'Jane', 'Sally', 'Jack'],
|
||||
required: true
|
||||
);
|
||||
|
||||
$this->line(sprintf('You like %s.', implode(', ', $names)));
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
$this
|
||||
->artisan('test:multiselect')
|
||||
->expectsChoice('Which names do you like?', ['John', 'Jane'], ['John', 'Jane', 'Sally', 'Jack'])
|
||||
->expectsOutput('You like John, Jane.');
|
||||
}
|
||||
|
||||
public function testAssertionForOptionalMultiselectPrompt()
|
||||
{
|
||||
$this->app[Kernel::class]->registerCommand(
|
||||
new class extends Command
|
||||
{
|
||||
protected $signature = 'test:multiselect';
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$names = multiselect(
|
||||
label: 'Which names do you like?',
|
||||
options: ['John', 'Jane', 'Sally', 'Jack'],
|
||||
);
|
||||
|
||||
if (empty($names)) {
|
||||
$this->line('You like nobody.');
|
||||
} else {
|
||||
$this->line(sprintf('You like %s.', implode(', ', $names)));
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
$this
|
||||
->artisan('test:multiselect')
|
||||
->expectsChoice('Which names do you like?', ['John', 'Jane'], ['John', 'Jane', 'Sally', 'Jack'])
|
||||
->expectsOutput('You like John, Jane.');
|
||||
|
||||
$this
|
||||
->artisan('test:multiselect')
|
||||
->expectsChoice('Which names do you like?', ['None'], ['John', 'Jane', 'Sally', 'Jack'])
|
||||
->expectsOutput('You like nobody.');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,387 @@
|
|||
<?php
|
||||
|
||||
namespace Illuminate\Tests\Integration\Database;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AfterQueryTest extends DatabaseTestCase
|
||||
{
|
||||
protected function afterRefreshingDatabase()
|
||||
{
|
||||
Schema::create('users', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->integer('team_id')->nullable();
|
||||
});
|
||||
|
||||
Schema::create('teams', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->integer('owner_id');
|
||||
});
|
||||
|
||||
Schema::create('posts', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
});
|
||||
|
||||
Schema::create('users_posts', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->integer('user_id');
|
||||
$table->integer('post_id');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function testAfterQueryOnEloquentBuilder()
|
||||
{
|
||||
AfterQueryUser::create();
|
||||
AfterQueryUser::create();
|
||||
|
||||
$afterQueryIds = collect();
|
||||
|
||||
$users = AfterQueryUser::query()
|
||||
->afterQuery(function (Collection $users) use ($afterQueryIds) {
|
||||
$afterQueryIds->push(...$users->pluck('id')->all());
|
||||
|
||||
foreach ($users as $user) {
|
||||
$this->assertInstanceOf(AfterQueryUser::class, $user);
|
||||
}
|
||||
})
|
||||
->get();
|
||||
|
||||
$this->assertCount(2, $users);
|
||||
$this->assertEqualsCanonicalizing($afterQueryIds->toArray(), $users->pluck('id')->toArray());
|
||||
}
|
||||
|
||||
public function testAfterQueryOnBaseBuilder()
|
||||
{
|
||||
AfterQueryUser::create();
|
||||
AfterQueryUser::create();
|
||||
|
||||
$afterQueryIds = collect();
|
||||
|
||||
$users = AfterQueryUser::query()
|
||||
->toBase()
|
||||
->afterQuery(function (Collection $users) use ($afterQueryIds) {
|
||||
$afterQueryIds->push(...$users->pluck('id')->all());
|
||||
|
||||
foreach ($users as $user) {
|
||||
$this->assertNotInstanceOf(AfterQueryUser::class, $user);
|
||||
}
|
||||
})
|
||||
->get();
|
||||
|
||||
$this->assertCount(2, $users);
|
||||
$this->assertEqualsCanonicalizing($afterQueryIds->toArray(), $users->pluck('id')->toArray());
|
||||
}
|
||||
|
||||
public function testAfterQueryOnEloquentCursor()
|
||||
{
|
||||
AfterQueryUser::create();
|
||||
AfterQueryUser::create();
|
||||
|
||||
$afterQueryIds = collect();
|
||||
|
||||
$users = AfterQueryUser::query()
|
||||
->afterQuery(function (Collection $users) use ($afterQueryIds) {
|
||||
$afterQueryIds->push(...$users->pluck('id')->all());
|
||||
|
||||
foreach ($users as $user) {
|
||||
$this->assertInstanceOf(AfterQueryUser::class, $user);
|
||||
}
|
||||
})
|
||||
->cursor();
|
||||
|
||||
$this->assertCount(2, $users);
|
||||
$this->assertEqualsCanonicalizing($afterQueryIds->toArray(), $users->pluck('id')->toArray());
|
||||
}
|
||||
|
||||
public function testAfterQueryOnBaseBuilderCursor()
|
||||
{
|
||||
AfterQueryUser::create();
|
||||
AfterQueryUser::create();
|
||||
|
||||
$afterQueryIds = collect();
|
||||
|
||||
$users = AfterQueryUser::query()
|
||||
->toBase()
|
||||
->afterQuery(function (Collection $users) use ($afterQueryIds) {
|
||||
$afterQueryIds->push(...$users->pluck('id')->all());
|
||||
|
||||
foreach ($users as $user) {
|
||||
$this->assertNotInstanceOf(AfterQueryUser::class, $user);
|
||||
}
|
||||
})
|
||||
->cursor();
|
||||
|
||||
$this->assertCount(2, $users);
|
||||
$this->assertEqualsCanonicalizing($afterQueryIds->toArray(), $users->pluck('id')->toArray());
|
||||
}
|
||||
|
||||
public function testAfterQueryOnEloquentPluck()
|
||||
{
|
||||
AfterQueryUser::create();
|
||||
AfterQueryUser::create();
|
||||
|
||||
$afterQueryIds = collect();
|
||||
|
||||
$userIds = AfterQueryUser::query()
|
||||
->afterQuery(function (Collection $userIds) use ($afterQueryIds) {
|
||||
$afterQueryIds->push(...$userIds->all());
|
||||
|
||||
foreach ($userIds as $userId) {
|
||||
$this->assertIsInt($userId);
|
||||
}
|
||||
})
|
||||
->pluck('id');
|
||||
|
||||
$this->assertCount(2, $userIds);
|
||||
$this->assertEqualsCanonicalizing($afterQueryIds->toArray(), $userIds->toArray());
|
||||
}
|
||||
|
||||
public function testAfterQueryOnBaseBuilderPluck()
|
||||
{
|
||||
AfterQueryUser::create();
|
||||
AfterQueryUser::create();
|
||||
|
||||
$afterQueryIds = collect();
|
||||
|
||||
$userIds = AfterQueryUser::query()
|
||||
->toBase()
|
||||
->afterQuery(function (Collection $userIds) use ($afterQueryIds) {
|
||||
$afterQueryIds->push(...$userIds->all());
|
||||
|
||||
foreach ($userIds as $userId) {
|
||||
$this->assertIsInt((int) $userId);
|
||||
}
|
||||
})
|
||||
->pluck('id');
|
||||
|
||||
$this->assertCount(2, $userIds);
|
||||
$this->assertEqualsCanonicalizing($afterQueryIds->toArray(), $userIds->toArray());
|
||||
}
|
||||
|
||||
public function testAfterQueryHookOnBelongsToManyRelationship()
|
||||
{
|
||||
$user = AfterQueryUser::create();
|
||||
$firstPost = AfterQueryPost::create();
|
||||
$secondPost = AfterQueryPost::create();
|
||||
|
||||
$user->posts()->attach($firstPost);
|
||||
$user->posts()->attach($secondPost);
|
||||
|
||||
$afterQueryIds = collect();
|
||||
|
||||
$posts = $user->posts()
|
||||
->afterQuery(function (Collection $posts) use ($afterQueryIds) {
|
||||
$afterQueryIds->push(...$posts->pluck('id')->all());
|
||||
|
||||
foreach ($posts as $post) {
|
||||
$this->assertInstanceOf(AfterQueryPost::class, $post);
|
||||
}
|
||||
})
|
||||
->get();
|
||||
|
||||
$this->assertCount(2, $posts);
|
||||
$this->assertEqualsCanonicalizing($afterQueryIds->toArray(), $posts->pluck('id')->toArray());
|
||||
}
|
||||
|
||||
public function testAfterQueryHookOnHasManyThroughRelationship()
|
||||
{
|
||||
$user = AfterQueryUser::create();
|
||||
$team = AfterQueryTeam::create(['owner_id' => $user->id]);
|
||||
|
||||
AfterQueryUser::create(['team_id' => $team->id]);
|
||||
AfterQueryUser::create(['team_id' => $team->id]);
|
||||
|
||||
$afterQueryIds = collect();
|
||||
|
||||
$teamMates = $user->teamMates()
|
||||
->afterQuery(function (Collection $teamMates) use ($afterQueryIds) {
|
||||
$afterQueryIds->push(...$teamMates->pluck('id')->all());
|
||||
|
||||
foreach ($teamMates as $teamMate) {
|
||||
$this->assertInstanceOf(AfterQueryUser::class, $teamMate);
|
||||
}
|
||||
})
|
||||
->get();
|
||||
|
||||
$this->assertCount(2, $teamMates);
|
||||
$this->assertEqualsCanonicalizing($afterQueryIds->toArray(), $teamMates->pluck('id')->toArray());
|
||||
}
|
||||
|
||||
public function testAfterQueryOnEloquentBuilderCanAlterReturnedResult()
|
||||
{
|
||||
$firstUser = AfterQueryUser::create();
|
||||
$secondUser = AfterQueryUser::create();
|
||||
|
||||
$users = AfterQueryUser::query()
|
||||
->afterQuery(function () {
|
||||
return collect(['foo', 'bar']);
|
||||
})
|
||||
->get();
|
||||
|
||||
$this->assertEquals(collect(['foo', 'bar']), $users);
|
||||
|
||||
$users = AfterQueryUser::query()
|
||||
->afterQuery(function () {
|
||||
return collect(['foo', 'bar']);
|
||||
})
|
||||
->pluck('id');
|
||||
|
||||
$this->assertEquals(collect(['foo', 'bar']), $users);
|
||||
|
||||
$users = AfterQueryUser::query()
|
||||
->afterQuery(function ($users) use ($firstUser) {
|
||||
return $users->first()->is($firstUser) ? collect(['foo', 'bar']) : collect(['bar', 'foo']);
|
||||
})
|
||||
->cursor();
|
||||
|
||||
$this->assertEquals(collect(['foo', 'bar']), $users->collect());
|
||||
|
||||
$users = AfterQueryUser::query()
|
||||
->afterQuery(function ($users) use ($firstUser) {
|
||||
return $users->where('id', '!=', $firstUser->id);
|
||||
})
|
||||
->cursor();
|
||||
|
||||
$this->assertEquals([$secondUser->id], $users->collect()->pluck('id')->all());
|
||||
|
||||
$firstPost = AfterQueryPost::create();
|
||||
$secondPost = AfterQueryPost::create();
|
||||
|
||||
$firstUser->posts()->attach($firstPost);
|
||||
$firstUser->posts()->attach($secondPost);
|
||||
|
||||
$posts = $firstUser->posts()
|
||||
->afterQuery(function () {
|
||||
return collect(['foo', 'bar']);
|
||||
})
|
||||
->get();
|
||||
|
||||
$this->assertEquals(collect(['foo', 'bar']), $posts);
|
||||
|
||||
$user = AfterQueryUser::create();
|
||||
$team = AfterQueryTeam::create(['owner_id' => $user->id]);
|
||||
|
||||
AfterQueryUser::create(['team_id' => $team->id]);
|
||||
AfterQueryUser::create(['team_id' => $team->id]);
|
||||
|
||||
$teamMates = $user->teamMates()
|
||||
->afterQuery(function () {
|
||||
return collect(['foo', 'bar']);
|
||||
})
|
||||
->get();
|
||||
|
||||
$this->assertEquals(collect(['foo', 'bar']), $teamMates);
|
||||
}
|
||||
|
||||
public function testAfterQueryOnBaseBuilderCanAlterReturnedResult()
|
||||
{
|
||||
$firstUser = AfterQueryUser::create();
|
||||
$secondUser = AfterQueryUser::create();
|
||||
|
||||
$users = AfterQueryUser::query()
|
||||
->toBase()
|
||||
->afterQuery(function () {
|
||||
return collect(['foo', 'bar']);
|
||||
})
|
||||
->get();
|
||||
|
||||
$this->assertEquals(collect(['foo', 'bar']), $users);
|
||||
|
||||
$users = AfterQueryUser::query()
|
||||
->toBase()
|
||||
->afterQuery(function () {
|
||||
return collect(['foo', 'bar']);
|
||||
})
|
||||
->pluck('id');
|
||||
|
||||
$this->assertEquals(collect(['foo', 'bar']), $users);
|
||||
|
||||
$users = AfterQueryUser::query()
|
||||
->toBase()
|
||||
->afterQuery(function ($users) use ($firstUser) {
|
||||
return ((int) $users->first()->id) === $firstUser->id ? collect(['foo', 'bar']) : collect(['bar', 'foo']);
|
||||
})
|
||||
->cursor();
|
||||
|
||||
$this->assertEquals(collect(['foo', 'bar']), $users->collect());
|
||||
|
||||
$users = AfterQueryUser::query()
|
||||
->toBase()
|
||||
->afterQuery(function ($users) use ($firstUser) {
|
||||
return $users->where('id', '!=', $firstUser->id);
|
||||
})
|
||||
->cursor();
|
||||
|
||||
$this->assertEquals([$secondUser->id], $users->collect()->pluck('id')->all());
|
||||
|
||||
$firstPost = AfterQueryPost::create();
|
||||
$secondPost = AfterQueryPost::create();
|
||||
|
||||
$firstUser->posts()->attach($firstPost);
|
||||
$firstUser->posts()->attach($secondPost);
|
||||
|
||||
$posts = $firstUser->posts()
|
||||
->toBase()
|
||||
->afterQuery(function () {
|
||||
return collect(['foo', 'bar']);
|
||||
})
|
||||
->get();
|
||||
|
||||
$this->assertEquals(collect(['foo', 'bar']), $posts);
|
||||
|
||||
$user = AfterQueryUser::create();
|
||||
$team = AfterQueryTeam::create(['owner_id' => $user->id]);
|
||||
|
||||
AfterQueryUser::create(['team_id' => $team->id]);
|
||||
AfterQueryUser::create(['team_id' => $team->id]);
|
||||
|
||||
$teamMates = $user->teamMates()
|
||||
->toBase()
|
||||
->afterQuery(function () {
|
||||
return collect(['foo', 'bar']);
|
||||
})
|
||||
->get();
|
||||
|
||||
$this->assertEquals(collect(['foo', 'bar']), $teamMates);
|
||||
}
|
||||
}
|
||||
|
||||
class AfterQueryUser extends Model
|
||||
{
|
||||
protected $table = 'users';
|
||||
protected $guarded = [];
|
||||
public $timestamps = false;
|
||||
|
||||
public function teamMates()
|
||||
{
|
||||
return $this->hasManyThrough(self::class, AfterQueryTeam::class, 'owner_id', 'team_id');
|
||||
}
|
||||
|
||||
public function posts()
|
||||
{
|
||||
return $this->belongsToMany(AfterQueryPost::class, 'users_posts', 'user_id', 'post_id')->withTimestamps();
|
||||
}
|
||||
}
|
||||
|
||||
class AfterQueryTeam extends Model
|
||||
{
|
||||
protected $table = 'teams';
|
||||
protected $guarded = [];
|
||||
public $timestamps = false;
|
||||
|
||||
public function members()
|
||||
{
|
||||
return $this->hasMany(AfterQueryUser::class, 'team_id');
|
||||
}
|
||||
}
|
||||
|
||||
class AfterQueryPost extends Model
|
||||
{
|
||||
protected $table = 'posts';
|
||||
protected $guarded = [];
|
||||
public $timestamps = false;
|
||||
}
|
|
@ -21,6 +21,7 @@ class DatabaseSqlServerSchemaBuilderTest extends SqlServerTestCase
|
|||
protected function destroyDatabaseMigrations()
|
||||
{
|
||||
Schema::drop('users');
|
||||
Schema::dropIfExists('computed');
|
||||
DB::statement('drop view if exists users_view');
|
||||
}
|
||||
|
||||
|
@ -64,4 +65,12 @@ class DatabaseSqlServerSchemaBuilderTest extends SqlServerTestCase
|
|||
{
|
||||
$this->assertSame([], Schema::getViews());
|
||||
}
|
||||
|
||||
public function testComputedColumnsListing()
|
||||
{
|
||||
DB::statement('create table dbo.computed (id int identity (1,1) not null, computed as id + 1)');
|
||||
|
||||
$userColumns = Schema::getColumns('users');
|
||||
$this->assertNull($userColumns[1]['generation']);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -105,7 +105,7 @@ class ShouldDispatchAfterCommitEventTest extends TestCase
|
|||
$this->assertTrue(AnotherShouldDispatchAfterCommitTestEvent::$ran);
|
||||
}
|
||||
|
||||
public function testItOnlyDispatchesNestedTransactionsEventsAfterTheRootTransactionIsCommitedDifferentOrder()
|
||||
public function testItOnlyDispatchesNestedTransactionsEventsAfterTheRootTransactionIsCommittedDifferentOrder()
|
||||
{
|
||||
Event::listen(ShouldDispatchAfterCommitTestEvent::class, ShouldDispatchAfterCommitListener::class);
|
||||
Event::listen(AnotherShouldDispatchAfterCommitTestEvent::class, AnotherShouldDispatchAfterCommitListener::class);
|
||||
|
|
|
@ -17,7 +17,7 @@ class RoutingServiceProviderTest extends TestCase
|
|||
|
||||
$response = $this->withoutExceptionHandling()->get('test-route?'.http_build_query([
|
||||
'sent' => 'sent-data',
|
||||
'overridden' => 'overriden-sent-data',
|
||||
'overridden' => 'overridden-sent-data',
|
||||
]));
|
||||
|
||||
$response->assertOk();
|
||||
|
@ -48,7 +48,7 @@ class RoutingServiceProviderTest extends TestCase
|
|||
|
||||
$response = $this->getJson('test-route?'.http_build_query([
|
||||
'sent' => 'sent-data',
|
||||
'overridden' => 'overriden-sent-data',
|
||||
'overridden' => 'overridden-sent-data',
|
||||
]), [
|
||||
'content-type' => 'application/json',
|
||||
]);
|
||||
|
@ -57,7 +57,7 @@ class RoutingServiceProviderTest extends TestCase
|
|||
$response->assertExactJson([
|
||||
'json-data' => 'json-data',
|
||||
'merged' => 'replaced-merged-data',
|
||||
'overridden' => 'overriden-merged-data',
|
||||
'overridden' => 'overridden-merged-data',
|
||||
'request-data' => 'request-data',
|
||||
]);
|
||||
}
|
||||
|
@ -70,14 +70,14 @@ class RoutingServiceProviderTest extends TestCase
|
|||
|
||||
$response = $this->getJson('test-route?'.http_build_query([
|
||||
'sent' => 'sent-data',
|
||||
'overridden' => 'overriden-sent-data',
|
||||
'overridden' => 'overridden-sent-data',
|
||||
]));
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertExactJson([
|
||||
'json-data' => 'json-data',
|
||||
'merged' => 'replaced-merged-data',
|
||||
'overridden' => 'overriden-merged-data',
|
||||
'overridden' => 'overridden-merged-data',
|
||||
'request-data' => 'request-data',
|
||||
]);
|
||||
}
|
||||
|
@ -90,14 +90,14 @@ class RoutingServiceProviderTest extends TestCase
|
|||
|
||||
$response = $this->post('test-route', [
|
||||
'sent' => 'sent-data',
|
||||
'overridden' => 'overriden-sent-data',
|
||||
'overridden' => 'overridden-sent-data',
|
||||
]);
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertExactJson([
|
||||
'sent' => 'sent-data',
|
||||
'merged' => 'replaced-merged-data',
|
||||
'overridden' => 'overriden-merged-data',
|
||||
'overridden' => 'overridden-merged-data',
|
||||
'request-data' => 'request-data',
|
||||
]);
|
||||
}
|
||||
|
@ -110,7 +110,7 @@ class RoutingServiceProviderTest extends TestCase
|
|||
|
||||
$response = $this->postJson('test-route', [
|
||||
'sent' => 'sent-data',
|
||||
'overridden' => 'overriden-sent-data',
|
||||
'overridden' => 'overridden-sent-data',
|
||||
]);
|
||||
|
||||
$response->assertOk();
|
||||
|
@ -118,7 +118,7 @@ class RoutingServiceProviderTest extends TestCase
|
|||
'json-data' => 'json-data',
|
||||
'sent' => 'sent-data',
|
||||
'merged' => 'replaced-merged-data',
|
||||
'overridden' => 'overriden-merged-data',
|
||||
'overridden' => 'overridden-merged-data',
|
||||
'request-data' => 'request-data',
|
||||
]);
|
||||
}
|
||||
|
@ -144,7 +144,7 @@ class MergeDataMiddleware
|
|||
|
||||
$request->merge(['merged' => 'replaced-merged-data']);
|
||||
|
||||
$request->merge(['overridden' => 'overriden-merged-data']);
|
||||
$request->merge(['overridden' => 'overridden-merged-data']);
|
||||
|
||||
$request->request->set('request-data', 'request-data');
|
||||
|
||||
|
|
|
@ -305,7 +305,7 @@ class ThrottleRequestsTest extends TestCase
|
|||
|
||||
public function testItFallbacksToUserAccessorWhenThereIsNoNamedLimiterWhenAuthenticated()
|
||||
{
|
||||
$user = UserWithAcessor::make();
|
||||
$user = UserWithAccessor::make();
|
||||
|
||||
Carbon::setTestNow(Carbon::create(2018, 1, 1, 0, 0, 0));
|
||||
|
||||
|
@ -333,7 +333,7 @@ class ThrottleRequestsTest extends TestCase
|
|||
}
|
||||
}
|
||||
|
||||
class UserWithAcessor extends User
|
||||
class UserWithAccessor extends User
|
||||
{
|
||||
public function getRateLimitingAttribute(): int
|
||||
{
|
||||
|
|
|
@ -0,0 +1,594 @@
|
|||
<?php
|
||||
|
||||
namespace Illuminate\Tests\Integration\Support;
|
||||
|
||||
use ErrorException;
|
||||
use Exception;
|
||||
use Illuminate\Contracts\Debug\ExceptionHandler;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Foundation\Exceptions\Handler;
|
||||
use Illuminate\Support\Facades\Exceptions;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Support\Testing\Fakes\ExceptionHandlerFake;
|
||||
use InvalidArgumentException;
|
||||
use Orchestra\Testbench\TestCase;
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
use RuntimeException;
|
||||
use Throwable;
|
||||
|
||||
class ExceptionsFacadeTest extends TestCase
|
||||
{
|
||||
public function testFakeAssertReported()
|
||||
{
|
||||
Exceptions::fake();
|
||||
|
||||
Exceptions::report(new RuntimeException('test 1'));
|
||||
report(new RuntimeException('test 2'));
|
||||
|
||||
Exceptions::assertReported(RuntimeException::class);
|
||||
Exceptions::assertReported(fn (RuntimeException $e) => $e->getMessage() === 'test 1');
|
||||
Exceptions::assertReported(fn (RuntimeException $e) => $e->getMessage() === 'test 2');
|
||||
Exceptions::assertReportedCount(2);
|
||||
}
|
||||
|
||||
public function testFakeAssertReportedCount()
|
||||
{
|
||||
Exceptions::fake();
|
||||
|
||||
Exceptions::report(new RuntimeException('test 1'));
|
||||
report(new RuntimeException('test 2'));
|
||||
|
||||
Exceptions::assertReportedCount(2);
|
||||
}
|
||||
|
||||
public function testFakeAssertReportedCountMayFail()
|
||||
{
|
||||
Exceptions::fake();
|
||||
|
||||
Exceptions::report(new RuntimeException('test 1'));
|
||||
report(new RuntimeException('test 2'));
|
||||
|
||||
$this->expectException(ExpectationFailedException::class);
|
||||
$this->expectExceptionMessage('The total number of exceptions reported was 2 instead of 1.');
|
||||
|
||||
Exceptions::assertReportedCount(1);
|
||||
}
|
||||
|
||||
public function testFakeAssertReportedWithFakedExceptions()
|
||||
{
|
||||
Exceptions::fake([
|
||||
RuntimeException::class,
|
||||
]);
|
||||
|
||||
Exceptions::report(new RuntimeException('test 1'));
|
||||
report(new RuntimeException('test 2'));
|
||||
report(new InvalidArgumentException('test 3'));
|
||||
|
||||
Exceptions::assertReported(RuntimeException::class);
|
||||
Exceptions::assertReported(fn (RuntimeException $e) => $e->getMessage() === 'test 1');
|
||||
Exceptions::assertReported(fn (RuntimeException $e) => $e->getMessage() === 'test 2');
|
||||
|
||||
Exceptions::assertNotReported(InvalidArgumentException::class);
|
||||
Exceptions::assertReportedCount(2);
|
||||
}
|
||||
|
||||
public function testFakeAssertReportedAsStringMayFail()
|
||||
{
|
||||
$this->expectException(ExpectationFailedException::class);
|
||||
$this->expectExceptionMessage('The expected [InvalidArgumentException] exception was not reported.');
|
||||
|
||||
Exceptions::fake();
|
||||
|
||||
Exceptions::report(new RuntimeException('test 1'));
|
||||
|
||||
Exceptions::assertReportedCount(1);
|
||||
Exceptions::assertReported(InvalidArgumentException::class);
|
||||
}
|
||||
|
||||
public function testFakeAssertReportedAsClosureMayFail()
|
||||
{
|
||||
$this->expectException(ExpectationFailedException::class);
|
||||
$this->expectExceptionMessage('The expected [InvalidArgumentException] exception was not reported.');
|
||||
|
||||
Exceptions::fake();
|
||||
|
||||
Exceptions::report(new RuntimeException('test 1'));
|
||||
|
||||
Exceptions::assertReportedCount(1);
|
||||
Exceptions::assertReported(fn (InvalidArgumentException $e) => $e->getMessage() === 'test 2');
|
||||
}
|
||||
|
||||
public function testFakeAssertReportedWithFakedExceptionsMayFail()
|
||||
{
|
||||
$this->expectException(ExpectationFailedException::class);
|
||||
$this->expectExceptionMessage('The expected [RuntimeException] exception was not reported.');
|
||||
|
||||
Exceptions::fake(InvalidArgumentException::class);
|
||||
|
||||
Exceptions::report(new InvalidArgumentException('test 1'));
|
||||
report(new RuntimeException('test 2'));
|
||||
|
||||
Exceptions::assertReported(InvalidArgumentException::class);
|
||||
Exceptions::assertReported(RuntimeException::class);
|
||||
}
|
||||
|
||||
public function testFakeAssertNotReported()
|
||||
{
|
||||
Exceptions::fake();
|
||||
|
||||
Exceptions::report(new RuntimeException('test 1'));
|
||||
report(new RuntimeException('test 2'));
|
||||
|
||||
Exceptions::assertNotReported(InvalidArgumentException::class);
|
||||
Exceptions::assertNotReported(fn (InvalidArgumentException $e) => $e->getMessage() === 'test 1');
|
||||
Exceptions::assertNotReported(fn (InvalidArgumentException $e) => $e->getMessage() === 'test 2');
|
||||
Exceptions::assertNotReported(fn (InvalidArgumentException $e) => $e->getMessage() === 'test 3');
|
||||
Exceptions::assertNotReported(fn (InvalidArgumentException $e) => $e->getMessage() === 'test 4');
|
||||
|
||||
Exceptions::assertReportedCount(2);
|
||||
}
|
||||
|
||||
public function testFakeAssertNotReportedWithFakedExceptions()
|
||||
{
|
||||
Exceptions::fake([
|
||||
InvalidArgumentException::class,
|
||||
]);
|
||||
|
||||
report(new RuntimeException('test 2'));
|
||||
|
||||
Exceptions::assertNotReported(InvalidArgumentException::class);
|
||||
Exceptions::assertNotReported(RuntimeException::class);
|
||||
}
|
||||
|
||||
public function testFakeAssertNotReportedMayFail()
|
||||
{
|
||||
$this->expectException(ExpectationFailedException::class);
|
||||
$this->expectExceptionMessage('The expected [RuntimeException] exception was not reported.');
|
||||
|
||||
Exceptions::fake();
|
||||
|
||||
Exceptions::report(new RuntimeException('test 1'));
|
||||
|
||||
Exceptions::assertNotReported(RuntimeException::class);
|
||||
}
|
||||
|
||||
public function testFakeAssertNotReportedAsClosureMayFail()
|
||||
{
|
||||
$this->expectException(ExpectationFailedException::class);
|
||||
$this->expectExceptionMessage('The expected [RuntimeException] exception was not reported.');
|
||||
|
||||
Exceptions::fake();
|
||||
|
||||
Exceptions::report(new RuntimeException('test 1'));
|
||||
|
||||
Exceptions::assertNotReported(fn (RuntimeException $e) => $e->getMessage() === 'test 1');
|
||||
}
|
||||
|
||||
public function testResolvesExceptionHandler()
|
||||
{
|
||||
$this->assertInstanceOf(
|
||||
ExceptionHandler::class,
|
||||
Exceptions::getFacadeRoot()
|
||||
);
|
||||
}
|
||||
|
||||
public function testFakeAssertNothingReported()
|
||||
{
|
||||
Exceptions::fake();
|
||||
|
||||
Exceptions::assertNothingReported();
|
||||
}
|
||||
|
||||
public function testFakeAssertNothingReportedWithFakedExceptions()
|
||||
{
|
||||
Exceptions::fake([
|
||||
InvalidArgumentException::class,
|
||||
]);
|
||||
|
||||
report(new RuntimeException('test 1'));
|
||||
|
||||
Exceptions::assertNothingReported();
|
||||
}
|
||||
|
||||
public function testFakeAssertNothingReportedMayFail()
|
||||
{
|
||||
$this->expectException(ExpectationFailedException::class);
|
||||
$this->expectExceptionMessage('The following exceptions were reported: RuntimeException, RuntimeException, InvalidArgumentException.');
|
||||
|
||||
Exceptions::fake();
|
||||
|
||||
Exceptions::report(new RuntimeException('test 1'));
|
||||
report(new RuntimeException('test 2'));
|
||||
report(new InvalidArgumentException('test 3'));
|
||||
|
||||
Exceptions::assertNothingReported();
|
||||
}
|
||||
|
||||
public function testFakeMethodReturnsExceptionHandlerFake()
|
||||
{
|
||||
$this->assertInstanceOf(ExceptionHandlerFake::class, $fake = Exceptions::fake());
|
||||
$this->assertInstanceOf(ExceptionHandlerFake::class, Exceptions::getFacadeRoot());
|
||||
$this->assertInstanceOf(Handler::class, $fake->handler());
|
||||
|
||||
$this->assertInstanceOf(ExceptionHandlerFake::class, $fake = Exceptions::fake());
|
||||
$this->assertInstanceOf(ExceptionHandlerFake::class, Exceptions::getFacadeRoot());
|
||||
$this->assertInstanceOf(Handler::class, $fake->handler());
|
||||
}
|
||||
|
||||
public function testReportedExceptionsAreNotThrownByDefault()
|
||||
{
|
||||
report(new Exception('Test exception'));
|
||||
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
public function testReportedExceptionsAreNotThrownByDefaultWithExceptionHandling()
|
||||
{
|
||||
Route::get('/', function () {
|
||||
report(new Exception('Test exception'));
|
||||
});
|
||||
|
||||
$this->get('/')->assertStatus(200);
|
||||
}
|
||||
|
||||
public function testReportedExceptionsAreNotThrownByDefaultWithoutExceptionHandling()
|
||||
{
|
||||
$this->withoutExceptionHandling();
|
||||
|
||||
Route::get('/', function () {
|
||||
report(new Exception('Test exception'));
|
||||
});
|
||||
|
||||
$this->get('/')->assertStatus(200);
|
||||
}
|
||||
|
||||
public function testThrowOnReport()
|
||||
{
|
||||
Exceptions::fake()->throwOnReport();
|
||||
|
||||
$this->expectException(Exception::class);
|
||||
$this->expectExceptionMessage('Test exception');
|
||||
|
||||
report(new Exception('Test exception'));
|
||||
}
|
||||
|
||||
public function testThrowOnReportDoesNotThrowExceptionsThatShouldNotBeReported()
|
||||
{
|
||||
Exceptions::fake()->throwOnReport();
|
||||
|
||||
Route::get('/302', function () {
|
||||
Validator::validate(['name' => ''], ['name' => 'required']);
|
||||
});
|
||||
|
||||
$this->get('/302')->assertStatus(302);
|
||||
|
||||
Route::get('/404', function () {
|
||||
throw new ModelNotFoundException();
|
||||
});
|
||||
|
||||
$this->get('/404')->assertStatus(404);
|
||||
|
||||
$this->doesNotPerformAssertions();
|
||||
|
||||
Exceptions::assertReportedCount(0);
|
||||
}
|
||||
|
||||
public function testThrowOnReportWithExceptionHandling()
|
||||
{
|
||||
Exceptions::fake()->throwOnReport();
|
||||
|
||||
Route::get('/', function () {
|
||||
report(new Exception('Test exception'));
|
||||
});
|
||||
|
||||
$this->expectException(Exception::class);
|
||||
$this->expectExceptionMessage('Test exception');
|
||||
|
||||
$this->get('/');
|
||||
}
|
||||
|
||||
public function testThrowOnReportWithoutExceptionHandling()
|
||||
{
|
||||
Exceptions::fake()->throwOnReport();
|
||||
|
||||
$this->withoutExceptionHandling();
|
||||
|
||||
Route::get('/', function () {
|
||||
report(new Exception('Test exception'));
|
||||
});
|
||||
|
||||
$this->expectException(Exception::class);
|
||||
$this->expectExceptionMessage('Test exception');
|
||||
|
||||
$this->get('/');
|
||||
}
|
||||
|
||||
public function testThrowOnReportRegardlessOfTheCallingOrderOfWithoutExceptionHandling()
|
||||
{
|
||||
Exceptions::fake()->throwOnReport();
|
||||
|
||||
$this
|
||||
->withoutExceptionHandling()
|
||||
->withExceptionHandling()
|
||||
->withoutExceptionHandling();
|
||||
|
||||
Route::get('/', function () {
|
||||
rescue(fn () => throw new Exception('Test exception'));
|
||||
});
|
||||
|
||||
$this->expectException(Exception::class);
|
||||
$this->expectExceptionMessage('Test exception');
|
||||
|
||||
$this->get('/');
|
||||
}
|
||||
|
||||
public function testThrowOnReportRegardlessOfTheCallingOrderOfWithExceptionHandling()
|
||||
{
|
||||
Exceptions::fake()->throwOnReport();
|
||||
|
||||
$this->withoutExceptionHandling()
|
||||
->withExceptionHandling()
|
||||
->withoutExceptionHandling()
|
||||
->withExceptionHandling();
|
||||
|
||||
Route::get('/', function () {
|
||||
rescue(fn () => throw new Exception('Test exception'));
|
||||
});
|
||||
|
||||
$this->expectException(Exception::class);
|
||||
$this->expectExceptionMessage('Test exception');
|
||||
|
||||
$this->get('/');
|
||||
}
|
||||
|
||||
public function testThrowOnReportWithFakedExceptions()
|
||||
{
|
||||
Exceptions::fake([InvalidArgumentException::class])->throwOnReport();
|
||||
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
|
||||
report(new Exception('Test exception'));
|
||||
report(new RuntimeException('Test exception'));
|
||||
report(new InvalidArgumentException('Test exception'));
|
||||
}
|
||||
|
||||
public function testThrowOnReportWithFakedExceptionsFromFacade()
|
||||
{
|
||||
Exceptions::fake([InvalidArgumentException::class])->throwOnReport();
|
||||
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
|
||||
report(new Exception('Test exception'));
|
||||
report(new RuntimeException('Test exception'));
|
||||
Exceptions::assertReportedCount(0);
|
||||
|
||||
report(new InvalidArgumentException('Test exception'));
|
||||
}
|
||||
|
||||
public function testThrowOnReporEvenWhenAppReportablesReturnFalse()
|
||||
{
|
||||
app(ExceptionHandler::class)->reportable(function (Throwable $e) {
|
||||
return false;
|
||||
});
|
||||
|
||||
Exceptions::fake()->throwOnReport();
|
||||
|
||||
$this->expectException(Exception::class);
|
||||
$this->expectExceptionMessage('Test exception');
|
||||
|
||||
report(new Exception('Test exception'));
|
||||
}
|
||||
|
||||
public function testAppReportablesAreNotCalledIfExceptionIsNotFaked()
|
||||
{
|
||||
app(ExceptionHandler::class)->reportable(function (Throwable $e) {
|
||||
throw new InvalidArgumentException($e->getMessage());
|
||||
});
|
||||
|
||||
Exceptions::fake([RuntimeException::class, Exception::class]);
|
||||
|
||||
report(new Exception('My exception message'));
|
||||
|
||||
Exceptions::assertReported(Exception::class);
|
||||
}
|
||||
|
||||
public function testThrowOnReportLeaveAppReportablesUntouched()
|
||||
{
|
||||
app(ExceptionHandler::class)->reportable(function (Throwable $e) {
|
||||
throw new InvalidArgumentException($e->getMessage());
|
||||
});
|
||||
|
||||
Exceptions::fake([RuntimeException::class])->throwOnReport();
|
||||
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('My exception message');
|
||||
|
||||
report(new Exception('My exception message'));
|
||||
}
|
||||
|
||||
public function testThrowReportedExceptions()
|
||||
{
|
||||
Exceptions::fake();
|
||||
|
||||
$this->expectException(Exception::class);
|
||||
$this->expectExceptionMessage('Test exception');
|
||||
|
||||
report(new Exception('Test exception'));
|
||||
|
||||
Exceptions::throwFirstReported();
|
||||
}
|
||||
|
||||
public function testThrowReportedExceptionsWithFakedExceptions()
|
||||
{
|
||||
Exceptions::fake([InvalidArgumentException::class]);
|
||||
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('Test exception');
|
||||
|
||||
report(new RuntimeException('Test exception'));
|
||||
report(new InvalidArgumentException('Test exception'));
|
||||
|
||||
Exceptions::throwFirstReported();
|
||||
}
|
||||
|
||||
public function testThrowReportedExceptionsWhenThereIsNone()
|
||||
{
|
||||
Exceptions::fake();
|
||||
|
||||
Exceptions::throwFirstReported();
|
||||
|
||||
Exceptions::fake([InvalidArgumentException::class]);
|
||||
|
||||
report(new RuntimeException('Test exception'));
|
||||
|
||||
Exceptions::throwFirstReported();
|
||||
|
||||
$this->doesNotPerformAssertions();
|
||||
}
|
||||
|
||||
public function testFakingExceptionsThatShouldNotBeReportedWithExceptionHandling()
|
||||
{
|
||||
Exceptions::fake();
|
||||
|
||||
Route::get('/302', function () {
|
||||
Validator::validate(['name' => ''], ['name' => 'required']);
|
||||
});
|
||||
|
||||
$this->get('/302')->assertStatus(302);
|
||||
|
||||
Route::get('/404', function () {
|
||||
throw new ModelNotFoundException();
|
||||
});
|
||||
|
||||
$this->get('/404')->assertStatus(404);
|
||||
|
||||
report(new ModelNotFoundException());
|
||||
|
||||
Exceptions::assertNothingReported();
|
||||
}
|
||||
|
||||
public function testFakingExceptionsThatShouldNotBeReportedWithRescueAndWithoutExceptionHandling()
|
||||
{
|
||||
Exceptions::fake();
|
||||
|
||||
$this->withoutExceptionHandling();
|
||||
|
||||
Route::get('/validation', function () {
|
||||
rescue(fn () => Validator::validate(['name' => ''], ['name' => 'required']));
|
||||
});
|
||||
|
||||
$this->get('/validation')->assertStatus(200);
|
||||
|
||||
Route::get('/model', function () {
|
||||
rescue(fn () => throw new ModelNotFoundException());
|
||||
});
|
||||
|
||||
$this->get('/model')->assertStatus(200);
|
||||
|
||||
rescue(fn () => throw new ModelNotFoundException());
|
||||
|
||||
Exceptions::assertReportedCount(3);
|
||||
}
|
||||
|
||||
public function testRescue()
|
||||
{
|
||||
Exceptions::fake();
|
||||
|
||||
rescue(fn () => throw new Exception('Test exception'));
|
||||
|
||||
Exceptions::assertReported(Exception::class);
|
||||
}
|
||||
|
||||
public function testRescueWithoutReport()
|
||||
{
|
||||
Exceptions::fake();
|
||||
|
||||
rescue(fn () => throw new Exception('Test exception'), null, false);
|
||||
|
||||
Exceptions::assertNothingReported();
|
||||
}
|
||||
|
||||
public function testFlowBetweenFakeAndTestExceptionHandling()
|
||||
{
|
||||
$this->assertInstanceOf(Handler::class, app(ExceptionHandler::class));
|
||||
|
||||
Exceptions::fake();
|
||||
$this->assertInstanceOf(ExceptionHandlerFake::class, app(ExceptionHandler::class));
|
||||
$this->assertInstanceOf(Handler::class, Exceptions::fake()->handler());
|
||||
$this->assertFalse((new \ReflectionClass(Exceptions::fake()->handler()))->isAnonymous());
|
||||
|
||||
Exceptions::fake();
|
||||
$this->assertInstanceOf(ExceptionHandlerFake::class, app(ExceptionHandler::class));
|
||||
$this->assertInstanceOf(Handler::class, Exceptions::fake()->handler());
|
||||
$this->assertFalse((new \ReflectionClass(Exceptions::fake()->handler()))->isAnonymous());
|
||||
|
||||
$this->withoutExceptionHandling();
|
||||
$this->assertInstanceOf(ExceptionHandlerFake::class, app(ExceptionHandler::class));
|
||||
$this->assertInstanceOf(ExceptionHandler::class, Exceptions::fake()->handler());
|
||||
$this->assertTrue((new \ReflectionClass(Exceptions::fake()->handler()))->isAnonymous());
|
||||
|
||||
$this->withExceptionHandling();
|
||||
$this->assertInstanceOf(ExceptionHandlerFake::class, app(ExceptionHandler::class));
|
||||
$this->assertInstanceOf(ExceptionHandler::class, Exceptions::fake()->handler());
|
||||
$this->assertFalse((new \ReflectionClass(Exceptions::fake()->handler()))->isAnonymous());
|
||||
|
||||
Exceptions::fake();
|
||||
$this->assertInstanceOf(ExceptionHandlerFake::class, app(ExceptionHandler::class));
|
||||
$this->assertInstanceOf(Handler::class, Exceptions::fake()->handler());
|
||||
$this->assertFalse((new \ReflectionClass(Exceptions::fake()->handler()))->isAnonymous());
|
||||
}
|
||||
|
||||
public function testFlowBetweenTestExceptionHandlingAndFake()
|
||||
{
|
||||
$this->withoutExceptionHandling();
|
||||
$this->assertTrue((new \ReflectionClass(app(ExceptionHandler::class)))->isAnonymous());
|
||||
|
||||
Exceptions::fake();
|
||||
$this->assertInstanceOf(ExceptionHandlerFake::class, app(ExceptionHandler::class));
|
||||
$this->assertInstanceOf(ExceptionHandler::class, Exceptions::fake()->handler());
|
||||
$this->assertTrue((new \ReflectionClass(Exceptions::fake()->handler()))->isAnonymous());
|
||||
|
||||
Exceptions::fake();
|
||||
$this->assertInstanceOf(ExceptionHandlerFake::class, app(ExceptionHandler::class));
|
||||
$this->assertInstanceOf(ExceptionHandler::class, Exceptions::fake()->handler());
|
||||
$this->assertTrue((new \ReflectionClass(Exceptions::fake()->handler()))->isAnonymous());
|
||||
|
||||
$this->withExceptionHandling();
|
||||
$this->assertInstanceOf(ExceptionHandlerFake::class, app(ExceptionHandler::class));
|
||||
$this->assertInstanceOf(Handler::class, Exceptions::fake()->handler());
|
||||
$this->assertFalse((new \ReflectionClass(Exceptions::fake()->handler()))->isAnonymous());
|
||||
}
|
||||
|
||||
public function testWithDeprecationHandling()
|
||||
{
|
||||
Exceptions::fake();
|
||||
|
||||
Route::get('/', function () {
|
||||
str_contains(null, null);
|
||||
});
|
||||
|
||||
$this->get('/')->assertStatus(200);
|
||||
|
||||
Exceptions::assertNothingReported();
|
||||
}
|
||||
|
||||
public function testWithoutDeprecationHandler()
|
||||
{
|
||||
Exceptions::fake();
|
||||
|
||||
$this->withoutDeprecationHandling();
|
||||
|
||||
Route::get('/', function () {
|
||||
str_contains(null, null);
|
||||
});
|
||||
|
||||
$this->get('/')->assertStatus(500);
|
||||
|
||||
Exceptions::assertReported(function (ErrorException $e) {
|
||||
return $e->getMessage() === 'str_contains(): Passing null to parameter #1 ($haystack) of type string is deprecated';
|
||||
});
|
||||
|
||||
Exceptions::assertReportedCount(1);
|
||||
}
|
||||
}
|
|
@ -13,3 +13,9 @@ enum CategoryBackedEnum: string
|
|||
case People = 'people';
|
||||
case Fruits = 'fruits';
|
||||
}
|
||||
|
||||
enum CategoryIntBackedEnum: int
|
||||
{
|
||||
case People = 1;
|
||||
case Fruits = 2;
|
||||
}
|
||||
|
|
|
@ -31,6 +31,24 @@ class ImplicitRouteBindingTest extends TestCase
|
|||
$this->assertSame('fruits', $route->parameter('category')->value);
|
||||
}
|
||||
|
||||
public function test_it_can_resolve_the_implicit_int_backed_enum_route_bindings_for_the_given_route()
|
||||
{
|
||||
$action = ['uses' => function (CategoryIntBackedEnum $category) {
|
||||
return $category->value;
|
||||
}];
|
||||
|
||||
$route = new Route('GET', '/test', $action);
|
||||
$route->parameters = ['category' => '1'];
|
||||
|
||||
$route->prepareForSerialization();
|
||||
|
||||
$container = Container::getInstance();
|
||||
|
||||
ImplicitRouteBinding::resolveForRoute($container, $route);
|
||||
|
||||
$this->assertSame(1, $route->parameter('category')->value);
|
||||
}
|
||||
|
||||
public function test_it_can_resolve_the_implicit_backed_enum_route_bindings_for_the_given_route_with_optional_parameter()
|
||||
{
|
||||
$action = ['uses' => function (?CategoryBackedEnum $category = null) {
|
||||
|
@ -91,6 +109,29 @@ class ImplicitRouteBindingTest extends TestCase
|
|||
ImplicitRouteBinding::resolveForRoute($container, $route);
|
||||
}
|
||||
|
||||
public function test_implicit_int_backed_enum_internal_exception()
|
||||
{
|
||||
$action = ['uses' => function (CategoryIntBackedEnum $category) {
|
||||
return $category->value;
|
||||
}];
|
||||
|
||||
$route = new Route('GET', '/test', $action);
|
||||
$route->parameters = ['category' => ' 00001.'];
|
||||
|
||||
$route->prepareForSerialization();
|
||||
|
||||
$container = Container::getInstance();
|
||||
|
||||
$this->expectException(BackedEnumCaseNotFoundException::class);
|
||||
$this->expectExceptionMessage(sprintf(
|
||||
'Case [%s] not found on Backed Enum [%s].',
|
||||
' 00001.',
|
||||
CategoryIntBackedEnum::class,
|
||||
));
|
||||
|
||||
ImplicitRouteBinding::resolveForRoute($container, $route);
|
||||
}
|
||||
|
||||
public function test_it_can_resolve_the_implicit_model_route_bindings_for_the_given_route()
|
||||
{
|
||||
$this->expectNotToPerformAssertions();
|
||||
|
|
|
@ -12,3 +12,9 @@ enum TestBackedEnum: int
|
|||
case A = 1;
|
||||
case B = 2;
|
||||
}
|
||||
|
||||
enum TestStringBackedEnum: string
|
||||
{
|
||||
case A = 'A';
|
||||
case B = 'B';
|
||||
}
|
||||
|
|
|
@ -2961,6 +2961,32 @@ class SupportCollectionTest extends TestCase
|
|||
$this->assertSame('second', $data->get(1)->value);
|
||||
}
|
||||
|
||||
#[DataProvider('collectionClassProvider')]
|
||||
public function testMapIntoWithIntBackedEnums($collection)
|
||||
{
|
||||
$data = new $collection([
|
||||
1, 2,
|
||||
]);
|
||||
|
||||
$data = $data->mapInto(TestBackedEnum::class);
|
||||
|
||||
$this->assertSame(TestBackedEnum::A, $data->get(0));
|
||||
$this->assertSame(TestBackedEnum::B, $data->get(1));
|
||||
}
|
||||
|
||||
#[DataProvider('collectionClassProvider')]
|
||||
public function testMapIntoWithStringBackedEnums($collection)
|
||||
{
|
||||
$data = new $collection([
|
||||
'A', 'B',
|
||||
]);
|
||||
|
||||
$data = $data->mapInto(TestStringBackedEnum::class);
|
||||
|
||||
$this->assertSame(TestStringBackedEnum::A, $data->get(0));
|
||||
$this->assertSame(TestStringBackedEnum::B, $data->get(1));
|
||||
}
|
||||
|
||||
#[DataProvider('collectionClassProvider')]
|
||||
public function testNth($collection)
|
||||
{
|
||||
|
|
|
@ -7,6 +7,7 @@ use Illuminate\Support\Carbon;
|
|||
use Illuminate\Support\ItemNotFoundException;
|
||||
use Illuminate\Support\LazyCollection;
|
||||
use Illuminate\Support\MultipleItemsFoundException;
|
||||
use Illuminate\Support\Sleep;
|
||||
use Mockery as m;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use stdClass;
|
||||
|
@ -1370,6 +1371,25 @@ class SupportLazyCollectionIsLazyTest extends TestCase
|
|||
});
|
||||
}
|
||||
|
||||
public function testThrottleIsLazy()
|
||||
{
|
||||
Sleep::fake();
|
||||
|
||||
$this->assertDoesNotEnumerate(function ($collection) {
|
||||
$collection->throttle(10);
|
||||
});
|
||||
|
||||
$this->assertEnumerates(5, function ($collection) {
|
||||
$collection->throttle(10)->take(5)->all();
|
||||
});
|
||||
|
||||
$this->assertEnumeratesOnce(function ($collection) {
|
||||
$collection->throttle(10)->all();
|
||||
});
|
||||
|
||||
Sleep::fake(false);
|
||||
}
|
||||
|
||||
public function testTimesIsLazy()
|
||||
{
|
||||
$data = LazyCollection::times(INF);
|
||||
|
|
|
@ -2,9 +2,12 @@
|
|||
|
||||
namespace Illuminate\Tests\Support;
|
||||
|
||||
use Carbon\CarbonInterval as Duration;
|
||||
use Illuminate\Foundation\Testing\Wormhole;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\LazyCollection;
|
||||
use Illuminate\Support\Sleep;
|
||||
use InvalidArgumentException;
|
||||
use Mockery as m;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
@ -223,6 +226,58 @@ class SupportLazyCollectionTest extends TestCase
|
|||
$this->assertSame([1, 2, 3, 4, 5], $tapped);
|
||||
}
|
||||
|
||||
public function testThrottle()
|
||||
{
|
||||
Sleep::fake();
|
||||
|
||||
$data = LazyCollection::times(3)
|
||||
->throttle(2)
|
||||
->all();
|
||||
|
||||
Sleep::assertSlept(function (Duration $duration) {
|
||||
$this->assertEqualsWithDelta(
|
||||
2_000_000, $duration->totalMicroseconds, 1_000
|
||||
);
|
||||
|
||||
return true;
|
||||
}, times: 3);
|
||||
|
||||
$this->assertSame([1, 2, 3], $data);
|
||||
|
||||
Sleep::fake(false);
|
||||
}
|
||||
|
||||
public function testThrottleAccountsForTimePassed()
|
||||
{
|
||||
Sleep::fake();
|
||||
Carbon::setTestNow(now());
|
||||
|
||||
$data = LazyCollection::times(3)
|
||||
->throttle(3)
|
||||
->tapEach(function ($value, $index) {
|
||||
if ($index == 1) {
|
||||
// Travel in time...
|
||||
(new Wormhole(1))->second();
|
||||
}
|
||||
})
|
||||
->all();
|
||||
|
||||
Sleep::assertSlept(function (Duration $duration, int $index) {
|
||||
$expectation = $index == 1 ? 2_000_000 : 3_000_000;
|
||||
|
||||
$this->assertEqualsWithDelta(
|
||||
$expectation, $duration->totalMicroseconds, 1_000
|
||||
);
|
||||
|
||||
return true;
|
||||
}, times: 3);
|
||||
|
||||
$this->assertSame([1, 2, 3], $data);
|
||||
|
||||
Sleep::fake(false);
|
||||
Carbon::setTestNow();
|
||||
}
|
||||
|
||||
public function testUniqueDoubleEnumeration()
|
||||
{
|
||||
$data = LazyCollection::times(2)->unique();
|
||||
|
|
|
@ -37,6 +37,17 @@ class SupportStrTest extends TestCase
|
|||
{
|
||||
$this->assertSame('Jefferson Costella', Str::title('jefferson costella'));
|
||||
$this->assertSame('Jefferson Costella', Str::title('jefFErson coSTella'));
|
||||
|
||||
$this->assertSame('', Str::title(''));
|
||||
$this->assertSame('123 Laravel', Str::title('123 laravel'));
|
||||
$this->assertSame('❤Laravel', Str::title('❤laravel'));
|
||||
$this->assertSame('Laravel ❤', Str::title('laravel ❤'));
|
||||
$this->assertSame('Laravel123', Str::title('laravel123'));
|
||||
$this->assertSame('Laravel123', Str::title('Laravel123'));
|
||||
|
||||
$longString = 'lorem ipsum '.str_repeat('dolor sit amet ', 1000);
|
||||
$expectedResult = 'Lorem Ipsum Dolor Sit Amet '.str_repeat('Dolor Sit Amet ', 999);
|
||||
$this->assertSame($expectedResult, Str::title($longString));
|
||||
}
|
||||
|
||||
public function testStringHeadline()
|
||||
|
|
|
@ -2447,6 +2447,22 @@ class ValidationValidatorTest extends TestCase
|
|||
$this->assertSame('The foo field must be accepted when bar is true.', $v->messages()->first('foo'));
|
||||
}
|
||||
|
||||
public function testValidateRequiredIfDeclined()
|
||||
{
|
||||
$trans = $this->getIlluminateArrayTranslator();
|
||||
$v = new Validator($trans, ['foo' => 'yes', 'bar' => 'baz'], ['bar' => 'required_if_declined:foo']);
|
||||
$this->assertTrue($v->passes());
|
||||
|
||||
$v = new Validator($trans, ['foo' => 'no', 'bar' => 'baz'], ['bar' => 'required_if_declined:foo']);
|
||||
$this->assertTrue($v->passes());
|
||||
|
||||
$v = new Validator($trans, ['foo' => 'yes', 'bar' => ''], ['bar' => 'required_if_declined:foo']);
|
||||
$this->assertTrue($v->passes());
|
||||
|
||||
$v = new Validator($trans, ['foo' => 'no', 'bar' => ''], ['bar' => 'required_if_declined:foo']);
|
||||
$this->assertFalse($v->passes());
|
||||
}
|
||||
|
||||
public function testValidateDeclined()
|
||||
{
|
||||
$trans = $this->getIlluminateArrayTranslator();
|
||||
|
|
|
@ -113,6 +113,17 @@ class ViewComponentAttributeBagTest extends TestCase
|
|||
$this->assertSame('required="required" x-data=""', (string) $bag);
|
||||
}
|
||||
|
||||
public function testItMakesAnExceptionForLivewireWireAttributes()
|
||||
{
|
||||
$bag = new ComponentAttributeBag([
|
||||
'wire:loading' => true,
|
||||
'wire:loading.remove' => true,
|
||||
'wire:poll' => true,
|
||||
]);
|
||||
|
||||
$this->assertSame('wire:loading="" wire:loading.remove="" wire:poll=""', (string) $bag);
|
||||
}
|
||||
|
||||
public function testAttributeExistence()
|
||||
{
|
||||
$bag = new ComponentAttributeBag(['name' => 'test']);
|
||||
|
|
Loading…
Reference in New Issue