get(route('dashboard')); $response->assertRedirect(route('login')); }); test('authenticated users can visit the dashboard', function () { $user = User::factory()->create(); $this->actingAs($user); $response = $this->get(route('dashboard')); $response->assertOk(); $response->assertInertia(fn ($page) => $page->component('Dashboard')->has('unreadDynamics')); }); test('visiting dynamic updates the read cursor', function () { $user = User::factory()->create(); $dynamic = Dynamic::factory()->create(); $dynamic->participants()->attach($user->id, ['role' => 'owner']); $this->actingAs($user); $service = app(ActivityService::class); $initialCursor = $service->getCursorReadAt($user, $dynamic); expect($initialCursor->toIso8601String())->toBe('1970-01-01T00:00:00+00:00'); // Visit Dynamic Show $this->get(route('dynamics.show', $dynamic->uuid))->assertOk(); // Re-check cursor is updated to near now $updatedCursor = $service->getCursorReadAt($user, $dynamic); expect($updatedCursor->gt($initialCursor))->toBeTrue(); expect($updatedCursor->diffInSeconds(Carbon::now()))->toBeLessThan(5); }); test('visiting ledger updates the read cursor', function () { $user = User::factory()->create(); $dynamic = Dynamic::factory()->create(); $dynamic->participants()->attach($user->id, ['role' => 'owner']); $ledger = Ledger::factory()->create(['dynamic_id' => $dynamic->id]); $this->actingAs($user); $service = app(ActivityService::class); $initialCursor = $service->getCursorReadAt($user, $ledger); expect($initialCursor->toIso8601String())->toBe('1970-01-01T00:00:00+00:00'); // Visit Ledger Show $this->get(route('dynamics.ledgers.show', [$dynamic->uuid, $ledger->uuid]))->assertOk(); // Re-check cursor is updated to near now $updatedCursor = $service->getCursorReadAt($user, $ledger); expect($updatedCursor->gt($initialCursor))->toBeTrue(); expect($updatedCursor->diffInSeconds(Carbon::now()))->toBeLessThan(5); }); test('dashboard groups and filters unread entities correctly based on cursor', function () { $user = User::factory()->create(); $dynamic = Dynamic::factory()->create(['name' => 'Testing Dynamic']); $dynamic->participants()->attach($user->id, ['role' => 'owner']); // Create custom Chat so booted has chat available $dynamic->chat()->create([]); $this->actingAs($user); // Create past message (already read) $pastMsg = Message::create([ 'chat_id' => $dynamic->chat->id, 'user_id' => $user->id, 'content' => 'Old message context', ]); // Artificially advance cursor to after past message Carbon::setTestNow(Carbon::now()->addMinutes(5)); $service = app(ActivityService::class); $service->updateCursor($user, $dynamic); // Create new unread message Carbon::setTestNow(Carbon::now()->addMinutes(5)); $unreadMsg = Message::create([ 'chat_id' => $dynamic->chat->id, 'user_id' => $user->id, 'content' => 'New unread message alert', ]); // Retrieve unread groupings $response = $this->get(route('dashboard')); $response->assertOk(); // Verify unread grouping structure $response->assertInertia(fn ($page) => $page ->component('Dashboard') ->where('unreadDynamics.0.name', 'Testing Dynamic') ->where('unreadDynamics.0.unread_count', 1) ->has('unreadDynamics.0.context_activities', 1) // Should have old message as context ->where('unreadDynamics.0.context_activities.0.content', 'Old message context') ->has('unreadDynamics.0.new_activities', 1) // Should have unread message ->where('unreadDynamics.0.new_activities.0.content', 'New unread message alert') ); // Now visit the Dynamic, which clears the unread count $this->get(route('dynamics.show', $dynamic->uuid))->assertOk(); // Dashboard should now show 0 unread groups (caught up) $response2 = $this->get(route('dashboard')); $response2->assertOk(); $response2->assertInertia(fn ($page) => $page ->component('Dashboard') ->has('unreadDynamics', 0) ); Carbon::setTestNow(); // Reset test time });