122 lines
5.0 KiB
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();
|
|
});
|