Skip to content

Commit 35c4872

Browse files
authored
refactor: add full testing to logs:clear command (#10090)
1 parent b23f415 commit 35c4872

File tree

2 files changed

+125
-22
lines changed

2 files changed

+125
-22
lines changed

system/Commands/Housekeeping/ClearLogs.php

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -67,27 +67,22 @@ public function run(array $params)
6767
$force = array_key_exists('force', $params) || CLI::getOption('force');
6868

6969
if (! $force && CLI::prompt('Are you sure you want to delete the logs?', ['n', 'y']) === 'n') {
70-
// @codeCoverageIgnoreStart
71-
CLI::error('Deleting logs aborted.', 'light_gray', 'red');
72-
CLI::error('If you want, use the "-force" option to force delete all log files.', 'light_gray', 'red');
73-
CLI::newLine();
70+
CLI::error('Deleting logs aborted.');
71+
CLI::error('If you want, use the "--force" option to force delete all log files.');
7472

75-
return;
76-
// @codeCoverageIgnoreEnd
73+
return EXIT_ERROR;
7774
}
7875

7976
helper('filesystem');
8077

8178
if (! delete_files(WRITEPATH . 'logs', false, true)) {
82-
// @codeCoverageIgnoreStart
83-
CLI::error('Error in deleting the logs files.', 'light_gray', 'red');
84-
CLI::newLine();
79+
CLI::error('Error in deleting the logs files.');
8580

86-
return;
87-
// @codeCoverageIgnoreEnd
81+
return EXIT_ERROR;
8882
}
8983

9084
CLI::write('Logs cleared.', 'green');
91-
CLI::newLine();
85+
86+
return EXIT_SUCCESS;
9287
}
9388
}

tests/system/Commands/ClearLogsTest.php

Lines changed: 118 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@
1313

1414
namespace CodeIgniter\Commands;
1515

16+
use CodeIgniter\CLI\CLI;
1617
use CodeIgniter\Test\CIUnitTestCase;
18+
use CodeIgniter\Test\Mock\MockInputOutput;
1719
use CodeIgniter\Test\StreamFilterTrait;
1820
use PHPUnit\Framework\Attributes\Group;
21+
use PHPUnit\Framework\Attributes\RequiresOperatingSystem;
1922

2023
/**
2124
* @internal
@@ -34,37 +37,142 @@ protected function setUp(): void
3437
// test runs on other tests may log errors since default threshold
3538
// is now 4, so set this to a safe distance
3639
$this->date = date('Y-m-d', strtotime('+1 year'));
40+
41+
command('logs:clear --force');
42+
$this->resetStreamFilterBuffer();
43+
44+
$this->createDummyLogFiles();
45+
}
46+
47+
protected function tearDown(): void
48+
{
49+
command('logs:clear --force');
50+
$this->resetStreamFilterBuffer();
51+
52+
CLI::reset();
53+
54+
parent::tearDown();
3755
}
3856

39-
protected function createDummyLogFiles(): void
57+
private function createDummyLogFiles(): void
4058
{
4159
$date = $this->date;
4260
$path = WRITEPATH . 'logs' . DIRECTORY_SEPARATOR . "log-{$date}.log";
4361

4462
// create 10 dummy log files
4563
for ($i = 0; $i < 10; $i++) {
4664
$newDate = date('Y-m-d', strtotime("+1 year -{$i} day"));
47-
$path = str_replace($date, $newDate, $path);
65+
66+
$path = str_replace($date, $newDate, $path);
4867
file_put_contents($path, 'Lorem ipsum');
4968

5069
$date = $newDate;
5170
}
5271
}
5372

54-
public function testClearLogsWorks(): void
73+
public function testClearLogsUsingForce(): void
5574
{
56-
// test clean logs dir
75+
$this->assertFileExists(WRITEPATH . 'logs' . DIRECTORY_SEPARATOR . "log-{$this->date}.log");
76+
77+
command('logs:clear --force');
78+
5779
$this->assertFileDoesNotExist(WRITEPATH . 'logs' . DIRECTORY_SEPARATOR . "log-{$this->date}.log");
80+
$this->assertFileExists(WRITEPATH . 'logs' . DIRECTORY_SEPARATOR . 'index.html');
81+
$this->assertSame("Logs cleared.\n", preg_replace('/\e\[[^m]+m/', '', $this->getStreamFilterBuffer()));
82+
}
5883

59-
// test dir is now populated with logs
60-
$this->createDummyLogFiles();
84+
public function testClearLogsAbortsClearWithoutForce(): void
85+
{
86+
$this->assertFileExists(WRITEPATH . 'logs' . DIRECTORY_SEPARATOR . "log-{$this->date}.log");
87+
88+
$io = new MockInputOutput();
89+
$io->setInputs(['n']);
90+
CLI::setInputOutput($io);
91+
92+
command('logs:clear');
93+
94+
$this->assertFileExists(WRITEPATH . 'logs' . DIRECTORY_SEPARATOR . "log-{$this->date}.log");
95+
$this->assertSame(
96+
<<<'EOT'
97+
Deleting logs aborted.
98+
If you want, use the "--force" option to force delete all log files.
99+
100+
EOT,
101+
preg_replace('/\e\[[^m]+m/', '', $io->getOutput(2) . $io->getOutput(3)),
102+
);
103+
}
104+
105+
public function testClearLogsAbortsClearWithoutForceWithDefaultAnswer(): void
106+
{
107+
$this->assertFileExists(WRITEPATH . 'logs' . DIRECTORY_SEPARATOR . "log-{$this->date}.log");
108+
109+
$io = new MockInputOutput();
110+
$io->setInputs(['']);
111+
CLI::setInputOutput($io);
112+
113+
command('logs:clear');
114+
115+
$this->assertFileExists(WRITEPATH . 'logs' . DIRECTORY_SEPARATOR . "log-{$this->date}.log");
116+
$this->assertSame(
117+
<<<'EOT'
118+
Deleting logs aborted.
119+
If you want, use the "--force" option to force delete all log files.
120+
121+
EOT,
122+
preg_replace('/\e\[[^m]+m/', '', $io->getOutput(2) . $io->getOutput(3)),
123+
);
124+
}
125+
126+
public function testClearLogsWithoutForceButWithConfirmation(): void
127+
{
61128
$this->assertFileExists(WRITEPATH . 'logs' . DIRECTORY_SEPARATOR . "log-{$this->date}.log");
62129

63-
command('logs:clear -force');
64-
$result = $this->getStreamFilterBuffer();
130+
$io = new MockInputOutput();
131+
$io->setInputs(['y']);
132+
CLI::setInputOutput($io);
133+
134+
command('logs:clear');
65135

66136
$this->assertFileDoesNotExist(WRITEPATH . 'logs' . DIRECTORY_SEPARATOR . "log-{$this->date}.log");
67-
$this->assertFileExists(WRITEPATH . 'logs' . DIRECTORY_SEPARATOR . 'index.html');
68-
$this->assertStringContainsString('Logs cleared.', $result);
137+
$this->assertSame("Logs cleared.\n", preg_replace('/\e\[[^m]+m/', '', $io->getOutput(2)));
138+
}
139+
140+
#[RequiresOperatingSystem('Darwin|Linux')]
141+
public function testClearLogsFailsOnChmodFailure(): void
142+
{
143+
$path = WRITEPATH . 'logs' . DIRECTORY_SEPARATOR . "log-{$this->date}.log";
144+
file_put_contents($path, 'Lorem ipsum');
145+
146+
// Attempt to make the file itself undeletable by setting the
147+
// immutable/uchg flag on supported platforms.
148+
$immutableSet = false;
149+
if (str_starts_with(PHP_OS, 'Darwin')) {
150+
@exec(sprintf('chflags uchg %s', escapeshellarg($path)), $output, $rc);
151+
$immutableSet = $rc === 0;
152+
} else {
153+
// Try chattr on Linux with sudo (for containerized environments)
154+
@exec('which chattr', $whichOut, $whichRc);
155+
156+
if ($whichRc === 0) {
157+
@exec(sprintf('sudo chattr +i %s', escapeshellarg($path)), $output, $rc);
158+
$immutableSet = $rc === 0;
159+
}
160+
}
161+
162+
if (! $immutableSet) {
163+
$this->markTestSkipped('Cannot set file immutability in this environment');
164+
}
165+
166+
command('logs:clear --force');
167+
168+
// Restore attributes so other tests are not affected.
169+
if (str_starts_with(PHP_OS, 'Darwin')) {
170+
@exec(sprintf('chflags nouchg %s', escapeshellarg($path)));
171+
} else {
172+
@exec(sprintf('sudo chattr -i %s', escapeshellarg($path)));
173+
}
174+
175+
$this->assertFileExists($path);
176+
$this->assertSame("Error in deleting the logs files.\n", preg_replace('/\e\[[^m]+m/', '', $this->getStreamFilterBuffer()));
69177
}
70178
}

0 commit comments

Comments
 (0)