Skip to content

Commit d88336c

Browse files
Fix issue #271: Storage and storage_path inconsistences; add additional tests; upgrade phpunit.xml (#272)
* Fix issue with Storage and storage_path inconsistencies; Upgrade phpunit.xml; Add additional tests * Apply styleci fixes
1 parent adf3b23 commit d88336c

12 files changed

+334
-46
lines changed

composer.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
"illuminate/database": "^8.11",
2525
"illuminate/events": "^8.11",
2626
"illuminate/notifications": "^8.11",
27-
"laravelcollective/html": "^6.0"
27+
"laravelcollective/html": "^6.0",
28+
"ext-json": "*"
2829
},
2930
"require-dev": {
3031
"mockery/mockery": "^1.0",

config/totem.php

+2
Original file line numberDiff line numberDiff line change
@@ -233,4 +233,6 @@
233233
'enabled' => env('TOTEM_BROADCASTING_ENABLED', true),
234234
'channel' => env('TOTEM_BROADCASTING_CHANNEL', 'task.events'),
235235
],
236+
237+
'log_folder' => env('TOTEM_LOG_FOLDER', 'totem'),
236238
];

phpunit.xml

+23-31
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,25 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<phpunit backupGlobals="false"
3-
backupStaticAttributes="false"
4-
bootstrap="vendor/autoload.php"
5-
colors="true"
6-
convertErrorsToExceptions="true"
7-
convertNoticesToExceptions="true"
8-
convertWarningsToExceptions="true"
9-
processIsolation="false"
10-
stopOnFailure="false">
11-
<testsuites>
12-
<testsuite name="Laravel Totem Test Suite">
13-
<directory suffix="Test.php">./tests/</directory>
14-
</testsuite>
15-
</testsuites>
16-
<filter>
17-
<whitelist>
18-
<directory suffix=".php">./src/</directory>
19-
<exclude>
20-
<file>./src/ServiceProvider.php</file>
21-
</exclude>
22-
</whitelist>
23-
</filter>
24-
<php>
25-
<env name="APP_ENV" value="testing"></env>
26-
<env name="CACHE_DRIVER" value="array"/>
27-
<env name="SESSION_DRIVER" value="array"/>
28-
<env name="QUEUE_DRIVER" value="sync"/>
29-
<env name="DB_CONNECTION" value="sqlite"></env>
30-
<env name="DB_DATABASE" value=":memory:"></env>
31-
<env name="APP_KEY" value="AckfSECXIvnK5r28GVIWUAxmbBSjTsmF"/>
32-
</php>
2+
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" backupStaticAttributes="false" bootstrap="vendor/autoload.php" colors="true" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
3+
<coverage>
4+
<include>
5+
<directory suffix=".php">./src/</directory>
6+
</include>
7+
<exclude>
8+
<file>./src/ServiceProvider.php</file>
9+
</exclude>
10+
</coverage>
11+
<testsuites>
12+
<testsuite name="Laravel Totem Test Suite">
13+
<directory suffix="Test.php">./tests/</directory>
14+
</testsuite>
15+
</testsuites>
16+
<php>
17+
<env name="APP_ENV" value="testing"/>
18+
<env name="CACHE_DRIVER" value="array"/>
19+
<env name="SESSION_DRIVER" value="array"/>
20+
<env name="QUEUE_DRIVER" value="sync"/>
21+
<env name="DB_CONNECTION" value="sqlite"/>
22+
<env name="DB_DATABASE" value=":memory:"/>
23+
<env name="APP_KEY" value="AckfSECXIvnK5r28GVIWUAxmbBSjTsmF"/>
24+
</php>
3325
</phpunit>

src/Events/Executed.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,15 @@ public function __construct(Task $task, $started)
2020

2121
$time_elapsed_secs = microtime(true) - $started;
2222

23-
if (Storage::exists(storage_path($task->getMutexName()))) {
24-
$output = Storage::get(storage_path($task->getMutexName()));
23+
if (Storage::exists($task->getMutexName())) {
24+
$output = Storage::get($task->getMutexName());
2525

2626
$task->results()->create([
2727
'duration' => $time_elapsed_secs * 1000,
2828
'result' => $output,
2929
]);
3030

31-
Storage::delete(storage_path($task->getMutexName()));
31+
Storage::delete($task->getMutexName());
3232

3333
$task->notify(new TaskCompleted($output));
3434
$task->autoCleanup();

src/Http/Controllers/ExportTasksController.php

+11-6
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22

33
namespace Studio\Totem\Http\Controllers;
44

5-
use File;
6-
use function storage_path;
75
use Studio\Totem\Contracts\TaskInterface;
86

97
class ExportTasksController extends Controller
@@ -27,14 +25,21 @@ public function __construct(TaskInterface $tasks)
2725
/**
2826
* Export all tasks to a json file.
2927
*
30-
* @return \Symfony\Component\HttpFoundation\BinaryFileResponse
28+
* @return \Symfony\Component\HttpFoundation\StreamedResponse
3129
*/
3230
public function index()
3331
{
34-
File::put(storage_path('tasks.json'), $this->tasks->findAll()->toJson());
32+
$headers = [
33+
'Content-Type' => 'text/json',
34+
'Pragma' => 'no-cache',
35+
'Cache-Control' => 'must-revalidate, post-check=0, pre-check=0',
36+
'Expires' => '0',
37+
];
3538

3639
return response()
37-
->download(storage_path('tasks.json'), 'tasks.json')
38-
->deleteFileAfterSend(true);
40+
->streamDownload(function () {
41+
echo $this->tasks->findAll()->toJson();
42+
}, 'tasks.json',
43+
$headers);
3944
}
4045
}

src/Providers/ConsoleServiceProvider.php

+6-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Studio\Totem\Providers;
44

55
use Illuminate\Console\Scheduling\Schedule;
6+
use Illuminate\Support\Facades\Storage;
67
use Illuminate\Support\ServiceProvider;
78
use Studio\Totem\Events\Executed;
89
use Studio\Totem\Events\Executing;
@@ -30,6 +31,10 @@ public function schedule(Schedule $schedule)
3031
{
3132
$tasks = app('totem.tasks')->findAllActive();
3233

34+
Storage::makeDirectory(config('totem.log_folder'));
35+
36+
Storage::put(config('totem.log_folder').'/test.txt', 'abcdegf');
37+
3338
$tasks->each(function ($task) use ($schedule) {
3439
$event = $schedule->command($task->command, $task->compileParameters(true));
3540

@@ -43,7 +48,7 @@ public function schedule(Schedule $schedule)
4348
->after(function () use ($event, $task) {
4449
Executed::dispatch($task, $event->start ?? microtime(true));
4550
})
46-
->sendOutputTo(storage_path($task->getMutexName()));
51+
->sendOutputTo(Storage::path($task->getMutexName()));
4752
if ($task->dont_overlap) {
4853
$event->withoutOverlapping();
4954
}

src/Repositories/EloquentTaskRepository.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -196,9 +196,9 @@ public function execute($id)
196196
try {
197197
Artisan::call($task->command, $task->compileParameters());
198198

199-
Storage::put(storage_path($task->getMutexName()), Artisan::output());
199+
Storage::put($task->getMutexName(), Artisan::output());
200200
} catch (\Exception $e) {
201-
Storage::put(storage_path($task->getMutexName()), $e->getMessage());
201+
Storage::put($task->getMutexName(), $e->getMessage());
202202
}
203203

204204
Executed::dispatch($task, $start);

src/Traits/HasFrequencies.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ public function getCronExpression()
123123
*/
124124
public function getMutexName()
125125
{
126-
return 'logs'.DIRECTORY_SEPARATOR.'schedule-'.sha1($this->expression.$this->command.$this->parameters);
126+
return config('totem.log_folder').DIRECTORY_SEPARATOR.'schedule-'.sha1($this->expression.$this->command.$this->parameters);
127127
}
128128

129129
/**
@@ -194,7 +194,7 @@ private function processData()
194194
})
195195
->first();
196196

197-
if ($task && $task->frequencies) {
197+
if ($task && ($task->frequencies ?? false)) {
198198
$data['type'] = 'frequency';
199199
$data['frequencies'] = collect($task->frequencies)->map(function ($frequency) {
200200
return (array) $frequency;

tests/Feature/ExportTasksTest.php

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
namespace Studio\Totem\Tests\Feature;
4+
5+
use Carbon\Carbon;
6+
use Studio\Totem\Task;
7+
use Studio\Totem\Tests\TestCase;
8+
9+
class ExportTasksTest extends TestCase
10+
{
11+
/** @test */
12+
public function it_exports_tasks_as_json()
13+
{
14+
$tasks = Task::factory()->count(5)->create();
15+
16+
$exportedTasks = $this->signIn()
17+
->get(route('totem.tasks.export'))
18+
->assertHeader('Content-Disposition', 'attachment; filename=tasks.json')
19+
->assertHeader('Content-Type', 'text/json; charset=UTF-8')
20+
->streamedContent();
21+
22+
$exportedTasks = json_decode($exportedTasks);
23+
24+
$this->assertCount($tasks->count(), $exportedTasks);
25+
26+
collect($exportedTasks)->each(function ($exportedTask) {
27+
$task = Task::find($exportedTask->id);
28+
29+
$this->assertEquals($task->description, $exportedTask->description);
30+
$this->assertEquals($task->command, $exportedTask->command);
31+
$this->assertEquals($task->parameters, $exportedTask->parameters);
32+
$this->assertEquals($task->expression, $exportedTask->expression);
33+
$this->assertEquals($task->timezone, $exportedTask->timezone);
34+
$this->assertEquals($task->is_active, $exportedTask->is_active);
35+
$this->assertEquals($task->dont_overlap, $exportedTask->dont_overlap);
36+
$this->assertEquals($task->run_in_maintenance, $exportedTask->run_in_maintenance);
37+
$this->assertEquals($task->notification_email_address, $exportedTask->notification_email_address);
38+
$this->assertTrue($task->created_at->eq(Carbon::parse($exportedTask->created_at)));
39+
$this->assertTrue($task->updated_at->eq(Carbon::parse($exportedTask->updated_at)));
40+
$this->assertEquals($task->notification_phone_number, $exportedTask->notification_phone_number);
41+
$this->assertEquals($task->notification_slack_webhook, $exportedTask->notification_slack_webhook);
42+
$this->assertEquals($task->auto_cleanup_num, $exportedTask->auto_cleanup_num);
43+
$this->assertEquals($task->auto_cleanup_type, $exportedTask->auto_cleanup_type);
44+
$this->assertEquals($task->run_on_one_server, $exportedTask->run_on_one_server);
45+
$this->assertEquals($task->run_in_background, $exportedTask->run_in_background);
46+
$this->assertEquals($task->activated, $exportedTask->activated);
47+
$this->assertEquals($task->upcoming, $exportedTask->upcoming);
48+
$this->assertEquals($task->last_result, $exportedTask->last_result);
49+
$this->assertEquals($task->average_runtime, $exportedTask->average_runtime);
50+
});
51+
}
52+
}

tests/Feature/ImportTasksTest.php

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
namespace Studio\Totem\Tests\Feature;
4+
5+
use Illuminate\Http\UploadedFile;
6+
use Illuminate\Support\Facades\Event;
7+
use Studio\Totem\Events\Created;
8+
use Studio\Totem\Events\Creating;
9+
use Studio\Totem\Task;
10+
use Studio\Totem\Tests\TestCase;
11+
12+
class ImportTasksTest extends TestCase
13+
{
14+
/** @test */
15+
public function it_imports_tasks_from_a_json_file()
16+
{
17+
Event::fake();
18+
19+
$this->assertEquals(0, Task::count());
20+
21+
$this->signIn()
22+
->post(route('totem.tasks.import'), [
23+
'tasks' => new UploadedFile(realpath(__DIR__.'/../Fixtures/tasks.json'), 'tasks.json', 'json', false, true),
24+
])->assertSuccessful();
25+
26+
$this->assertEquals(5, Task::count());
27+
28+
collect(json_decode(file_get_contents(realpath(__DIR__.'/../Fixtures/tasks.json'))))
29+
->each(function ($jsonTask) {
30+
$task = Task::find($jsonTask->id);
31+
32+
$this->assertEquals($jsonTask->description, $task->description);
33+
$this->assertEquals($jsonTask->command, $task->command);
34+
$this->assertEquals($jsonTask->parameters, $task->parameters);
35+
$this->assertEquals($jsonTask->expression, $task->expression);
36+
$this->assertEquals($jsonTask->timezone, $task->timezone);
37+
$this->assertEquals($jsonTask->is_active, $task->is_active);
38+
$this->assertEquals($jsonTask->dont_overlap, $task->dont_overlap);
39+
$this->assertEquals($jsonTask->run_in_maintenance, $task->run_in_maintenance);
40+
$this->assertEquals($jsonTask->notification_email_address, $task->notification_email_address);
41+
$this->assertEquals($jsonTask->notification_phone_number, $task->notification_phone_number);
42+
$this->assertEquals($jsonTask->notification_slack_webhook, $task->notification_slack_webhook);
43+
$this->assertEquals($jsonTask->auto_cleanup_num, $task->auto_cleanup_num);
44+
$this->assertEquals($jsonTask->auto_cleanup_type, $task->auto_cleanup_type);
45+
$this->assertEquals($jsonTask->run_on_one_server, $task->run_on_one_server);
46+
$this->assertEquals($jsonTask->run_in_background, $task->run_in_background);
47+
$this->assertEquals($jsonTask->activated, $task->activated);
48+
});
49+
50+
Event::assertDispatched(Creating::class, 5);
51+
Event::assertDispatched(Created::class, 5);
52+
}
53+
}

tests/Feature/TaskExecutionTest.php

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
namespace Studio\Totem\Tests\Feature;
4+
5+
use Illuminate\Console\Scheduling\Schedule;
6+
use Illuminate\Support\Facades\Event;
7+
use Studio\Totem\Events\Executed;
8+
use Studio\Totem\Events\Executing;
9+
use Studio\Totem\Providers\ConsoleServiceProvider;
10+
use Studio\Totem\Result;
11+
use Studio\Totem\Task;
12+
use Studio\Totem\Tests\TestCase;
13+
14+
class TaskExecutionTest extends TestCase
15+
{
16+
/** @test */
17+
public function it_runs_a_scheduled_task()
18+
{
19+
$task = Task::factory()->create();
20+
21+
Event::fake();
22+
23+
$scheduler = $this->app->get(Schedule::class);
24+
$this->app->resolveProvider(ConsoleServiceProvider::class)
25+
->schedule($scheduler);
26+
27+
$scheduler->events()[0]
28+
->run($this->app);
29+
30+
$this->assertEquals(1, Result::count());
31+
32+
$result = Result::first();
33+
$this->assertEquals($task->id, $result->task_id);
34+
35+
Event::assertDispatched(Executing::class);
36+
Event::assertDispatched(Executed::class);
37+
}
38+
39+
/** @test */
40+
public function it_executes_a_scheduled_task()
41+
{
42+
$task = Task::factory()->create();
43+
44+
Event::fake();
45+
46+
$this->get(route('totem.task.execute', $task->id))
47+
->assertSuccessful();
48+
49+
$this->assertEquals(1, Result::count());
50+
51+
$result = Result::first();
52+
$this->assertEquals($task->id, $result->task_id);
53+
54+
Event::assertDispatched(Executed::class);
55+
}
56+
}

0 commit comments

Comments
 (0)