CluPilotCloud/tests/Feature/DashboardTest.php

122 lines
5.0 KiB
PHP

<?php
use App\Livewire\Dashboard;
use App\Models\Customer;
use App\Models\Instance;
use App\Models\Order;
use App\Models\User;
use Illuminate\Support\Facades\DB;
it('redirects guests to the login page', function () {
$this->get('/dashboard')->assertRedirect('/login');
});
it('shows the dashboard to authenticated users', function () {
$user = User::factory()->create();
$this->actingAs($user)->get('/dashboard')
->assertOk()
->assertSee('CluPilot')
->assertSee(__('dashboard.title'));
});
it('shows no next invoice once the customer has given notice', function () {
// Cancelling leaves the subscription active until the term runs out, so
// `current_period_end` becomes the day service ENDS rather than the day the
// next charge falls. Billing a renewal that will never happen, on the sheet
// a customer checks their invoices against, is the worst line this page
// could carry.
$user = App\Models\User::factory()->create();
$customer = App\Models\Customer::factory()->create(['user_id' => $user->id]);
$instance = App\Models\Instance::factory()->create([
'customer_id' => $customer->id,
'status' => 'cancellation_scheduled',
'cancel_requested_at' => now(),
'service_ends_at' => now()->addMonth(),
]);
App\Models\Subscription::factory()->create([
'customer_id' => $customer->id,
'instance_id' => $instance->id,
'status' => 'active',
'price_cents' => 17900,
'currency' => 'EUR',
'term' => 'monthly',
'current_period_end' => now()->addMonth(),
]);
Livewire::actingAs($user)
->test(App\Livewire\Dashboard::class)
->assertViewHas('nextInvoice', null)
// The price is still on the master record — they pay it until the end.
->assertViewHas('planPrice', fn (?array $p) => $p !== null && $p['cents'] === 17900)
->assertDontSee(__('dashboard.invoice.label'));
});
it('shows the credentials card, and lets acknowledging delete the password rather than just hide it', function () {
$user = User::factory()->create(['email' => 'ack@cred.test']);
$customer = Customer::factory()->create(['email' => 'ack@cred.test', 'user_id' => $user->id]);
$order = Order::factory()->create(['customer_id' => $customer->id]);
$instance = Instance::factory()->create([
'customer_id' => $customer->id,
'order_id' => $order->id,
'status' => 'active',
'nc_admin_ref' => 'admin',
'admin_password' => 'my-secret-pw',
]);
Livewire::actingAs($user)
->test(Dashboard::class)
->assertSee(__('dashboard.credentials.title'))
->assertSee('my-secret-pw')
->call('acknowledgeCredentials', $instance->uuid)
->assertDontSee('my-secret-pw')
->assertDontSee(__('dashboard.credentials.title'));
// Not merely hidden from the card — actually gone, at the raw stored
// column too, so a mutant that only toggled a "hide the card" flag
// without clearing admin_password is caught here.
expect($instance->fresh()->admin_password)->toBeNull()
->and($instance->fresh()->credentials_acknowledged_at)->not->toBeNull();
$raw = DB::table('instances')->where('id', $instance->id)->value('admin_password');
expect($raw)->toBeNull();
});
it("refuses to acknowledge — or leak — another customer's credentials", function () {
$userA = User::factory()->create(['email' => 'a@cred.test']);
$customerA = Customer::factory()->create(['email' => 'a@cred.test', 'user_id' => $userA->id]);
$orderA = Order::factory()->create(['customer_id' => $customerA->id]);
$instanceA = Instance::factory()->create([
'customer_id' => $customerA->id,
'order_id' => $orderA->id,
'status' => 'active',
'nc_admin_ref' => 'admin',
'admin_password' => 'a-secret-pw',
]);
$userB = User::factory()->create(['email' => 'b@cred.test']);
$customerB = Customer::factory()->create(['email' => 'b@cred.test', 'user_id' => $userB->id]);
$orderB = Order::factory()->create(['customer_id' => $customerB->id]);
Instance::factory()->create([
'customer_id' => $customerB->id,
'order_id' => $orderB->id,
'status' => 'active',
]);
// B's own dashboard render must not show A's password — render() scopes
// $instance to $customer's own instances, so this should hold already.
Livewire::actingAs($userB)
->test(Dashboard::class)
->assertDontSee('a-secret-pw')
// B posting A's instance uuid directly — exactly what an unchecked
// Livewire action allows, since the card that would normally supply
// this uuid is never rendered for B. acknowledgeCredentials() must
// re-resolve the instance from B's own customer, not trust the uuid.
->call('acknowledgeCredentials', $instanceA->uuid);
expect($instanceA->fresh()->admin_password)->not->toBeNull()
->and($instanceA->fresh()->credentials_acknowledged_at)->toBeNull();
});