diff --git a/src/app/Console/Commands/Domain/CreateCommand.php b/src/app/Console/Commands/Domain/CreateCommand.php
--- a/src/app/Console/Commands/Domain/CreateCommand.php
+++ b/src/app/Console/Commands/Domain/CreateCommand.php
@@ -38,7 +38,7 @@
         }
 
         if ($domain) {
-            if ($domain->deleted_at) {
+            if ($domain->trashed()) {
                 // set the status back to new
                 $domain->status = \App\Domain::STATUS_NEW;
                 $domain->save();
diff --git a/src/app/Console/Commands/Domain/DeleteCommand.php b/src/app/Console/Commands/Domain/DeleteCommand.php
--- a/src/app/Console/Commands/Domain/DeleteCommand.php
+++ b/src/app/Console/Commands/Domain/DeleteCommand.php
@@ -15,12 +15,10 @@
 
     public function handle()
     {
-        $argument = $this->argument('domain');
-
-        $domain = $this->getDomain($argument);
+        $domain = $this->getDomain($this->argument('domain'));
 
         if (!$domain) {
-            $this->error("No such domain {$argument}");
+            $this->error("Domain not found.");
             return 1;
         }
 
diff --git a/src/app/Console/Commands/DomainRestore.php b/src/app/Console/Commands/Domain/RestoreCommand.php
rename from src/app/Console/Commands/DomainRestore.php
rename to src/app/Console/Commands/Domain/RestoreCommand.php
--- a/src/app/Console/Commands/DomainRestore.php
+++ b/src/app/Console/Commands/Domain/RestoreCommand.php
@@ -1,11 +1,11 @@
 <?php
 
-namespace App\Console\Commands;
+namespace App\Console\Commands\Domain;
 
 use App\Console\Command;
 use Illuminate\Support\Facades\DB;
 
-class DomainRestore extends Command
+class RestoreCommand extends Command
 {
     /**
      * The name and signature of the console command.
diff --git a/src/app/Console/Commands/DomainSetStatus.php b/src/app/Console/Commands/Domain/SetStatusCommand.php
rename from src/app/Console/Commands/DomainSetStatus.php
rename to src/app/Console/Commands/Domain/SetStatusCommand.php
--- a/src/app/Console/Commands/DomainSetStatus.php
+++ b/src/app/Console/Commands/Domain/SetStatusCommand.php
@@ -1,12 +1,11 @@
 <?php
 
-namespace App\Console\Commands;
+namespace App\Console\Commands\Domain;
 
 use App\Console\Command;
-use App\Domain;
 use Illuminate\Support\Facades\Queue;
 
-class DomainSetStatus extends Command
+class SetStatusCommand extends Command
 {
     /**
      * The name and signature of the console command.
@@ -32,6 +31,7 @@
         $domain = $this->getDomain($this->argument('domain'));
 
         if (!$domain) {
+            $this->error("Domain not found.");
             return 1;
         }
 
diff --git a/src/app/Console/Commands/DomainSetWallet.php b/src/app/Console/Commands/Domain/SetWalletCommand.php
rename from src/app/Console/Commands/DomainSetWallet.php
rename to src/app/Console/Commands/Domain/SetWalletCommand.php
--- a/src/app/Console/Commands/DomainSetWallet.php
+++ b/src/app/Console/Commands/Domain/SetWalletCommand.php
@@ -1,15 +1,14 @@
 <?php
 
-namespace App\Console\Commands;
+namespace App\Console\Commands\Domain;
 
 use App\Console\Command;
 use App\Entitlement;
 use App\Domain;
 use App\Sku;
-use App\Wallet;
 use Illuminate\Support\Facades\Queue;
 
-class DomainSetWallet extends Command
+class SetWalletCommand extends Command
 {
     /**
      * The name and signature of the console command.
@@ -51,7 +50,7 @@
             return 1;
         }
 
-        $sku = Sku::where('title', 'domain-hosting')->first();
+        $sku = Sku::withObjectTenantContext($domain)->where('title', 'domain-hosting')->first();
 
         Queue::fake(); // ignore LDAP for now (note: adding entitlements updates the domain)
 
diff --git a/src/app/Console/Commands/DomainStatus.php b/src/app/Console/Commands/Domain/StatusCommand.php
rename from src/app/Console/Commands/DomainStatus.php
rename to src/app/Console/Commands/Domain/StatusCommand.php
--- a/src/app/Console/Commands/DomainStatus.php
+++ b/src/app/Console/Commands/Domain/StatusCommand.php
@@ -1,11 +1,11 @@
 <?php
 
-namespace App\Console\Commands;
+namespace App\Console\Commands\Domain;
 
 use App\Console\Command;
 use App\Domain;
 
-class DomainStatus extends Command
+class StatusCommand extends Command
 {
     /**
      * The name and signature of the console command.
@@ -19,7 +19,7 @@
      *
      * @var string
      */
-    protected $description = 'Display the status of a domain';
+    protected $description = "Display the status of a domain";
 
     /**
      * Execute the console command.
@@ -31,6 +31,7 @@
         $domain = $this->getDomain($this->argument('domain'));
 
         if (!$domain) {
+            $this->error("Domain not found.");
             return 1;
         }
 
diff --git a/src/app/Console/Commands/DomainSuspend.php b/src/app/Console/Commands/Domain/SuspendCommand.php
rename from src/app/Console/Commands/DomainSuspend.php
rename to src/app/Console/Commands/Domain/SuspendCommand.php
--- a/src/app/Console/Commands/DomainSuspend.php
+++ b/src/app/Console/Commands/Domain/SuspendCommand.php
@@ -1,11 +1,10 @@
 <?php
 
-namespace App\Console\Commands;
+namespace App\Console\Commands\Domain;
 
 use App\Console\Command;
-use App\Domain;
 
-class DomainSuspend extends Command
+class SuspendCommand extends Command
 {
     /**
      * The name and signature of the console command.
@@ -31,10 +30,11 @@
         $domain = $this->getDomain($this->argument('domain'));
 
         if (!$domain) {
+            $this->error("Domain not found.");
             return 1;
         }
 
-        $this->info("Found domain: {$domain->id}");
+        $this->info("Found domain {$domain->id}");
 
         $domain->suspend();
     }
diff --git a/src/app/Console/Commands/DomainUnsuspend.php b/src/app/Console/Commands/Domain/UnsuspendCommand.php
rename from src/app/Console/Commands/DomainUnsuspend.php
rename to src/app/Console/Commands/Domain/UnsuspendCommand.php
--- a/src/app/Console/Commands/DomainUnsuspend.php
+++ b/src/app/Console/Commands/Domain/UnsuspendCommand.php
@@ -1,11 +1,10 @@
 <?php
 
-namespace App\Console\Commands;
+namespace App\Console\Commands\Domain;
 
 use App\Console\Command;
-use App\Domain;
 
-class DomainUnsuspend extends Command
+class UnsuspendCommand extends Command
 {
     /**
      * The name and signature of the console command.
@@ -31,6 +30,7 @@
         $domain = $this->getDomain($this->argument('domain'));
 
         if (!$domain) {
+            $this->error("Domain not found.");
             return 1;
         }
 
diff --git a/src/app/Console/Commands/DomainList.php b/src/app/Console/Commands/DomainList.php
deleted file mode 100644
--- a/src/app/Console/Commands/DomainList.php
+++ /dev/null
@@ -1,49 +0,0 @@
-<?php
-
-namespace App\Console\Commands;
-
-use App\Console\Command;
-use App\Domain;
-
-class DomainList extends Command
-{
-    /**
-     * The name and signature of the console command.
-     *
-     * @var string
-     */
-    protected $signature = 'domain:list {--deleted}';
-
-    /**
-     * The console command description.
-     *
-     * @var string
-     */
-    protected $description = 'List domains';
-
-    /**
-     * Execute the console command.
-     *
-     * @return mixed
-     */
-    public function handle()
-    {
-        if ($this->option('deleted')) {
-            $domains = Domain::withTrashed()->orderBy('namespace');
-        } else {
-            $domains = Domain::orderBy('namespace');
-        }
-
-        $domains->withEnvTenantContext()->each(
-            function ($domain) {
-                $msg = $domain->namespace;
-
-                if ($domain->deleted_at) {
-                    $msg .= " (deleted at {$domain->deleted_at})";
-                }
-
-                $this->info($msg);
-            }
-        );
-    }
-}
diff --git a/src/app/Console/Kernel.php b/src/app/Console/Kernel.php
--- a/src/app/Console/Kernel.php
+++ b/src/app/Console/Kernel.php
@@ -48,7 +48,7 @@
     {
         $this->load(__DIR__ . '/Commands');
 
-        if (\app('env') != 'production') {
+        if (\app('env') == 'local') {
             $this->load(__DIR__ . '/Development');
         }
 
diff --git a/src/tests/Feature/Console/Domain/CreateTest.php b/src/tests/Feature/Console/Domain/CreateTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Feature/Console/Domain/CreateTest.php
@@ -0,0 +1,86 @@
+<?php
+
+namespace Tests\Feature\Console\Domain;
+
+use Illuminate\Support\Facades\Queue;
+use Tests\TestCase;
+
+class CreateTest extends TestCase
+{
+    /**
+     * {@inheritDoc}
+     */
+    public function setUp(): void
+    {
+        parent::setUp();
+
+        $this->deleteTestDomain('domain-delete.com');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function tearDown(): void
+    {
+        $this->deleteTestDomain('domain-delete.com');
+
+        parent::tearDown();
+    }
+
+    /**
+     * Test the command
+     */
+    public function testHandle(): void
+    {
+        Queue::fake();
+
+        // Existing domain
+        $ns = \config('app.domain');
+        $code = \Artisan::call("domain:create $ns");
+        $output = trim(\Artisan::output());
+        $this->assertSame(1, $code);
+        $this->assertSame("Domain $ns already exists.", $output);
+
+        // Existing domain (with --force param)
+        $code = \Artisan::call("domain:create $ns --force");
+        $output = trim(\Artisan::output());
+        $this->assertSame(1, $code);
+        $this->assertSame("Domain $ns not marked as deleted... examine more closely", $output);
+
+        // A new domain
+        $code = \Artisan::call("domain:create domain-delete.com");
+        $output = trim(\Artisan::output());
+        $this->assertSame(0, $code);
+
+        $domain = \App\Domain::where('namespace', 'domain-delete.com')->first();
+
+        $this->assertSame("Domain domain-delete.com created with ID {$domain->id}. "
+            . "Remember to assign it to a wallet with 'domain:set-wallet'", $output);
+
+        $this->assertTrue($domain->isExternal());
+        $this->assertNull($domain->wallet());
+        $this->assertSame('domain-delete.com', $domain->namespace);
+
+        $domain->status |= \App\Domain::STATUS_ACTIVE;
+        $domain->save();
+        $domain->delete();
+
+        $domain->refresh();
+
+        $this->assertTrue($domain->trashed());
+        $this->assertTrue($domain->isActive());
+
+        // Deleted domain
+        $code = \Artisan::call("domain:create domain-delete.com --force");
+        $output = trim(\Artisan::output());
+        $this->assertSame(0, $code);
+        $this->assertSame("Domain domain-delete.com with ID {$domain->id} revived. "
+            . "Remember to assign it to a wallet with 'domain:set-wallet'", $output);
+
+        $domain->refresh();
+
+        $this->assertTrue($domain->isNew());
+        $this->assertFalse($domain->isActive());
+        $this->assertFalse($domain->trashed());
+    }
+}
diff --git a/src/tests/Feature/Console/DomainRestoreTest.php b/src/tests/Feature/Console/Domain/DeleteTest.php
rename from src/tests/Feature/Console/DomainRestoreTest.php
rename to src/tests/Feature/Console/Domain/DeleteTest.php
--- a/src/tests/Feature/Console/DomainRestoreTest.php
+++ b/src/tests/Feature/Console/Domain/DeleteTest.php
@@ -1,11 +1,11 @@
 <?php
 
-namespace Tests\Feature\Console;
+namespace Tests\Feature\Console\Domain;
 
 use Illuminate\Support\Facades\Queue;
 use Tests\TestCase;
 
-class DomainRestoreTest extends TestCase
+class DeleteTest extends TestCase
 {
     /**
      * {@inheritDoc}
@@ -14,8 +14,8 @@
     {
         parent::setUp();
 
-        $this->deleteTestUser('user@force-delete.com');
-        $this->deleteTestDomain('force-delete.com');
+        $this->deleteTestUser('user@domain-delete.com');
+        $this->deleteTestDomain('domain-delete.com');
     }
 
     /**
@@ -23,8 +23,8 @@
      */
     public function tearDown(): void
     {
-        $this->deleteTestUser('user@force-delete.com');
-        $this->deleteTestDomain('force-delete.com');
+        $this->deleteTestUser('user@domain-delete.com');
+        $this->deleteTestDomain('domain-delete.com');
 
         parent::tearDown();
     }
@@ -37,14 +37,20 @@
         Queue::fake();
 
         // Non-existing domain
-        $code = \Artisan::call("domain:restore unknown.org");
+        $code = \Artisan::call("domain:delete unknown.org");
         $output = trim(\Artisan::output());
         $this->assertSame(1, $code);
         $this->assertSame("Domain not found.", $output);
 
+        // Public domain
+        $code = \Artisan::call("domain:delete " . \config('app.domain'));
+        $output = trim(\Artisan::output());
+        $this->assertSame(1, $code);
+        $this->assertSame("This domain is a public registration domain.", $output);
+
         // Create a user account for delete
-        $user = $this->getTestUser('user@force-delete.com');
-        $domain = $this->getTestDomain('force-delete.com', [
+        $user = $this->getTestUser('user@domain-delete.com');
+        $domain = $this->getTestDomain('domain-delete.com', [
                 'status' => \App\Domain::STATUS_NEW,
                 'type' => \App\Domain::TYPE_HOSTED,
         ]);
@@ -52,40 +58,20 @@
         $package_domain = \App\Package::where('title', 'domain-hosting')->first();
         $user->assignPackage($package_kolab);
         $domain->assignPackage($package_domain, $user);
-        $wallet = $user->wallets()->first();
-        $entitlements = $wallet->entitlements->pluck('id')->all();
-
-        $this->assertCount(8, $entitlements);
 
         // Non-deleted domain
-        $code = \Artisan::call("domain:restore force-delete.com");
-        $output = trim(\Artisan::output());
-        $this->assertSame(1, $code);
-        $this->assertSame("The domain is not yet deleted.", $output);
-
-        $domain->delete();
-
-        $this->assertTrue($domain->fresh()->trashed());
-
-        // Deleted domain
-        $code = \Artisan::call("domain:restore force-delete.com");
+        $code = \Artisan::call("domain:delete domain-delete.com");
         $output = trim(\Artisan::output());
         $this->assertSame(0, $code);
         $this->assertSame("", $output);
 
-        $this->assertFalse($domain->fresh()->trashed());
-
-        $user->delete();
-
         $this->assertTrue($domain->fresh()->trashed());
-        $this->assertTrue($user->fresh()->trashed());
+        $this->assertFalse($user->fresh()->trashed());
 
-        // Deleted domain, deleted owner
-        $code = \Artisan::call("domain:restore force-delete.com");
+        // Deleted domain
+        $code = \Artisan::call("domain:delete domain-delete.com");
         $output = trim(\Artisan::output());
         $this->assertSame(1, $code);
-        $this->assertSame("The domain owner is deleted.", $output);
-
-        $this->assertTrue($domain->fresh()->trashed());
+        $this->assertSame("Domain not found.", $output);
     }
 }
diff --git a/src/tests/Feature/Console/DomainRestoreTest.php b/src/tests/Feature/Console/Domain/RestoreTest.php
rename from src/tests/Feature/Console/DomainRestoreTest.php
rename to src/tests/Feature/Console/Domain/RestoreTest.php
--- a/src/tests/Feature/Console/DomainRestoreTest.php
+++ b/src/tests/Feature/Console/Domain/RestoreTest.php
@@ -1,11 +1,11 @@
 <?php
 
-namespace Tests\Feature\Console;
+namespace Tests\Feature\Console\Domain;
 
 use Illuminate\Support\Facades\Queue;
 use Tests\TestCase;
 
-class DomainRestoreTest extends TestCase
+class RestoreTest extends TestCase
 {
     /**
      * {@inheritDoc}
diff --git a/src/tests/Feature/Console/Domain/SetStatusTest.php b/src/tests/Feature/Console/Domain/SetStatusTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Feature/Console/Domain/SetStatusTest.php
@@ -0,0 +1,57 @@
+<?php
+
+namespace Tests\Feature\Console\Domain;
+
+use Illuminate\Support\Facades\Queue;
+use Tests\TestCase;
+
+class SetStatusTest extends TestCase
+{
+    /**
+     * {@inheritDoc}
+     */
+    public function setUp(): void
+    {
+        parent::setUp();
+
+        $this->deleteTestDomain('domain-delete.com');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function tearDown(): void
+    {
+        $this->deleteTestDomain('domain-delete.com');
+
+        parent::tearDown();
+    }
+
+    /**
+     * Test the command
+     */
+    public function testHandle(): void
+    {
+        Queue::fake();
+
+        // Non-existing domain
+        $code = \Artisan::call("domain:set-status unknown.org 1");
+        $output = trim(\Artisan::output());
+        $this->assertSame(1, $code);
+        $this->assertSame("Domain not found.", $output);
+
+        $domain = $this->getTestDomain('domain-delete.com', [
+                'status' => \App\Domain::STATUS_NEW,
+                'type' => \App\Domain::TYPE_HOSTED,
+        ]);
+
+        $code = \Artisan::call("domain:set-status domain-delete.com " . \App\Domain::STATUS_LDAP_READY);
+        $output = trim(\Artisan::output());
+        $this->assertSame(0, $code);
+        $this->assertSame((string) \App\Domain::STATUS_LDAP_READY, $output);
+
+        $domain->refresh();
+
+        $this->assertSame(\App\Domain::STATUS_LDAP_READY, $domain->status);
+    }
+}
diff --git a/src/tests/Feature/Console/Domain/SetWalletTest.php b/src/tests/Feature/Console/Domain/SetWalletTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Feature/Console/Domain/SetWalletTest.php
@@ -0,0 +1,75 @@
+<?php
+
+namespace Tests\Feature\Console\Domain;
+
+use Illuminate\Support\Facades\Queue;
+use Tests\TestCase;
+
+class SetWalletTest extends TestCase
+{
+    /**
+     * {@inheritDoc}
+     */
+    public function setUp(): void
+    {
+        parent::setUp();
+
+        $this->deleteTestDomain('domain-delete.com');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function tearDown(): void
+    {
+        $this->deleteTestDomain('domain-delete.com');
+
+        parent::tearDown();
+    }
+
+    /**
+     * Test the command
+     */
+    public function testHandle(): void
+    {
+        Queue::fake();
+
+        // Non-existing domain
+        $code = \Artisan::call("domain:set-wallet unknown.org 12345");
+        $output = trim(\Artisan::output());
+        $this->assertSame(1, $code);
+        $this->assertSame("Domain not found.", $output);
+
+        $domain = $this->getTestDomain('domain-delete.com', [
+                'status' => \App\Domain::STATUS_NEW,
+                'type' => \App\Domain::TYPE_HOSTED,
+        ]);
+
+        // Non-existing wallet
+        $code = \Artisan::call("domain:set-wallet domain-delete.com 12345");
+        $output = trim(\Artisan::output());
+        $this->assertSame(1, $code);
+        $this->assertSame("Wallet not found.", $output);
+
+        $john = $this->getTestUser('john@kolab.org');
+        $wallet = $john->wallets->first();
+
+        $code = \Artisan::call("domain:set-wallet domain-delete.com " . $wallet->id);
+        $output = trim(\Artisan::output());
+        $this->assertSame(0, $code);
+        $this->assertSame('', $output);
+
+        $domain->refresh();
+        $sku = \App\Sku::withObjectTenantContext($domain)
+            ->where('title', 'domain-hosting')->first();
+
+        $this->assertSame($sku->id, $domain->entitlement->sku_id);
+        $this->assertSame($wallet->id, $domain->entitlement->wallet_id);
+
+        // Already assigned to a wallet
+        $code = \Artisan::call("domain:set-wallet domain-delete.com " . $wallet->id);
+        $output = trim(\Artisan::output());
+        $this->assertSame(1, $code);
+        $this->assertSame("Domain already assigned to a wallet: {$wallet->id}.", $output);
+    }
+}
diff --git a/src/tests/Feature/Console/Domain/StatusTest.php b/src/tests/Feature/Console/Domain/StatusTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Feature/Console/Domain/StatusTest.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace Tests\Feature\Console\Domain;
+
+use Tests\TestCase;
+
+class StatusTest extends TestCase
+{
+    /**
+     * Test the command
+     */
+    public function testHandle(): void
+    {
+        // Non-existing domain
+        $code = \Artisan::call("domain:status unknown.org");
+        $output = trim(\Artisan::output());
+        $this->assertSame(1, $code);
+        $this->assertSame("Domain not found.", $output);
+
+
+        // Existing domain
+        $code = \Artisan::call("domain:status kolab.org");
+        $output = trim(\Artisan::output());
+        $this->assertSame(0, $code);
+
+        $this->assertTrue(strpos($output, "In total: 114") !== false);
+
+        // TODO: More precise output testing
+    }
+}
diff --git a/src/tests/Feature/Console/Domain/SuspendTest.php b/src/tests/Feature/Console/Domain/SuspendTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Feature/Console/Domain/SuspendTest.php
@@ -0,0 +1,55 @@
+<?php
+
+namespace Tests\Feature\Console\Domain;
+
+use Illuminate\Support\Facades\Queue;
+use Tests\TestCase;
+
+class SuspendTest extends TestCase
+{
+    /**
+     * {@inheritDoc}
+     */
+    public function setUp(): void
+    {
+        parent::setUp();
+
+        $this->deleteTestDomain('domain-delete.com');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function tearDown(): void
+    {
+        $this->deleteTestDomain('domain-delete.com');
+
+        parent::tearDown();
+    }
+
+    /**
+     * Test the command
+     */
+    public function testHandle(): void
+    {
+        Queue::fake();
+
+        // Non-existing domain
+        $code = \Artisan::call("domain:suspend unknown.org");
+        $output = trim(\Artisan::output());
+        $this->assertSame(1, $code);
+        $this->assertSame("Domain not found.", $output);
+
+        $domain = $this->getTestDomain('domain-delete.com', [
+                'status' => \App\Domain::STATUS_NEW,
+                'type' => \App\Domain::TYPE_HOSTED,
+        ]);
+
+        $code = \Artisan::call("domain:suspend {$domain->namespace}");
+        $output = trim(\Artisan::output());
+        $this->assertSame(0, $code);
+        $this->assertSame("Found domain {$domain->id}", $output);
+
+        $this->assertTrue($domain->fresh()->isSuspended());
+    }
+}
diff --git a/src/tests/Feature/Console/Domain/UnsuspendTest.php b/src/tests/Feature/Console/Domain/UnsuspendTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Feature/Console/Domain/UnsuspendTest.php
@@ -0,0 +1,57 @@
+<?php
+
+namespace Tests\Feature\Console\Domain;
+
+use Illuminate\Support\Facades\Queue;
+use Tests\TestCase;
+
+class UnsuspendTest extends TestCase
+{
+    /**
+     * {@inheritDoc}
+     */
+    public function setUp(): void
+    {
+        parent::setUp();
+
+        $this->deleteTestDomain('domain-delete.com');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function tearDown(): void
+    {
+        $this->deleteTestDomain('domain-delete.com');
+
+        parent::tearDown();
+    }
+
+    /**
+     * Test the command
+     */
+    public function testHandle(): void
+    {
+        Queue::fake();
+
+        // Non-existing domain
+        $code = \Artisan::call("domain:unsuspend unknown.org");
+        $output = trim(\Artisan::output());
+        $this->assertSame(1, $code);
+        $this->assertSame("Domain not found.", $output);
+
+        $domain = $this->getTestDomain('domain-delete.com', [
+                'status' => \App\Domain::STATUS_NEW | \App\Domain::STATUS_SUSPENDED,
+                'type' => \App\Domain::TYPE_HOSTED,
+        ]);
+
+        $this->assertTrue($domain->isSuspended());
+
+        $code = \Artisan::call("domain:unsuspend {$domain->namespace}");
+        $output = trim(\Artisan::output());
+        $this->assertSame(0, $code);
+        $this->assertSame("Found domain {$domain->id}", $output);
+
+        $this->assertFalse($domain->fresh()->isSuspended());
+    }
+}
diff --git a/src/tests/Feature/Console/Domain/UsersTest.php b/src/tests/Feature/Console/Domain/UsersTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Feature/Console/Domain/UsersTest.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace Tests\Feature\Console\Domain;
+
+use Tests\TestCase;
+
+class UsersTest extends TestCase
+{
+    /**
+     * Test the command
+     */
+    public function testHandle(): void
+    {
+        // Existing domain
+        $code = \Artisan::call("domain:users kolab.org");
+        $output = trim(\Artisan::output());
+        $this->assertSame(0, $code);
+
+        $john = \App\User::where('email', 'john@kolab.org')->first();
+
+        $this->assertTrue(strpos($output, (string) $john->id) !== false);
+
+        // TODO: Test output format and additional attributes
+    }
+}
diff --git a/src/tests/Feature/Console/DomainsTest.php b/src/tests/Feature/Console/DomainsTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Feature/Console/DomainsTest.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace Tests\Feature\Console\Domain;
+
+use Tests\TestCase;
+
+class DomainsTest extends TestCase
+{
+    /**
+     * Test the command
+     */
+    public function testHandle(): void
+    {
+        $domain = \App\Domain::where('namespace', 'kolab.org')->first();
+
+        // Existing domain
+        $code = \Artisan::call("domains");
+        $output = trim(\Artisan::output());
+        $this->assertSame(0, $code);
+
+        $this->assertTrue(strpos($output, (string) $domain->id) !== false);
+
+        // TODO: Test --deleted argument
+        // TODO: Test output format and other attributes
+        // TODO: Test tenant context
+    }
+}