create(['is_admin' => true]); $user = User::factory()->create(); $product = $this->createHostingProduct(); $account = HostingAccount::create([ 'user_id' => $user->id, 'hosting_product_id' => $product->id, 'username' => 'unsuspendacct', 'type' => HostingAccount::TYPE_SHARED, 'status' => HostingAccount::STATUS_SUSPENDED, 'resource_status' => HostingAccount::RESOURCE_STATUS_SUSPENDED, 'suspension_reason' => 'Manual suspension.', 'suspended_at' => now()->subHour(), 'metadata' => [ 'suspension_origin' => 'manual', ], ]); $order = CustomerHostingOrder::create([ 'user_id' => $user->id, 'hosting_product_id' => $product->id, 'hosting_account_id' => $account->id, 'domain_name' => 'example.test', 'billing_cycle' => CustomerHostingOrder::CYCLE_MONTHLY, 'amount_paid' => 10, 'currency' => 'GHS', 'status' => CustomerHostingOrder::STATUS_SUSPENDED, 'suspended_at' => now()->subHour(), ]); $provider = \Mockery::mock(SharedNodeProvider::class); $provider->shouldReceive('unsuspendAccount')->once()->withArgs( fn (HostingAccount $candidate): bool => $candidate->id === $account->id )->andReturn(true); $provider->shouldReceive('applyAccountResourceProfile')->once()->withArgs( fn (HostingAccount $candidate, bool $throttled): bool => $candidate->id === $account->id && $throttled === false )->andReturn(true); $this->app->instance(SharedNodeProvider::class, $provider); $response = $this->actingAs($admin)->post(route('admin.users.hosting-accounts.unsuspend', [$user, $account])); $response->assertRedirect(); $response->assertSessionHas('success', 'Hosting account unsuspended.'); $account->refresh(); $order->refresh(); $this->assertSame(HostingAccount::STATUS_ACTIVE, $account->status); $this->assertSame(HostingAccount::RESOURCE_STATUS_ACTIVE, $account->resource_status); $this->assertNull($account->suspended_at); $this->assertNull($account->suspension_reason); $this->assertSame(CustomerHostingOrder::STATUS_ACTIVE, $order->status); $this->assertNull($order->suspended_at); } public function test_admin_order_unsuspend_works_when_account_is_suspended_but_order_status_drifted(): void { $admin = User::factory()->create(['is_admin' => true]); $user = User::factory()->create(); $product = $this->createHostingProduct(); $account = HostingAccount::create([ 'user_id' => $user->id, 'hosting_product_id' => $product->id, 'username' => 'driftacct', 'type' => HostingAccount::TYPE_SHARED, 'status' => HostingAccount::STATUS_SUSPENDED, 'resource_status' => HostingAccount::RESOURCE_STATUS_SUSPENDED, 'suspension_reason' => 'CPU overage.', 'suspended_at' => now()->subMinutes(30), 'metadata' => [ 'suspension_origin' => 'automatic', ], ]); $order = CustomerHostingOrder::create([ 'user_id' => $user->id, 'hosting_product_id' => $product->id, 'hosting_account_id' => $account->id, 'domain_name' => 'drift.example.test', 'billing_cycle' => CustomerHostingOrder::CYCLE_MONTHLY, 'amount_paid' => 10, 'currency' => 'GHS', 'status' => CustomerHostingOrder::STATUS_ACTIVE, ]); $provider = \Mockery::mock(SharedNodeProvider::class); $provider->shouldReceive('unsuspendAccount')->once()->withArgs( fn (HostingAccount $candidate): bool => $candidate->id === $account->id )->andReturn(true); $provider->shouldReceive('applyAccountResourceProfile')->once()->withArgs( fn (HostingAccount $candidate, bool $throttled): bool => $candidate->id === $account->id && $throttled === false )->andReturn(true); $this->app->instance(SharedNodeProvider::class, $provider); $response = $this->actingAs($admin)->post(route('admin.hosting.orders.unsuspend', $order)); $response->assertRedirect(); $response->assertSessionHas('success', 'Order unsuspended.'); $account->refresh(); $order->refresh(); $this->assertSame(HostingAccount::STATUS_ACTIVE, $account->status); $this->assertSame(HostingAccount::RESOURCE_STATUS_ACTIVE, $account->resource_status); $this->assertSame(CustomerHostingOrder::STATUS_ACTIVE, $order->status); } private function createHostingProduct(): HostingProduct { return HostingProduct::create([ 'name' => 'Admin Unsuspend Test', 'slug' => 'admin-unsuspend-test-' . str()->random(6), 'category' => HostingProduct::CATEGORY_SHARED, 'type' => HostingProduct::TYPE_SINGLE_DOMAIN, 'price_monthly' => 10, 'currency' => 'GHS', 'disk_gb' => 10, 'max_domains' => 1, 'is_active' => true, 'is_visible' => true, ]); } }