diff --git a/core/Command/Status.php b/core/Command/Status.php
index a00d4a94658fd..092fe00f81cb9 100644
--- a/core/Command/Status.php
+++ b/core/Command/Status.php
@@ -9,6 +9,7 @@
use OCP\Defaults;
use OCP\IConfig;
+use OCP\IDBConnection;
use OCP\ServerVersion;
use OCP\Util;
use Symfony\Component\Console\Input\InputInterface;
@@ -20,6 +21,7 @@ public function __construct(
private IConfig $config,
private Defaults $themingDefaults,
private ServerVersion $serverVersion,
+ private IDBConnection $connection,
) {
parent::__construct('status');
}
@@ -33,26 +35,56 @@ protected function configure() {
'exit-code',
'e',
InputOption::VALUE_NONE,
- 'exit with 0 if running in normal mode, 1 when in maintenance mode, 2 when `./occ upgrade` is needed. Does not write any output to STDOUT.'
+ 'exit with 0 if running in normal mode, 1 when in maintenance mode, 2 when `./occ upgrade` is needed, 3 when the database connection failed. Does not write any output to STDOUT.'
);
}
protected function execute(InputInterface $input, OutputInterface $output): int {
$maintenanceMode = $this->config->getSystemValueBool('maintenance', false);
$needUpgrade = Util::needUpgrade();
+ $installed = $this->config->getSystemValueBool('installed', false);
+
+ // Test database connection if Nextcloud is installed
+ $databaseStatus = 'not_configured';
+ $databaseError = null;
+ if ($installed) {
+ try {
+ $result = $this->connection->executeQuery('SELECT 1');
+ $result->closeCursor();
+ $databaseStatus = 'connected';
+ } catch (\Exception $e) {
+ $databaseStatus = 'connection_failed';
+ $databaseError = $e->getMessage();
+ }
+ }
+
$values = [
- 'installed' => $this->config->getSystemValueBool('installed', false),
+ 'installed' => $installed,
'version' => implode('.', $this->serverVersion->getVersion()),
'versionstring' => $this->serverVersion->getVersionString(),
'edition' => '',
'maintenance' => $maintenanceMode,
'needsDbUpgrade' => $needUpgrade,
'productname' => $this->themingDefaults->getProductName(),
- 'extendedSupport' => Util::hasExtendedSupport()
+ 'extendedSupport' => Util::hasExtendedSupport(),
+ 'database' => $databaseStatus
];
+
+ if ($databaseError) {
+ $values['database_error'] = $databaseError;
+ }
if ($input->getOption('verbose') || !$input->getOption('exit-code')) {
$this->writeArrayInOutputFormat($input, $output, $values);
+
+ // Add friendly status messages
+ if (!$installed) {
+ $output->writeln('Nextcloud is not installed yet');
+ } elseif ($maintenanceMode) {
+ $output->writeln('Nextcloud is in maintenance mode');
+ } elseif ($databaseStatus === 'connection_failed') {
+ $output->writeln('Database connection failed: ' . $databaseError . '');
+ }
}
if ($input->getOption('exit-code')) {
@@ -62,6 +94,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int
if ($needUpgrade === true) {
return 2;
}
+ if ($databaseStatus === 'connection_failed') {
+ return 3;
+ }
}
return 0;
}
diff --git a/tests/Core/Command/StatusTest.php b/tests/Core/Command/StatusTest.php
new file mode 100644
index 0000000000000..57444ee50308f
--- /dev/null
+++ b/tests/Core/Command/StatusTest.php
@@ -0,0 +1,316 @@
+config = $this->createMock(IConfig::class);
+ $this->themingDefaults = $this->createMock(Defaults::class);
+ $this->serverVersion = $this->createMock(ServerVersion::class);
+ $this->connection = $this->createMock(IDBConnection::class);
+
+ $command = new Status(
+ $this->config,
+ $this->themingDefaults,
+ $this->serverVersion,
+ $this->connection
+ );
+ $this->commandTester = new CommandTester($command);
+ }
+
+ public function testStatusWithNotInstalled(): void {
+ $this->config->expects($this->exactly(2))
+ ->method('getSystemValueBool')
+ ->willReturnMap([
+ ['maintenance', false, false],
+ ['installed', false, false],
+ ]);
+
+ $this->serverVersion->expects($this->once())
+ ->method('getVersion')
+ ->willReturn([30, 0, 0]);
+
+ $this->serverVersion->expects($this->once())
+ ->method('getVersionString')
+ ->willReturn('30.0.0.0');
+
+ $this->themingDefaults->expects($this->once())
+ ->method('getProductName')
+ ->willReturn('Nextcloud');
+
+ // Database connection should not be tested when not installed
+ $this->connection->expects($this->never())
+ ->method('executeQuery');
+
+ $this->commandTester->execute([]);
+
+ $output = $this->commandTester->getDisplay();
+ $this->assertStringContainsString('"installed":false', $output);
+ $this->assertStringContainsString('"database":"not_configured"', $output);
+ $this->assertStringContainsString('Nextcloud is not installed yet', $output);
+ $this->assertEquals(0, $this->commandTester->getStatusCode());
+ }
+
+ public function testStatusWithDatabaseConnectionSuccess(): void {
+ $this->config->expects($this->exactly(2))
+ ->method('getSystemValueBool')
+ ->willReturnMap([
+ ['maintenance', false, false],
+ ['installed', false, true],
+ ]);
+
+ $this->serverVersion->expects($this->once())
+ ->method('getVersion')
+ ->willReturn([30, 0, 0]);
+
+ $this->serverVersion->expects($this->once())
+ ->method('getVersionString')
+ ->willReturn('30.0.0.0');
+
+ $this->themingDefaults->expects($this->once())
+ ->method('getProductName')
+ ->willReturn('Nextcloud');
+
+ // Mock successful database connection
+ $this->connection->expects($this->once())
+ ->method('executeQuery')
+ ->with('SELECT 1')
+ ->willReturn(true);
+
+ $this->commandTester->execute([]);
+
+ $output = $this->commandTester->getDisplay();
+ $this->assertStringContainsString('"installed":true', $output);
+ $this->assertStringContainsString('"database":"connected"', $output);
+ $this->assertStringNotContainsString('database_error', $output);
+ $this->assertEquals(0, $this->commandTester->getStatusCode());
+ }
+
+ public function testStatusWithDatabaseConnectionFailure(): void {
+ $this->config->expects($this->exactly(2))
+ ->method('getSystemValueBool')
+ ->willReturnMap([
+ ['maintenance', false, false],
+ ['installed', false, true],
+ ]);
+
+ $this->serverVersion->expects($this->once())
+ ->method('getVersion')
+ ->willReturn([30, 0, 0]);
+
+ $this->serverVersion->expects($this->once())
+ ->method('getVersionString')
+ ->willReturn('30.0.0.0');
+
+ $this->themingDefaults->expects($this->once())
+ ->method('getProductName')
+ ->willReturn('Nextcloud');
+
+ // Mock failed database connection
+ $this->connection->expects($this->once())
+ ->method('executeQuery')
+ ->with('SELECT 1')
+ ->willThrowException(new \Exception('Connection failed'));
+
+ $this->commandTester->execute([]);
+
+ $output = $this->commandTester->getDisplay();
+ $this->assertStringContainsString('"installed":true', $output);
+ $this->assertStringContainsString('"database":"connection_failed"', $output);
+ $this->assertStringContainsString('"database_error":"Connection failed"', $output);
+ $this->assertStringContainsString('Database connection failed: Connection failed', $output);
+ $this->assertEquals(0, $this->commandTester->getStatusCode());
+ }
+
+ public function testStatusWithMaintenanceMode(): void {
+ $this->config->expects($this->exactly(2))
+ ->method('getSystemValueBool')
+ ->willReturnMap([
+ ['maintenance', false, true],
+ ['installed', false, true],
+ ]);
+
+ $this->serverVersion->expects($this->once())
+ ->method('getVersion')
+ ->willReturn([30, 0, 0]);
+
+ $this->serverVersion->expects($this->once())
+ ->method('getVersionString')
+ ->willReturn('30.0.0.0');
+
+ $this->themingDefaults->expects($this->once())
+ ->method('getProductName')
+ ->willReturn('Nextcloud');
+
+ // Mock successful database connection
+ $this->connection->expects($this->once())
+ ->method('executeQuery')
+ ->with('SELECT 1')
+ ->willReturn(true);
+
+ $this->commandTester->execute([]);
+
+ $output = $this->commandTester->getDisplay();
+ $this->assertStringContainsString('"maintenance":true', $output);
+ $this->assertStringContainsString('Nextcloud is in maintenance mode', $output);
+ $this->assertEquals(0, $this->commandTester->getStatusCode());
+ }
+
+ public function testStatusExitCodeNormal(): void {
+ $this->config->expects($this->exactly(2))
+ ->method('getSystemValueBool')
+ ->willReturnMap([
+ ['maintenance', false, false],
+ ['installed', false, true],
+ ]);
+
+ $this->serverVersion->expects($this->once())
+ ->method('getVersion')
+ ->willReturn([30, 0, 0]);
+
+ $this->serverVersion->expects($this->once())
+ ->method('getVersionString')
+ ->willReturn('30.0.0.0');
+
+ $this->themingDefaults->expects($this->once())
+ ->method('getProductName')
+ ->willReturn('Nextcloud');
+
+ // Mock successful database connection
+ $this->connection->expects($this->once())
+ ->method('executeQuery')
+ ->with('SELECT 1')
+ ->willReturn(true);
+
+ $this->commandTester->execute(['--exit-code' => true]);
+
+ $this->assertEquals(0, $this->commandTester->getStatusCode());
+ $this->assertEmpty($this->commandTester->getDisplay()); // No output in exit-code mode
+ }
+
+ public function testStatusExitCodeMaintenanceMode(): void {
+ $this->config->expects($this->exactly(2))
+ ->method('getSystemValueBool')
+ ->willReturnMap([
+ ['maintenance', false, true],
+ ['installed', false, true],
+ ]);
+
+ $this->serverVersion->expects($this->once())
+ ->method('getVersion')
+ ->willReturn([30, 0, 0]);
+
+ $this->serverVersion->expects($this->once())
+ ->method('getVersionString')
+ ->willReturn('30.0.0.0');
+
+ $this->themingDefaults->expects($this->once())
+ ->method('getProductName')
+ ->willReturn('Nextcloud');
+
+ // Mock successful database connection
+ $this->connection->expects($this->once())
+ ->method('executeQuery')
+ ->with('SELECT 1')
+ ->willReturn(true);
+
+ $this->commandTester->execute(['--exit-code' => true]);
+
+ $this->assertEquals(1, $this->commandTester->getStatusCode());
+ $this->assertEmpty($this->commandTester->getDisplay()); // No output in exit-code mode
+ }
+
+ public function testStatusExitCodeDatabaseConnectionFailure(): void {
+ $this->config->expects($this->exactly(2))
+ ->method('getSystemValueBool')
+ ->willReturnMap([
+ ['maintenance', false, false],
+ ['installed', false, true],
+ ]);
+
+ $this->serverVersion->expects($this->once())
+ ->method('getVersion')
+ ->willReturn([30, 0, 0]);
+
+ $this->serverVersion->expects($this->once())
+ ->method('getVersionString')
+ ->willReturn('30.0.0.0');
+
+ $this->themingDefaults->expects($this->once())
+ ->method('getProductName')
+ ->willReturn('Nextcloud');
+
+ // Mock failed database connection
+ $this->connection->expects($this->once())
+ ->method('executeQuery')
+ ->with('SELECT 1')
+ ->willThrowException(new \Exception('Connection failed'));
+
+ $this->commandTester->execute(['--exit-code' => true]);
+
+ $this->assertEquals(3, $this->commandTester->getStatusCode()); // New exit code for DB failure
+ $this->assertEmpty($this->commandTester->getDisplay()); // No output in exit-code mode
+ }
+
+ public function testStatusOutputFormat(): void {
+ $this->config->expects($this->exactly(2))
+ ->method('getSystemValueBool')
+ ->willReturnMap([
+ ['maintenance', false, false],
+ ['installed', false, true],
+ ]);
+
+ $this->serverVersion->expects($this->once())
+ ->method('getVersion')
+ ->willReturn([30, 0, 1]);
+
+ $this->serverVersion->expects($this->once())
+ ->method('getVersionString')
+ ->willReturn('30.0.1.2');
+
+ $this->themingDefaults->expects($this->once())
+ ->method('getProductName')
+ ->willReturn('Nextcloud Test');
+
+ // Mock successful database connection
+ $this->connection->expects($this->once())
+ ->method('executeQuery')
+ ->with('SELECT 1')
+ ->willReturn(true);
+
+ $this->commandTester->execute([]);
+
+ $output = $this->commandTester->getDisplay();
+ $this->assertStringContainsString('"installed":true', $output);
+ $this->assertStringContainsString('"version":"30.0.1"', $output);
+ $this->assertStringContainsString('"versionstring":"30.0.1.2"', $output);
+ $this->assertStringContainsString('"edition":""', $output);
+ $this->assertStringContainsString('"maintenance":false', $output);
+ $this->assertStringContainsString('"productname":"Nextcloud Test"', $output);
+ $this->assertStringContainsString('"database":"connected"', $output);
+ }
+}
\ No newline at end of file