feat: Implement read cursor activity tracking and recent activity dashboard with context
This commit is contained in:
@@ -1,6 +1,12 @@
|
||||
<?php
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\Dynamic;
|
||||
use App\Models\Ledger;
|
||||
use App\Models\Mutation;
|
||||
use App\Models\Message;
|
||||
use App\Services\ActivityService;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
test('guests are redirected to the login page', function () {
|
||||
$response = $this->get(route('dashboard'));
|
||||
@@ -13,4 +19,106 @@ test('authenticated users can visit the dashboard', function () {
|
||||
|
||||
$response = $this->get(route('dashboard'));
|
||||
$response->assertOk();
|
||||
$response->assertInertia(fn ($page) => $page->component('Dashboard')->has('unreadEntities'));
|
||||
});
|
||||
|
||||
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->id))->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->id, $ledger->id]))->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('unreadEntities.0.name', 'Testing Dynamic')
|
||||
->where('unreadEntities.0.unread_count', 1)
|
||||
->has('unreadEntities.0.context_activities', 1) // Should have old message as context
|
||||
->where('unreadEntities.0.context_activities.0.description', 'Old message context')
|
||||
->has('unreadEntities.0.new_activities', 1) // Should have unread message
|
||||
->where('unreadEntities.0.new_activities.0.description', 'New unread message alert')
|
||||
);
|
||||
|
||||
// Now visit the Dynamic, which clears the unread count
|
||||
$this->get(route('dynamics.show', $dynamic->id))->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('unreadEntities', 0)
|
||||
);
|
||||
|
||||
Carbon::setTestNow(); // Reset test time
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user